diff --git a/.circleci/bazel.linux.rc b/.circleci/bazel.linux.rc index 4889f934b22ff..afb8fd47f114e 100644 --- a/.circleci/bazel.linux.rc +++ b/.circleci/bazel.linux.rc @@ -14,7 +14,8 @@ build --repository_cache=/home/circleci/bazel_repository_cache # Bazel doesn't calculate the memory ceiling correctly when running under Docker. # Limit Bazel to consuming resources that fit in CircleCI "xlarge" class # https://circleci.com/docs/2.0/configuration-reference/#resource_class -build --local_resources=14336,8.0,1.0 +build --local_cpu_resources=8 +build --local_ram_resources=14336 # All build executed remotely should be done using our RBE configuration. build:remote --google_default_credentials diff --git a/.circleci/bazel.windows.rc b/.circleci/bazel.windows.rc index 9efa954554f1b..ce3e53392c8a4 100644 --- a/.circleci/bazel.windows.rc +++ b/.circleci/bazel.windows.rc @@ -10,6 +10,10 @@ try-import %workspace%/.circleci/bazel.common.rc # speeding up the analysis time significantly with Bazel managed node dependencies on the CI. build --repository_cache=C:/Users/circleci/bazel_repository_cache +# Manually set the local resources used in windows CI runs +build --local_ram_resources=13500 +build --local_cpu_resources=4 + # All windows jobs run on master and should use http caching build --remote_http_cache=https://storage.googleapis.com/angular-team-cache build --remote_accept_cached=true diff --git a/.circleci/config.yml b/.circleci/config.yml index 3a31a29a46a38..aa8e0a106bb13 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,18 +22,18 @@ version: 2.1 # **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 v4-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} +var_3: &cache_key v6-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} # We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache # folder will contain all previously used versions and ultimately cause the cache restoring to # be slower due to its growing size. -var_4: &cache_key_fallback v4-angular-node-12-{{ checksum ".bazelversion" }} -var_3_win: &cache_key_win v5-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} -var_4_win: &cache_key_win_fallback v5-angular-win-node-12-{{ checksum ".bazelversion" }} +var_4: &cache_key_fallback v6-angular-node-12-{{ checksum ".bazelversion" }} +var_3_win: &cache_key_win v6-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} +var_4_win: &cache_key_win_fallback v6-angular-win-node-12-{{ checksum ".bazelversion" }} # Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the # cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable. -var_5: &components_repo_unit_tests_cache_key v5-angular-components-598db096e668aa7e9debd56eedfd127b7a55e371 -var_6: &components_repo_unit_tests_cache_key_fallback v5-angular-components- +var_5: &components_repo_unit_tests_cache_key v6-angular-components-598db096e668aa7e9debd56eedfd127b7a55e371 +var_6: &components_repo_unit_tests_cache_key_fallback v6-angular-components- # Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and # `build-ivy-npm-packages`. @@ -226,6 +226,7 @@ jobs: executor: default-executor steps: - checkout + - init_environment - run: name: Rebase PR on target branch # After checkout, rebase on top of target branch. @@ -235,7 +236,7 @@ jobs: 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} + node tools/rebase-pr.js else echo "This build is not over a PR, nothing to do." fi @@ -244,7 +245,6 @@ jobs: keys: - *cache_key - *cache_key_fallback - - init_environment - run: name: Running Yarn install command: yarn install --frozen-lockfile --non-interactive @@ -278,9 +278,10 @@ jobs: - run: 'yarn bazel:lint || (echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)' - - run: yarn lint - - run: yarn ts-circular-deps:check - - run: node tools/pullapprove/verify.js + - run: yarn -s lint --branch $CI_GIT_BASE_REVISION + - run: yarn -s ts-circular-deps:check + - run: yarn -s ng-dev pullapprove verify + - run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE test: executor: @@ -454,7 +455,7 @@ jobs: test_docs_examples: parameters: - ivy: + viewengine: type: boolean default: false executor: @@ -470,7 +471,7 @@ jobs: # 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<> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2 + - run: yarn --cwd aio example-e2e --setup --local <<# parameters.viewengine >>--viewengine<> --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: @@ -822,8 +823,8 @@ workflows: requires: - build-npm-packages - test_docs_examples: - name: test_docs_examples_ivy - ivy: true + name: test_docs_examples_viewengine + viewengine: true requires: - build-npm-packages - aio_preview: @@ -850,7 +851,7 @@ workflows: - test_aio_local - test_aio_local_viewengine - test_docs_examples - - test_docs_examples_ivy + - test_docs_examples_viewengine # Get the artifacts to publish from the build-packages-dist job # since the publishing script expects the legacy outputs layout. - build-npm-packages diff --git a/.circleci/env.sh b/.circleci/env.sh index 2b20833205eb7..0865773d0feb5 100755 --- a/.circleci/env.sh +++ b/.circleci/env.sh @@ -3,72 +3,89 @@ # Variables readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..") readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh"; - -# Load helpers and make them available everywhere (through `$BASH_ENV`). -source $envHelpersPath; -echo "source $envHelpersPath;" >> $BASH_ENV; - - -#################################################################################################### -# Define PUBLIC environment variables for CircleCI. -#################################################################################################### -# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info. -#################################################################################################### -setPublicVar PROJECT_ROOT "$projectDir"; -setPublicVar CI_AIO_MIN_PWA_SCORE "95"; -# This is the branch being built; e.g. `pull/12345` for PR builds. -setPublicVar CI_BRANCH "$CIRCLE_BRANCH"; -setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL"; -setPublicVar CI_COMMIT "$CIRCLE_SHA1"; -# `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun -# workflows of such builds). -setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION"; -setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}"; -setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME"; -setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME"; - - -#################################################################################################### -# Define "lazy" PUBLIC environment variables for CircleCI. -# (I.e. functions to set an environment variable when called.) -#################################################################################################### -createPublicVarSetter CI_STABLE_BRANCH "\$(npm info @angular/core dist-tags.latest | sed -r 's/^\\s*([0-9]+\\.[0-9]+)\\.[0-9]+.*$/\\1.x/')"; - - -#################################################################################################### -# Define SECRET environment variables for CircleCI. -#################################################################################################### -setSecretVar CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN "$AIO_DEPLOY_TOKEN"; -setSecretVar CI_SECRET_PAYLOAD_FIREBASE_TOKEN "$ANGULAR_PAYLOAD_TOKEN"; - - -#################################################################################################### -# Define SauceLabs environment variables for CircleCI. -#################################################################################################### -setPublicVar SAUCE_USERNAME "angular-framework"; -setSecretVar SAUCE_ACCESS_KEY "0c731274ed5f-cbc9-16f4-021a-9835e39f"; -# TODO(josephperrott): Remove environment variables once all saucelabs tests are via bazel method. -setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log -setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock -setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock -setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-framework-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}" -# Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not -# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout. -setPublicVar SAUCE_READY_FILE_TIMEOUT 120 - - -#################################################################################################### -# Define environment variables for the `angular/components` repo unit tests job. -#################################################################################################### -# We specifically use a directory within "/tmp" here because we want the cloned repo to be -# completely isolated from angular/angular in order to avoid any bad interactions between -# their separate build setups. **NOTE**: When updating the temporary directory, also update -# the `save_cache` path configuration in `config.yml` -setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo" -setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git" -setPublicVar COMPONENTS_REPO_BRANCH "master" -# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`. -setPublicVar COMPONENTS_REPO_COMMIT "598db096e668aa7e9debd56eedfd127b7a55e371" +readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache"; + +if [ -f $bashEnvCachePath ]; then + # Since a bash env cache is present, load this into the $BASH_ENV + cat "$bashEnvCachePath" >> $BASH_ENV; + echo "BASH environment loaded from cached value at $bashEnvCachePath"; +else + # Since no bash env cache is present, build out $BASH_ENV values. + + # Load helpers and make them available everywhere (through `$BASH_ENV`). + source $envHelpersPath; + echo "source $envHelpersPath;" >> $BASH_ENV; + + + #################################################################################################### + # Define PUBLIC environment variables for CircleCI. + #################################################################################################### + # See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info. + #################################################################################################### + setPublicVar CI "$CI" + setPublicVar PROJECT_ROOT "$projectDir"; + setPublicVar CI_AIO_MIN_PWA_SCORE "95"; + # This is the branch being built; e.g. `pull/12345` for PR builds. + setPublicVar CI_BRANCH "$CIRCLE_BRANCH"; + setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL"; + setPublicVar CI_COMMIT "$CIRCLE_SHA1"; + # `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun + # workflows of such builds). + setPublicVar CI_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}"; + setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}"; + setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION"; + setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}"; + setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME"; + setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME"; + setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME"; + setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME"; + + + #################################################################################################### + # Define "lazy" PUBLIC environment variables for CircleCI. + # (I.e. functions to set an environment variable when called.) + #################################################################################################### + createPublicVarSetter CI_STABLE_BRANCH "\$(npm info @angular/core dist-tags.latest | sed -r 's/^\\s*([0-9]+\\.[0-9]+)\\.[0-9]+.*$/\\1.x/')"; + + + #################################################################################################### + # Define SECRET environment variables for CircleCI. + #################################################################################################### + setSecretVar CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN "$AIO_DEPLOY_TOKEN"; + setSecretVar CI_SECRET_PAYLOAD_FIREBASE_TOKEN "$ANGULAR_PAYLOAD_TOKEN"; + + + #################################################################################################### + # Define SauceLabs environment variables for CircleCI. + #################################################################################################### + setPublicVar SAUCE_USERNAME "angular-framework"; + setSecretVar SAUCE_ACCESS_KEY "0c731274ed5f-cbc9-16f4-021a-9835e39f"; + # TODO(josephperrott): Remove environment variables once all saucelabs tests are via bazel method. + setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log + setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock + setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock + setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-framework-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}" + # Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not + # acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout. + setPublicVar SAUCE_READY_FILE_TIMEOUT 120 + + + #################################################################################################### + # Define environment variables for the `angular/components` repo unit tests job. + #################################################################################################### + # We specifically use a directory within "/tmp" here because we want the cloned repo to be + # completely isolated from angular/angular in order to avoid any bad interactions between + # their separate build setups. **NOTE**: When updating the temporary directory, also update + # the `save_cache` path configuration in `config.yml` + setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo" + setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git" + setPublicVar COMPONENTS_REPO_BRANCH "master" + # **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`. + setPublicVar COMPONENTS_REPO_COMMIT "598db096e668aa7e9debd56eedfd127b7a55e371" + + # Save the created BASH_ENV into the bash env cache file. + cat "$BASH_ENV" >> $bashEnvCachePath; +fi #################################################################################################### diff --git a/.dev-infra.json b/.dev-infra.json new file mode 100644 index 0000000000000..0a0997a8e9957 --- /dev/null +++ b/.dev-infra.json @@ -0,0 +1,47 @@ +{ + "commitMessage": { + "maxLength": 120, + "minBodyLength": 0, + "types": [ + "build", + "ci", + "docs", + "feat", + "fix", + "perf", + "refactor", + "release", + "style", + "test" + ], + "scopes": [ + "animations", + "bazel", + "benchpress", + "changelog", + "common", + "compiler", + "compiler-cli", + "core", + "dev-infra", + "docs-infra", + "elements", + "forms", + "http", + "language-service", + "localize", + "ngcc", + "packaging", + "platform-browser", + "platform-browser-dynamic", + "platform-server", + "platform-webworker", + "platform-webworker-dynamic", + "router", + "service-worker", + "upgrade", + "ve", + "zone.js" + ] + } +} \ No newline at end of file diff --git a/.pullapprove.yml b/.pullapprove.yml index 480e472baba95..29637b8f9a52b 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -97,6 +97,12 @@ version: 3 +# Meta field that goes unused by PullApprove to allow for defining aliases to be +# used throughout the config. +meta: + 1: &can-be-global-approved "\"global-approvers\" not in groups.approved" + 2: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved" + # turn on 'draft' support # https://docs.pullapprove.com/config/github-api-version/ # https://developer.github.com/v3/previews/#draft-pull-requests @@ -115,11 +121,49 @@ pullapprove_conditions: groups: + # ========================================================= + # Global Approvers + # + # All reviews performed for global approvals require using + # the `Reviewed-for:` specifier to set the approval + # specificity as documented at: + # https://docs.pullapprove.com/reviewed-for/ + # ========================================================= + global-approvers: + type: optional + reviewers: + teams: + - framework-global-approvers + reviews: + request: 0 + required: 1 + reviewed_for: required + + # ========================================================= + # Global Approvers For Docs + # + # All reviews performed for global docs approvals require + # using the `Reviewed-for:` specifier to set the approval + # specificity as documented at: + # https://docs.pullapprove.com/reviewed-for/ + # ========================================================= + global-docs-approvers: + type: optional + reviewers: + teams: + - framework-global-approvers-for-docs-only-changes + reviews: + request: 0 + required: 1 + reviewed_for: required + # ========================================================= # Framework: Animations # ========================================================= fw-animations: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/animations/**', @@ -135,9 +179,6 @@ groups: reviewers: users: - matsko - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -145,6 +186,8 @@ groups: # ========================================================= fw-compiler: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/compiler/**', @@ -155,15 +198,16 @@ groups: 'aio/content/guide/aot-metadata-errors.md', 'aio/content/guide/template-typecheck.md ' ]) + - > + not contains_any_globs(files, [ + 'packages/compiler-cli/ngcc/**' + ]) reviewers: users: - alxhub - AndrewKushnir - JoostK - kara - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -171,6 +215,8 @@ groups: # ========================================================= fw-ngcc: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/compiler-cli/ngcc/**' @@ -181,9 +227,6 @@ groups: - gkalpak - JoostK - petebacondarwin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -191,6 +234,8 @@ groups: # ========================================================= fw-core: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/core/**', @@ -300,9 +345,6 @@ groups: - kara - mhevery - pkozlowski-opensource - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -310,6 +352,8 @@ groups: # ========================================================= fw-http: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/common/http/**', @@ -323,9 +367,6 @@ groups: users: - alxhub - IgorMinar - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -333,6 +374,8 @@ groups: # ========================================================= fw-elements: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/elements/**', @@ -344,9 +387,6 @@ groups: users: - andrewseguin - gkalpak - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -354,6 +394,8 @@ groups: # ========================================================= fw-forms: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/forms/**', @@ -377,9 +419,6 @@ groups: reviewers: users: - AndrewKushnir - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -387,6 +426,8 @@ groups: # ========================================================= fw-i18n: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/core/src/i18n/**', @@ -411,9 +452,6 @@ groups: - AndrewKushnir - mhevery - petebacondarwin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -421,6 +459,8 @@ groups: # ========================================================= fw-platform-server: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/platform-server/**', @@ -431,9 +471,6 @@ groups: users: - alxhub - kyliau - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -441,6 +478,8 @@ groups: # ========================================================= fw-router: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/router/**', @@ -452,9 +491,6 @@ groups: reviewers: users: - atscott - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -462,6 +498,8 @@ groups: # ========================================================= fw-server-worker: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/service-worker/**', @@ -480,9 +518,6 @@ groups: - alxhub - gkalpak - IgorMinar - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -490,6 +525,8 @@ groups: # ========================================================= fw-upgrade: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/upgrade/**', @@ -511,9 +548,6 @@ groups: users: - gkalpak - petebacondarwin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -521,6 +555,8 @@ groups: # ========================================================= fw-testing: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ '**/testing/**', @@ -533,9 +569,6 @@ groups: - IgorMinar - kara - pkozlowski-opensource - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -543,6 +576,7 @@ groups: # ========================================================= fw-benchmarks: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ 'modules/benchmarks/**' @@ -552,8 +586,6 @@ groups: - IgorMinar - kara - pkozlowski-opensource - teams: - - ~framework-global-approvers # ========================================================= @@ -561,6 +593,7 @@ groups: # ========================================================= fw-playground: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ 'modules/playground/**' @@ -569,8 +602,6 @@ groups: users: - IgorMinar - kara - teams: - - ~framework-global-approvers # ========================================================= @@ -578,6 +609,8 @@ groups: # ========================================================= fw-security: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/core/src/sanitization/**', @@ -592,15 +625,14 @@ groups: users: - IgorMinar - mhevery - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= # Bazel # ========================================================= bazel: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/bazel/**', @@ -611,9 +643,6 @@ groups: - IgorMinar - josephperrott - kyliau - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -621,6 +650,8 @@ groups: # ========================================================= language-service: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/language-service/**', @@ -631,9 +662,6 @@ groups: users: - ayazhafiz - kyliau - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -641,6 +669,8 @@ groups: # ========================================================= zone-js: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/zone.js/**', @@ -650,9 +680,6 @@ groups: users: - JiaLiPassion - mhevery - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -660,6 +687,8 @@ groups: # ========================================================= benchpress: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/benchpress/**' @@ -667,9 +696,6 @@ groups: reviewers: users: - alxhub - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -677,6 +703,7 @@ groups: # ========================================================= integration-tests: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ 'integration/**' @@ -687,8 +714,6 @@ groups: - josephperrott - kara - mhevery - teams: - - ~framework-global-approvers # ========================================================= @@ -696,6 +721,8 @@ groups: # ========================================================= docs-getting-started-and-tutorial: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/guide/setup-local.md', @@ -719,9 +746,6 @@ groups: - aikidave - IgorMinar - StephenFluin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -729,6 +753,8 @@ groups: # ========================================================= docs-marketing: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/marketing/**', @@ -742,9 +768,6 @@ groups: users: - IgorMinar - StephenFluin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -752,6 +775,8 @@ groups: # ========================================================= docs-observables: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/guide/observables.md', @@ -768,9 +793,6 @@ groups: reviewers: users: - alxhub - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -778,6 +800,8 @@ groups: # ========================================================= docs-packaging-and-releasing: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'docs/PUBLIC_API.md', @@ -802,9 +826,6 @@ groups: users: - IgorMinar - kara - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -812,6 +833,8 @@ groups: # ========================================================= docs-cli: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/cli/**', @@ -833,9 +856,6 @@ groups: - clydin - IgorMinar - mgechev - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -843,6 +863,8 @@ groups: # ========================================================= docs-libraries: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/guide/creating-libraries.md', @@ -854,9 +876,6 @@ groups: - alan-agius4 - IgorMinar - mgechev - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -864,6 +883,8 @@ groups: # ========================================================= docs-schematics: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/guide/schematics.md', @@ -877,9 +898,6 @@ groups: - alan-agius4 - IgorMinar - mgechev - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -887,6 +905,8 @@ groups: # ========================================================= docs-infra: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/*', @@ -907,9 +927,6 @@ groups: - gkalpak - IgorMinar - petebacondarwin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -917,6 +934,7 @@ groups: # ========================================================= dev-infra: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ '*', @@ -952,13 +970,12 @@ groups: 'tools/build/**', 'tools/circular_dependency_test/**', 'tools/contributing-stats/**', - 'tools/components/**' + 'tools/components/**', 'tools/gulp-tasks/**', 'tools/ng_rollup_bundle/**', 'tools/ngcontainer/**', 'tools/npm/**', 'tools/npm_integration_test/**', - 'tools/pullapprove/**', 'tools/rxjs/**', 'tools/saucelabs/**', 'tools/size-tracking/**', @@ -968,7 +985,6 @@ groups: 'tools/ts-api-guardian/**', 'tools/tslint/**', 'tools/utils/**', - 'tools/validate-commit-message/**', 'tools/yarn/**', 'tools/*', '**/*.bzl', @@ -981,8 +997,6 @@ groups: - gkalpak - IgorMinar - josephperrott - teams: - - ~framework-global-approvers # ========================================================= @@ -990,6 +1004,7 @@ groups: # ========================================================= public-api: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ 'goldens/public-api/**', @@ -1002,8 +1017,6 @@ groups: reviewers: users: - IgorMinar - teams: - - ~framework-global-approvers # ================================================ @@ -1011,6 +1024,7 @@ groups: # ================================================ size-tracking: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ 'aio/scripts/_payload-limits.json', @@ -1020,8 +1034,6 @@ groups: users: - IgorMinar - kara - teams: - - ~framework-global-approvers # ================================================ @@ -1029,6 +1041,7 @@ groups: # ================================================ circular-dependencies: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ 'goldens/packages-circular-deps.json' @@ -1038,8 +1051,6 @@ groups: - IgorMinar - josephperrott - kara - teams: - - ~framework-global-approvers #################################################################################### @@ -1051,6 +1062,7 @@ groups: # ========================================================= code-ownership: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ '.pullapprove.yml' @@ -1058,8 +1070,6 @@ groups: reviewers: users: - IgorMinar - teams: - - ~framework-global-approvers # ==================================================== @@ -1067,6 +1077,7 @@ groups: # ==================================================== fallback: conditions: + - *can-be-global-approved # Groups which are found to have matching conditions are `active` # according to PullApprove. If no groups are matched and considered # active, we still want to have a review occur. @@ -1074,5 +1085,3 @@ groups: reviewers: users: - IgorMinar - teams: - - ~framework-global-approvers diff --git a/.yarn/releases/yarn-1.21.1.js b/.yarn/releases/yarn-1.22.4.js similarity index 99% rename from .yarn/releases/yarn-1.21.1.js rename to .yarn/releases/yarn-1.22.4.js index 9420daa5b2338..09eb37a2cbfcf 100755 --- a/.yarn/releases/yarn-1.21.1.js +++ b/.yarn/releases/yarn-1.22.4.js @@ -34805,15 +34805,27 @@ function hasMergeConflicts(str) { function parse(str, fileLoc) { const parser = new Parser(str, fileLoc); parser.next(); - try { - return parser.parse(); - } catch (error1) { + + if (!fileLoc.endsWith(`.yml`)) { try { - return safeLoad(str, { - schema: FAILSAFE_SCHEMA - }); - } catch (error2) { - throw error1; + return parser.parse(); + } catch (error1) { + try { + return safeLoad(str, { + schema: FAILSAFE_SCHEMA + }); + } catch (error2) { + throw error1; + } + } + } else { + const result = safeLoad(str, { + schema: FAILSAFE_SCHEMA + }); + if (typeof result === 'object') { + return result; + } else { + return {}; } } } @@ -46666,7 +46678,7 @@ function mkdirfix (name, opts, cb) { /* 194 */ /***/ (function(module, exports) { -module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.21.1","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} +module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.4","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} /***/ }), /* 195 */ @@ -69876,12 +69888,12 @@ function getRcConfigForFolder(cwd) { } function loadRcFile(fileText, filePath) { - var _parse = (0, (_lockfile || _load_lockfile()).parse)(fileText, 'yarnrc'); + var _parse = (0, (_lockfile || _load_lockfile()).parse)(fileText, filePath); let values = _parse.object; - if (filePath.match(/\.yml$/)) { + if (filePath.match(/\.yml$/) && typeof values.yarnPath === 'string') { values = { 'yarn-path': values.yarnPath }; } @@ -74848,7 +74860,20 @@ let run = exports.run = (() => { } else { let suggestion; - for (const commandName in scripts) { + for (var _iterator9 = scripts.keys(), _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { + var _ref16; + + if (_isArray9) { + if (_i9 >= _iterator9.length) break; + _ref16 = _iterator9[_i9++]; + } else { + _i9 = _iterator9.next(); + if (_i9.done) break; + _ref16 = _i9.value; + } + + const commandName = _ref16; + const steps = leven(commandName, action); if (steps < 2) { suggestion = commandName; @@ -74933,19 +74958,19 @@ let run = exports.run = (() => { const printedCommands = new Map(); - for (var _iterator9 = pkgCommands, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { - var _ref16; + for (var _iterator10 = pkgCommands, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { + var _ref17; - if (_isArray9) { - if (_i9 >= _iterator9.length) break; - _ref16 = _iterator9[_i9++]; + if (_isArray10) { + if (_i10 >= _iterator10.length) break; + _ref17 = _iterator10[_i10++]; } else { - _i9 = _iterator9.next(); - if (_i9.done) break; - _ref16 = _i9.value; + _i10 = _iterator10.next(); + if (_i10.done) break; + _ref17 = _i10.value; } - const pkgCommand = _ref16; + const pkgCommand = _ref17; const action = scripts.get(pkgCommand); invariant(action, 'Action must exists'); @@ -76076,6 +76101,11 @@ class TarballFetcher extends (_baseFetcher || _load_baseFetcher()).default { chown: false, // don't chown. just leave as it is map: header => { header.mtime = now; + if (header.linkname) { + const basePath = path.posix.dirname(path.join('/', header.name)); + const jailPath = path.posix.join(basePath, header.linkname); + header.linkname = path.posix.relative('/', jailPath); + } return header; }, fs: patchedFs @@ -78409,6 +78439,11 @@ class RequestManager { rejectNext(err); }; + const rejectWithoutUrl = function rejectWithoutUrl(err) { + err.message = err.message; + rejectNext(err); + }; + const queueForRetry = reason => { const attempts = params.retryAttempts || 0; if (attempts >= this.maxRetryAttempts - 1) { @@ -78464,6 +78499,11 @@ class RequestManager { } } + if (res.statusCode === 401 && res.caseless && res.caseless.get('server') === 'GitHub.com') { + const message = `${res.body.message}. If using GITHUB_TOKEN in your env, check that it is valid.`; + rejectWithoutUrl(new Error(this.reporter.lang('unauthorizedResponse', res.caseless.get('server'), message))); + } + if (res.statusCode === 401 && res.headers['www-authenticate']) { const authMethods = res.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase()); @@ -96966,12 +97006,14 @@ function _load_asyncToGenerator() { let run = exports.run = (() => { var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, args) { - if (flags.install) { + const installVersion = flags[`2`] ? `berry` : flags.install; + + if (installVersion) { const lockfilePath = path.resolve(config.cwd, 'yarn.lock'); if (!(yield (_fs || _load_fs()).exists(lockfilePath))) { yield (_fs || _load_fs()).writeFile(lockfilePath, ''); } - yield (_child || _load_child()).spawn((_constants || _load_constants()).NODE_BIN_PATH, [process.argv[1], 'policies', 'set-version', flags.install], { + yield (_child || _load_child()).spawn((_constants || _load_constants()).NODE_BIN_PATH, [process.argv[1], 'policies', 'set-version', installVersion], { stdio: 'inherit', cwd: config.cwd }); @@ -97266,6 +97308,7 @@ function setFlags(commander) { commander.option('-y, --yes', 'use default options'); commander.option('-p, --private', 'use default options and private true'); commander.option('-i, --install ', 'install a specific Yarn release'); + commander.option('-2', 'generates the project using Yarn 2'); } function hasWrapper(commander, args) { @@ -98254,6 +98297,7 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def let bundleUrl; let bundleVersion; + let isV2 = false; if (range === 'nightly' || range === 'nightlies') { bundleUrl = 'https://nightly.yarnpkg.com/latest.js'; @@ -98261,10 +98305,18 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def } else if (range === 'berry' || range === 'v2' || range === '2') { bundleUrl = 'https://github.com/yarnpkg/berry/raw/master/packages/berry-cli/bin/berry.js'; bundleVersion = 'berry'; + isV2 = true; } else { - const releases = yield fetchReleases(config, { - includePrereleases: allowRc - }); + let releases = []; + + try { + releases = yield fetchReleases(config, { + includePrereleases: allowRc + }); + } catch (e) { + reporter.error(e.message); + return; + } const release = releases.find(function (release) { // $FlowFixMe @@ -98285,7 +98337,6 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def reporter.log(`Downloading ${chalk.green(bundleUrl)}...`); const bundle = yield fetchBundle(config, bundleUrl); - const rc = (0, (_rc || _load_rc()).getRcConfigForFolder)(config.lockfileFolder); const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`); reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`); @@ -98293,10 +98344,22 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def yield (_fs || _load_fs()).writeFile(yarnPath, bundle); yield (_fs || _load_fs()).chmod(yarnPath, 0o755); - const rcPath = `${config.lockfileFolder}/.yarnrc`; - reporter.log(`Updating ${chalk.magenta(rcPath)}...`); - rc['yarn-path'] = path.relative(config.lockfileFolder, yarnPath); - yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `${(0, (_lockfile || _load_lockfile()).stringify)(rc)}\n`); + const targetPath = path.relative(config.lockfileFolder, yarnPath).replace(/\\/g, '/'); + + if (isV2) { + const rcPath = `${config.lockfileFolder}/.yarnrc.yml`; + reporter.log(`Updating ${chalk.magenta(rcPath)}...`); + + yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `yarnPath: ${JSON.stringify(targetPath)}\n`); + } else { + const rcPath = `${config.lockfileFolder}/.yarnrc`; + reporter.log(`Updating ${chalk.magenta(rcPath)}...`); + + const rc = (0, (_rc || _load_rc()).getRcConfigForFolder)(config.lockfileFolder); + rc['yarn-path'] = targetPath; + + yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `${(0, (_lockfile || _load_lockfile()).stringify)(rc)}\n`); + } reporter.log(`Done!`); })(); @@ -99619,11 +99682,11 @@ let run = exports.run = (() => { throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceRootNotFound', config.cwd)); } - if (flags.originalArgs < 1) { + if (args.length < 1) { throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceMissingWorkspace')); } - if (flags.originalArgs < 2) { + if (args.length < 2) { throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceMissingCommand')); } @@ -99632,7 +99695,7 @@ let run = exports.run = (() => { const workspaces = yield config.resolveWorkspaces(workspaceRootFolder, manifest); - var _ref2 = flags.originalArgs || []; + var _ref2 = args || []; const workspaceName = _ref2[0], rest = _ref2.slice(1); @@ -99818,28 +99881,23 @@ let runScript = exports.runScript = (() => { const workspaces = yield config.resolveWorkspaces(workspaceRootFolder, manifest); try { - var _ref6 = flags.originalArgs || []; - - const _ = _ref6[0], - rest = _ref6.slice(1); - for (var _iterator4 = Object.keys(workspaces), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { - var _ref7; + var _ref6; if (_isArray4) { if (_i4 >= _iterator4.length) break; - _ref7 = _iterator4[_i4++]; + _ref6 = _iterator4[_i4++]; } else { _i4 = _iterator4.next(); if (_i4.done) break; - _ref7 = _i4.value; + _ref6 = _i4.value; } - const workspaceName = _ref7; + const workspaceName = _ref6; const loc = workspaces[workspaceName].loc; reporter.log(`${os.EOL}> ${workspaceName}`); - yield (_child || _load_child()).spawn((_constants2 || _load_constants2()).NODE_BIN_PATH, [(_constants2 || _load_constants2()).YARN_BIN_PATH, ...rest], { + yield (_child || _load_child()).spawn((_constants2 || _load_constants2()).NODE_BIN_PATH, [(_constants2 || _load_constants2()).YARN_BIN_PATH, 'run', ...args], { stdio: 'inherit', cwd: loc }); @@ -100059,7 +100117,11 @@ let main = exports.main = (() => { commandName = 'install'; isKnownCommand = true; } - + if (commandName === 'set' && args[0] === 'version') { + commandName = 'policies'; + args.splice(0, 1, 'set-version'); + isKnownCommand = true; + } if (!isKnownCommand) { // if command is not recognized, then set default to `run` args.unshift(commandName); @@ -100070,15 +100132,20 @@ let main = exports.main = (() => { let warnAboutRunDashDash = false; // we are using "yarn - `; - return html; + const file = `?file=${this._getPrimaryFile(config)}`; + const action = `https://run.stackblitz.com/api/angular/v1${file}`; + + return ` + +
+ + + `.trim(); } _createPostData(config, configFileName) { - var postData = {}; + const postData = {}; // If `config.main` is specified, ensure that it points to an existing file. - if (config.main && !this._existsSync(path.join(config.basePath, config.main))) { + if (config.main && !fs.existsSync(path.join(config.basePath, config.main))) { throw Error(`The main file ('${config.main}') specified in '${configFileName}' does not exist.`); } config.fileNames.forEach((fileName) => { - var content; - var extn = path.extname(fileName); - if (extn == '.png') { + let content; + const extn = path.extname(fileName); + if (extn === '.png') { content = this._encodeBase64(fileName); - fileName = fileName.substr(0, fileName.length - 4) + '.base64.png' + fileName = `${fileName.slice(0, -extn.length)}.base64${extn}`; } else { content = fs.readFileSync(fileName, 'utf-8'); } - if (extn == '.js' || extn == '.ts' || extn == '.css') { + if (extn === '.js' || extn === '.ts' || extn === '.css') { content = content + this.copyrights.jsCss; - } else if (extn == '.html') { + } else if (extn === '.html') { content = content + this.copyrights.html; } - // var escapedValue = escapeHtml(content); + // const escapedValue = escapeHtml(content); - var relativeFileName = path.relative(config.basePath, fileName); + let relativeFileName = path.relative(config.basePath, fileName); // Is the main a custom index-xxx.html file? Rename it - if (relativeFileName == config.main) { + if (relativeFileName === config.main) { relativeFileName = 'src/index.html'; } // A custom main.ts file? Rename it if (/src\/main[-.]\w+\.ts$/.test(relativeFileName)) { - relativeFileName = 'src/main.ts' + relativeFileName = 'src/main.ts'; } - if (relativeFileName == 'index.html') { + if (relativeFileName === 'index.html') { if (config.description == null) { // set config.description to title from index.html - var matches = /(.*)<\/title>/.exec(content); + const matches = /<title>(.*)<\/title>/.exec(content); if (matches) { config.description = matches[1]; } @@ -208,28 +226,26 @@ class StackblitzBuilder { postData[`files[${relativeFileName}]`] = content; }); - var tags = ['angular', 'example'].concat(config.tags || []); - tags.forEach(function(tag,ix) { - postData['tags[' + ix + ']'] = tag; - }); + const tags = ['angular', 'example', ...config.tags || []]; + tags.forEach((tag, ix) => postData[`tags[${ix}]`] = tag); - postData.description = "Angular Example - " + config.description; + postData.description = `Angular Example - ${config.description}`; return postData; } _createStackblitzHtml(config, postData) { - var baseHtml = this._createBaseStackblitzHtml(config); - var doc = jsdom.jsdom(baseHtml); - var form = doc.querySelector('form'); - _.forEach(postData, (value, key) => { - var ele = this._htmlToElement(doc, '<input type="hidden" name="' + key + '">'); + const baseHtml = this._createBaseStackblitzHtml(config); + const doc = jsdom.jsdom(baseHtml); + const form = doc.querySelector('form'); + + for(const [key, value] of Object.entries(postData)) { + const ele = this._htmlToElement(doc, `<input type="hidden" name="${key}">`); ele.setAttribute('value', value); - form.appendChild(ele) - }); - var html = doc.documentElement.outerHTML; + form.appendChild(ele); + } - return html; + return doc.documentElement.outerHTML; } _encodeBase64(file) { @@ -237,36 +253,20 @@ class StackblitzBuilder { return fs.readFileSync(file, { encoding: 'base64' }); } - _existsSync(filename) { - try { - fs.accessSync(filename); - return true; - } catch(ex) { - return false; - } - } - _htmlToElement(document, html) { - var div = document.createElement('div'); + const div = document.createElement('div'); div.innerHTML = html; return div.firstChild; } _initConfigAndCollectFileNames(configFileName) { - var configDir = path.dirname(configFileName); - var configSrc = fs.readFileSync(configFileName, 'utf-8'); - try { - var config = (configSrc && configSrc.trim().length) ? JSON.parse(configSrc) : {}; - config.basePath = configDir; // assumes 'stackblitz.json' is at `/src` level. - } catch (e) { - throw new Error(`Stackblitz config - unable to parse json file: ${configFileName}\n${e}`); - } + const config = this._parseConfig(configFileName); - var defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png', '**/*.svg']; - var boilerplateIncludes = ['src/environments/*.*', 'angular.json', 'src/polyfills.ts']; + const defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png', '**/*.svg']; + const boilerplateIncludes = ['src/environments/*.*', 'angular.json', 'src/polyfills.ts']; if (config.files) { if (config.files.length > 0) { - if (config.files[0].substr(0, 1) == '!') { + if (config.files[0][0] === '!') { config.files = defaultIncludes.concat(config.files); } } @@ -275,10 +275,10 @@ class StackblitzBuilder { } config.files = config.files.concat(boilerplateIncludes); - var includeSpec = false; - var gpaths = config.files.map(function(fileName) { + let includeSpec = false; + const gpaths = config.files.map((fileName) => { fileName = fileName.trim(); - if (fileName.substr(0,1) == '!') { + if (fileName[0] === '!') { return '!' + path.join(config.basePath, fileName.substr(1)); } else { includeSpec = includeSpec || /\.spec\.(ts|js)$/.test(fileName); @@ -286,7 +286,7 @@ class StackblitzBuilder { } }); - var defaultExcludes = [ + const defaultExcludes = [ '!**/e2e/**/*.*', '!**/tsconfig.json', '!**/package.json', @@ -308,10 +308,21 @@ class StackblitzBuilder { gpaths.push(...defaultExcludes); - config.fileNames = globby.sync(gpaths, { ignore: ["**/node_modules/**"] }); + config.fileNames = globby.sync(gpaths, { ignore: ['**/node_modules/**'] }); return config; } + + _parseConfig(configFileName) { + try { + const configSrc = fs.readFileSync(configFileName, 'utf-8'); + const config = (configSrc && configSrc.trim().length) ? JSON.parse(configSrc) : {}; + config.basePath = path.dirname(configFileName); // assumes 'stackblitz.json' is at `/src` level. + return config; + } catch (e) { + throw new Error(`Stackblitz config - unable to parse json file: ${configFileName}\n${e}`); + } + } } module.exports = StackblitzBuilder; diff --git a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js index 7ed9f89b241de..278205eef7bba 100644 --- a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js +++ b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.js @@ -28,56 +28,100 @@ module.exports = function autoLinkCode(getDocFromAlias) { return autoLinkCodeImpl; function autoLinkCodeImpl() { - return (ast) => { + return (ast, file) => { visit(ast, 'element', (node, ancestors) => { - // Only interested in code elements that: - // * do not have `no-auto-link` class - // * do not have an ignored language - // * are not inside links - if (autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType)) && - (!node.properties.className || !node.properties.className.includes('no-auto-link')) && - !autoLinkCodeImpl.ignoredLanguages.includes(node.properties.language) && - ancestors.every(ancestor => !is(ancestor, 'a'))) { - visit(node, 'text', (node, ancestors) => { - // Only interested in text nodes that are not inside links - if (ancestors.every(ancestor => !is(ancestor, 'a'))) { - const parent = ancestors[ancestors.length - 1]; - const index = parent.children.indexOf(node); - - // Can we convert the whole text node into a doc link? - const docs = getDocFromAlias(node.value); - if (foundValidDoc(docs)) { - parent.children.splice(index, 1, createLinkNode(docs[0], node.value)); - } else { - // Parse the text for words that we can convert to links - const nodes = - textContent(node) - .split(/([A-Za-z0-9_.-]+)/) - .filter(word => word.length) - .map((word, index, words) => { - // remove docs that fail the custom filter tests - const filteredDocs = autoLinkCodeImpl.customFilters.reduce( - (docs, filter) => filter(docs, words, index), getDocFromAlias(word)); - return foundValidDoc(filteredDocs) ? - // Create a link wrapping the text node. - createLinkNode(filteredDocs[0], word) : - // this is just text so push a new text node - {type: 'text', value: word}; - }); - - // Replace the text node with the links and leftover text nodes - Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes)); - } - } - }); + if (!isValidCodeElement(node, ancestors)) { + return; } + + visit(node, 'text', (node, ancestors) => { + const isInLink = isInsideLink(ancestors); + if (isInLink) { + return; + } + + const parent = ancestors[ancestors.length - 1]; + const index = parent.children.indexOf(node); + + // Can we convert the whole text node into a doc link? + const docs = getDocFromAlias(node.value); + if (foundValidDoc(docs, node.value, file)) { + parent.children.splice(index, 1, createLinkNode(docs[0], node.value)); + } else { + // Parse the text for words that we can convert to links + const nodes = getNodes(node, file); + // Replace the text node with the links and leftover text nodes + Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes)); + } + }); }); }; } - function foundValidDoc(docs) { - return docs.length === 1 && !docs[0].internal && - autoLinkCodeImpl.docTypes.indexOf(docs[0].docType) !== -1; + function isValidCodeElement(node, ancestors) { + // Only interested in code elements that: + // * do not have `no-auto-link` class + // * do not have an ignored language + // * are not inside links + const isCodeElement = autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType)); + const hasNoAutoLink = node.properties.className && node.properties.className.includes('no-auto-link'); + const isLanguageSupported = !autoLinkCodeImpl.ignoredLanguages.includes(node.properties.language); + const isInLink = isInsideLink(ancestors); + return isCodeElement && !hasNoAutoLink && isLanguageSupported && !isInLink; + } + + function isInsideLink(ancestors) { + return ancestors.some(ancestor => is(ancestor, 'a')); + } + + function getNodes(node, file) { + return textContent(node) + .split(/([A-Za-z0-9_.-]+)/) + .filter(word => word.length) + .map((word, index, words) => { + // remove docs that fail the custom filter tests + const filteredDocs = autoLinkCodeImpl.customFilters.reduce( + (docs, filter) => filter(docs, words, index), getDocFromAlias(word)); + + return foundValidDoc(filteredDocs, word, file) ? + // Create a link wrapping the text node. + createLinkNode(filteredDocs[0], word) : + // this is just text so push a new text node + {type: 'text', value: word}; + }); + } + + /** + * Validates the docs to be used to generate the links. The validation ensures + * that the docs are not `internal` and that the `docType` is supported. The `path` + * can be empty when the `API` is not public. + * + * @param {Array<Object>} docs An array of objects containing the doc details + * + * @param {string} keyword The keyword the doc applies to + */ + function foundValidDoc(docs, keyword, file) { + if (docs.length !== 1) { + return false; + } + + var doc = docs[0]; + + const isInvalidDoc = doc.docType === 'member' && !keyword.includes('.'); + if (isInvalidDoc) { + return false; + } + + if (doc.path === '') { + var message = ` + autoLinkCode: Doc path is empty for "${doc.id}" - link will not be generated for "${keyword}". + Please make sure if the doc should be public. If not, it should probably not be referenced in the docs.`; + + file.message(message); + return false; + } + + return !doc.internal && autoLinkCodeImpl.docTypes.includes(doc.docType); } function createLinkNode(doc, text) { diff --git a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js index 59281a5a73245..5f18756f63bb6 100644 --- a/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js +++ b/aio/tools/transforms/angular-base-package/post-processors/auto-link-code.spec.js @@ -126,6 +126,24 @@ describe('autoLinkCode post-processor', () => { expect(doc.renderedContent).toEqual('<code>MyClass</code>'); }); + it('should ignore code items that match an API doc but have no path set', + () => { + aliasMap.addDoc( + {docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: ''}); + const doc = {docType: 'test-doc', renderedContent: '<code>MyClass</code>'}; + processor.$process([doc]); + expect(doc.renderedContent).toEqual('<code>MyClass</code>'); + }); + + it('should ignore documents when the `docType` is set to `member` and the keyword doesn\'t include `.`', + () => { + aliasMap.addDoc( + {docType: 'member', id: 'MyEnum', aliases: ['MyEnum'], path: 'a/b/c'}); + const doc = {docType: 'test-doc', renderedContent: '<code>MyEnum</code>'}; + processor.$process([doc]); + expect(doc.renderedContent).toEqual('<code>MyEnum</code>'); + }); + it('should insert anchors for individual text nodes within a code block', () => { aliasMap.addDoc({docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass'}); const doc = { diff --git a/aio/tools/transforms/angular-base-package/services/auto-link-filters/ignoreGenericWords.js b/aio/tools/transforms/angular-base-package/services/auto-link-filters/ignoreGenericWords.js index d1496d39d8416..c1c84b821e94c 100644 --- a/aio/tools/transforms/angular-base-package/services/auto-link-filters/ignoreGenericWords.js +++ b/aio/tools/transforms/angular-base-package/services/auto-link-filters/ignoreGenericWords.js @@ -5,6 +5,6 @@ */ module.exports = function ignoreGenericWords() { - const ignoredWords = new Set(['a', 'classes', 'create', 'error', 'group', 'request', 'target', 'value']); + const ignoredWords = new Set(['a', 'classes', 'create', 'error', 'group', 'request', 'target', 'value', '_']); return (docs, words, index) => ignoredWords.has(words[index].toLowerCase()) ? [] : docs; }; diff --git a/aio/tools/transforms/cli-docs-package/processors/processCliCommands.spec.js b/aio/tools/transforms/cli-docs-package/processors/processCliCommands.spec.js index 7cb300c3987da..97413d647e1e3 100644 --- a/aio/tools/transforms/cli-docs-package/processors/processCliCommands.spec.js +++ b/aio/tools/transforms/cli-docs-package/processors/processCliCommands.spec.js @@ -302,7 +302,7 @@ describe('processCliCommands processor', () => { navigation)); }); - it('should collect potential search terms from options for indexing', () => { + it('should collect potential search terms from options and its subcommands for indexing', () => { const doc = { docType: 'cli-command', name: 'name', diff --git a/aio/tsconfig.json b/aio/tsconfig.json index aed6d033325dc..bcb138ef772e5 100644 --- a/aio/tsconfig.json +++ b/aio/tsconfig.json @@ -37,7 +37,7 @@ ], "angularCompilerOptions": { "disableTypeScriptVersionCheck": true, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true + "strictInjectionParameters": true, + "strictTemplates": true } } diff --git a/aio/yarn.lock b/aio/yarn.lock index 66019a47f200e..e76383732aeb1 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -13032,7 +13032,7 @@ zip-stream@^2.1.2: compress-commons "^2.1.1" readable-stream "^3.4.0" -zone.js@~0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.2.tgz#67ca084b3116fc33fc40435e0d5ea40a207e392e" - integrity sha512-UAYfiuvxLN4oyuqhJwd21Uxb4CNawrq6fPS/05Su5L4G+1TN+HVDJMUHNMobVQDFJRir2cLAODXwluaOKB7HFg== +zone.js@~0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16" + integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg== diff --git a/dev-infra/BUILD.bazel b/dev-infra/BUILD.bazel index 2d70417db398e..57897870bff31 100644 --- a/dev-infra/BUILD.bazel +++ b/dev-infra/BUILD.bazel @@ -8,8 +8,13 @@ ts_library( ], module_name = "@angular/dev-infra-private", deps = [ + "//dev-infra/commit-message", "//dev-infra/pullapprove", + "//dev-infra/ts-circular-dependencies", + "//dev-infra/utils:config", "@npm//@types/node", + "@npm//@types/yargs", + "@npm//yargs", ], ) @@ -21,8 +26,8 @@ genrule( ], outs = ["package.json"], cmd = """ - $(location //tools:inline-package-json-deps) $(location tmpl-package.json) \ - $(location //:package.json) $@ + $(execpath //tools:inline-package-json-deps) $(execpath tmpl-package.json) \ + $(execpath //:package.json) $@ """, tools = ["//tools:inline-package-json-deps"], ) @@ -33,6 +38,7 @@ pkg_npm( deps = [ ":cli", ":package-json", + "//dev-infra/commit-message", "//dev-infra/ts-circular-dependencies", ], ) diff --git a/dev-infra/cli.ts b/dev-infra/cli.ts index b6285c768d5b4..0c30ac4ae6199 100644 --- a/dev-infra/cli.ts +++ b/dev-infra/cli.ts @@ -6,16 +6,17 @@ * 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 {verify} from './pullapprove/verify'; +import * as yargs from 'yargs'; +import {tsCircularDependenciesBuilder} from './ts-circular-dependencies/index'; +import {buildPullapproveParser} from './pullapprove/cli'; +import {buildCommitMessageParser} from './commit-message/cli'; -const args = process.argv.slice(2); - - -// TODO(josephperrott): Set up proper cli flag/command handling -switch (args[0]) { - case 'pullapprove:verify': - verify(); - break; - default: - console.info('No commands were matched'); -} +yargs.scriptName('ng-dev') + .demandCommand() + .recommendCommands() + .command('ts-circular-deps <command>', '', tsCircularDependenciesBuilder) + .command('pullapprove <command>', '', buildPullapproveParser) + .command('commit-message <command>', '', buildCommitMessageParser) + .wrap(120) + .strict() + .parse(); diff --git a/dev-infra/commit-message/BUILD.bazel b/dev-infra/commit-message/BUILD.bazel new file mode 100644 index 0000000000000..1197df1dcc9dd --- /dev/null +++ b/dev-infra/commit-message/BUILD.bazel @@ -0,0 +1,46 @@ +load("//tools:defaults.bzl", "jasmine_node_test") +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +ts_library( + name = "commit-message", + srcs = [ + "cli.ts", + "config.ts", + "validate.ts", + "validate-file.ts", + "validate-range.ts", + ], + module_name = "@angular/dev-infra-private/commit-message", + visibility = ["//dev-infra:__subpackages__"], + deps = [ + "//dev-infra/utils:config", + "@npm//@types/node", + "@npm//@types/shelljs", + "@npm//@types/yargs", + "@npm//shelljs", + "@npm//tslib", + "@npm//yargs", + ], +) + +ts_library( + name = "validate-test", + testonly = True, + srcs = ["validate.spec.ts"], + deps = [ + ":commit-message", + "//dev-infra/utils:config", + "@npm//@types/events", + "@npm//@types/jasmine", + "@npm//@types/node", + ], +) + +jasmine_node_test( + name = "test", + bootstrap = ["//tools/testing:node_no_angular_es5"], + deps = [ + ":commit-message", + ":validate-test", + ], +) diff --git a/dev-infra/commit-message/cli.ts b/dev-infra/commit-message/cli.ts new file mode 100644 index 0000000000000..6e459d19d2558 --- /dev/null +++ b/dev-infra/commit-message/cli.ts @@ -0,0 +1,66 @@ +/** + * @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 * as yargs from 'yargs'; +import {validateFile} from './validate-file'; +import {validateCommitRange} from './validate-range'; + +/** Build the parser for the commit-message commands. */ +export function buildCommitMessageParser(localYargs: yargs.Argv) { + return localYargs.help() + .strict() + .command( + 'pre-commit-validate', 'Validate the most recent commit message', { + 'file': { + type: 'string', + conflicts: ['file-env-variable'], + description: 'The path of the commit message file.', + }, + 'file-env-variable': { + type: 'string', + conflicts: ['file'], + description: + 'The key of the environment variable for the path of the commit message file.', + coerce: arg => { + const file = process.env[arg]; + if (!file) { + throw new Error(`Provided environment variable "${arg}" was not found.`); + } + return file; + }, + } + }, + args => { + const file = args.file || args.fileEnvVariable || '.git/COMMIT_EDITMSG'; + validateFile(file); + }) + .command( + 'validate-range', 'Validate a range of commit messages', { + 'range': { + description: 'The range of commits to check, e.g. --range abc123..xyz456', + demandOption: ' A range must be provided, e.g. --range abc123..xyz456', + type: 'string', + requiresArg: true, + }, + }, + argv => { + // If on CI, and not pull request number is provided, assume the branch + // being run on is an upstream branch. + if (process.env['CI'] && process.env['CI_PULL_REQUEST'] === 'false') { + console.info( + `Since valid commit messages are enforced by PR linting on CI, we do not\n` + + `need to validate commit messages on CI runs on upstream branches.\n\n` + + `Skipping check of provided commit range`); + return; + } + validateCommitRange(argv.range); + }); +} + +if (require.main == module) { + buildCommitMessageParser(yargs).parse(); +} diff --git a/packages/language-service/src/version.ts b/dev-infra/commit-message/config.ts similarity index 51% rename from packages/language-service/src/version.ts rename to dev-infra/commit-message/config.ts index af66f0fa278a8..4085e809b170d 100644 --- a/packages/language-service/src/version.ts +++ b/dev-infra/commit-message/config.ts @@ -5,13 +5,9 @@ * 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 */ - -/** - * @module - * @description - * Entry point for all public APIs of the common package. - */ - -import {Version} from '@angular/core'; - -export const VERSION = new Version('0.0.0-PLACEHOLDER'); +export interface CommitMessageConfig { + maxLineLength: number; + minBodyLength: number; + types: string[]; + scopes: string[]; +} diff --git a/dev-infra/commit-message/validate-file.ts b/dev-infra/commit-message/validate-file.ts new file mode 100644 index 0000000000000..9769caa33bf0e --- /dev/null +++ b/dev-infra/commit-message/validate-file.ts @@ -0,0 +1,24 @@ +/** + * @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 {readFileSync} from 'fs'; +import {resolve} from 'path'; + +import {getRepoBaseDir} from '../utils/config'; + +import {validateCommitMessage} from './validate'; + +/** Validate commit message at the provided file path. */ +export function validateFile(filePath: string) { + const commitMessage = readFileSync(resolve(getRepoBaseDir(), filePath), 'utf8'); + if (validateCommitMessage(commitMessage)) { + console.info('√ Valid commit message'); + return; + } + // If the validation did not return true, exit as a failure. + process.exit(1); +} diff --git a/dev-infra/commit-message/validate-range.ts b/dev-infra/commit-message/validate-range.ts new file mode 100644 index 0000000000000..1633aac443fbb --- /dev/null +++ b/dev-infra/commit-message/validate-range.ts @@ -0,0 +1,55 @@ +/** + * @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 {exec} from 'shelljs'; +import {parseCommitMessage, validateCommitMessage, ValidateCommitMessageOptions} from './validate'; + +// Whether the provided commit is a fixup commit. +const isNonFixup = (m: string) => !parseCommitMessage(m).isFixup; + +// Extracts commit header (first line of commit message). +const extractCommitHeader = (m: string) => parseCommitMessage(m).header; + +/** Validate all commits in a provided git commit range. */ +export function validateCommitRange(range: string) { + // A random value is used as a string to allow for a definite split point in the git log result. + const randomValueSeparator = `${Math.random()}`; + // Custom git log format that provides the commit header and body, separated as expected with + // the custom separator as the trailing value. + const gitLogFormat = `%s%n%n%b${randomValueSeparator}`; + + // Retrieve the commits in the provided range. + const result = exec(`git log --reverse --format=${gitLogFormat} ${range}`, {silent: true}); + if (result.code) { + throw new Error(`Failed to get all commits in the range: \n ${result.stderr}`); + } + + // Separate the commits from a single string into individual commits + const commits = result.split(randomValueSeparator).map(l => l.trim()).filter(line => !!line); + + console.info(`Examining ${commits.length} commit(s) in the provided range: ${range}`); + + // Check each commit in the commit range. Commits are allowed to be fixup commits for other + // commits in the provided commit range. + const allCommitsInRangeValid = commits.every((m, i) => { + const options: ValidateCommitMessageOptions = { + disallowSquash: true, + nonFixupCommitHeaders: isNonFixup(m) ? + undefined : + commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader) + }; + return validateCommitMessage(m, options); + }); + + if (allCommitsInRangeValid) { + console.info('√ All commit messages in range valid.'); + } else { + // Exit with a non-zero exit code if invalid commit messages have + // been discovered. + process.exit(1); + } +} diff --git a/dev-infra/commit-message/validate.spec.ts b/dev-infra/commit-message/validate.spec.ts new file mode 100644 index 0000000000000..3029613e11cf8 --- /dev/null +++ b/dev-infra/commit-message/validate.spec.ts @@ -0,0 +1,250 @@ +/** + * @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 + */ + +// Imports +import * as utilConfig from '../utils/config'; + +import {validateCommitMessage} from './validate'; + + +// Constants +const config = { + 'commitMessage': { + 'maxLineLength': 120, + 'minBodyLength': 0, + 'types': [ + 'feat', + 'fix', + 'refactor', + 'release', + 'style', + ], + 'scopes': [ + 'common', + 'compiler', + 'core', + 'packaging', + ] + } +}; +const TYPES = config.commitMessage.types.join(', '); +const SCOPES = config.commitMessage.scopes.join(', '); +const INVALID = false; +const VALID = true; + +// TODO(josephperrott): Clean up tests to test script rather than for +// specific commit messages we want to use. +describe('validate-commit-message.js', () => { + let lastError: string = ''; + + beforeEach(() => { + lastError = ''; + + spyOn(console, 'error').and.callFake((msg: string) => lastError = msg); + spyOn(utilConfig, 'getAngularDevConfig').and.returnValue(config); + }); + + describe('validateMessage()', () => { + it('should be valid', () => { + expect(validateCommitMessage('feat(packaging): something')).toBe(VALID); + expect(lastError).toBe(''); + + expect(validateCommitMessage('release(packaging): something')).toBe(VALID); + expect(lastError).toBe(''); + + expect(validateCommitMessage('fixup! release(packaging): something')).toBe(VALID); + expect(lastError).toBe(''); + + expect(validateCommitMessage('squash! release(packaging): something')).toBe(VALID); + expect(lastError).toBe(''); + + expect(validateCommitMessage('Revert: "release(packaging): something"')).toBe(VALID); + expect(lastError).toBe(''); + }); + + it('should validate max length', () => { + const msg = + 'fix(compiler): something super mega extra giga tera long, maybe even longer and longer and longer and longer and longer and longer...'; + + expect(validateCommitMessage(msg)).toBe(INVALID); + expect(lastError).toContain(`The commit message header is longer than ${ + config.commitMessage.maxLineLength} characters`); + }); + + it('should validate "<type>(<scope>): <subject>" format', () => { + const msg = 'not correct format'; + + expect(validateCommitMessage(msg)).toBe(INVALID); + expect(lastError).toContain(`The commit message header does not match the expected format.`); + }); + + it('should fail when type is invalid', () => { + const msg = 'weird(core): something'; + + expect(validateCommitMessage(msg)).toBe(INVALID); + expect(lastError).toContain(`'weird' is not an allowed type.\n => TYPES: ${TYPES}`); + }); + + it('should fail when scope is invalid', () => { + const errorMessageFor = (scope: string, header: string) => + `'${scope}' is not an allowed scope.\n => SCOPES: ${SCOPES}`; + + expect(validateCommitMessage('fix(Compiler): something')).toBe(INVALID); + expect(lastError).toContain(errorMessageFor('Compiler', 'fix(Compiler): something')); + + expect(validateCommitMessage('feat(bah): something')).toBe(INVALID); + expect(lastError).toContain(errorMessageFor('bah', 'feat(bah): something')); + + expect(validateCommitMessage('style(webworker): something')).toBe(INVALID); + expect(lastError).toContain(errorMessageFor('webworker', 'style(webworker): something')); + + expect(validateCommitMessage('refactor(security): something')).toBe(INVALID); + expect(lastError).toContain(errorMessageFor('security', 'refactor(security): something')); + + expect(validateCommitMessage('refactor(docs): something')).toBe(INVALID); + expect(lastError).toContain(errorMessageFor('docs', 'refactor(docs): something')); + + expect(validateCommitMessage('release(angular): something')).toBe(INVALID); + expect(lastError).toContain(errorMessageFor('angular', 'release(angular): something')); + }); + + it('should allow empty scope', () => { + expect(validateCommitMessage('fix: blablabla')).toBe(VALID); + expect(lastError).toBe(''); + }); + + // We do not want to allow WIP. It is OK to fail the PR build in this case to show that there is + // work still to be done (i.e. fixing the commit message). + it('should not allow "WIP: ..." syntax', () => { + const msg = 'WIP: fix: something'; + + expect(validateCommitMessage(msg)).toBe(INVALID); + expect(lastError).toContain(`'WIP' is not an allowed type.\n => TYPES: ${TYPES}`); + }); + + describe('(revert)', () => { + it('should allow valid "revert: ..." syntaxes', () => { + expect(validateCommitMessage('revert: anything')).toBe(VALID); + expect(lastError).toBe(''); + + expect(validateCommitMessage('Revert: "anything"')).toBe(VALID); + expect(lastError).toBe(''); + + expect(validateCommitMessage('revert anything')).toBe(VALID); + expect(lastError).toBe(''); + + expect(validateCommitMessage('rEvErT anything')).toBe(VALID); + expect(lastError).toBe(''); + }); + + it('should not allow "revert(scope): ..." syntax', () => { + const msg = 'revert(compiler): reduce generated code payload size by 65%'; + + expect(validateCommitMessage(msg)).toBe(INVALID); + expect(lastError).toContain(`'revert' is not an allowed type.\n => TYPES: ${TYPES}`); + }); + + // https://github.com/angular/angular/issues/23479 + it('should allow typical Angular messages generated by git', () => { + const msg = + 'Revert "fix(compiler): Pretty print object instead of [Object object] (#22689)" (#23442)'; + + expect(validateCommitMessage(msg)).toBe(VALID); + expect(lastError).toBe(''); + }); + }); + + describe('(squash)', () => { + it('should strip the `squash! ` prefix and validate the rest', () => { + const errorMessage = `The commit message header does not match the expected format.`; + + // Valid messages. + expect(validateCommitMessage('squash! feat(core): add feature')).toBe(VALID); + expect(validateCommitMessage('squash! fix: a bug', {disallowSquash: false})).toBe(VALID); + + // Invalid messages. + expect(validateCommitMessage('squash! fix a typo', {disallowSquash: false})).toBe(INVALID); + expect(lastError).toContain('squash! fix a typo'); + expect(lastError).toContain(errorMessage); + + expect(validateCommitMessage('squash! squash! fix: a bug')).toBe(INVALID); + expect(lastError).toContain('squash! squash! fix: a bug'); + expect(lastError).toContain(errorMessage); + }); + + describe('with `disallowSquash`', () => { + it('should fail', () => { + expect(validateCommitMessage('fix(core): something', {disallowSquash: true})).toBe(VALID); + expect(validateCommitMessage('squash! fix(core): something', { + disallowSquash: true + })).toBe(INVALID); + expect(lastError).toContain( + 'The commit must be manually squashed into the target commit'); + }); + }); + }); + + describe('(fixup)', () => { + describe('without `nonFixupCommitHeaders`', () => { + it('should strip the `fixup! ` prefix and validate the rest', () => { + const errorMessage = `The commit message header does not match the expected format.`; + + // Valid messages. + expect(validateCommitMessage('fixup! feat(core): add feature')).toBe(VALID); + expect(validateCommitMessage('fixup! fix: a bug')).toBe(VALID); + + // Invalid messages. + expect(validateCommitMessage('fixup! fix a typo')).toBe(INVALID); + expect(lastError).toContain('fixup! fix a typo'); + expect(lastError).toContain(errorMessage); + + expect(validateCommitMessage('fixup! fixup! fix: a bug')).toBe(INVALID); + expect(lastError).toContain('fixup! fixup! fix: a bug'); + expect(lastError).toContain(errorMessage); + }); + }); + + describe('with `nonFixupCommitHeaders`', () => { + it('should check that the fixup commit matches a non-fixup one', () => { + const msg = 'fixup! foo'; + + expect(validateCommitMessage( + msg, {disallowSquash: false, nonFixupCommitHeaders: ['foo', 'bar', 'baz']})) + .toBe(VALID); + expect(validateCommitMessage( + msg, {disallowSquash: false, nonFixupCommitHeaders: ['bar', 'baz', 'foo']})) + .toBe(VALID); + expect(validateCommitMessage( + msg, {disallowSquash: false, nonFixupCommitHeaders: ['baz', 'foo', 'bar']})) + .toBe(VALID); + + expect(validateCommitMessage( + msg, {disallowSquash: false, nonFixupCommitHeaders: ['qux', 'quux', 'quuux']})) + .toBe(INVALID); + expect(lastError).toContain( + 'Unable to find match for fixup commit among prior commits: \n' + + ' qux\n' + + ' quux\n' + + ' quuux'); + }); + + it('should fail if `nonFixupCommitHeaders` is empty', () => { + expect(validateCommitMessage('refactor(core): make reactive', { + disallowSquash: false, + nonFixupCommitHeaders: [] + })).toBe(VALID); + expect(validateCommitMessage( + 'fixup! foo', {disallowSquash: false, nonFixupCommitHeaders: []})) + .toBe(INVALID); + expect(lastError).toContain( + `Unable to find match for fixup commit among prior commits: -`); + }); + }); + }); + }); +}); diff --git a/dev-infra/commit-message/validate.ts b/dev-infra/commit-message/validate.ts new file mode 100644 index 0000000000000..7088b9081cbc0 --- /dev/null +++ b/dev-infra/commit-message/validate.ts @@ -0,0 +1,139 @@ +/** + * @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 {getAngularDevConfig} from '../utils/config'; +import {CommitMessageConfig} from './config'; + +/** Options for commit message validation. */ +export interface ValidateCommitMessageOptions { + disallowSquash?: boolean; + nonFixupCommitHeaders?: string[]; +} + +const FIXUP_PREFIX_RE = /^fixup! /i; +const GITHUB_LINKING_RE = /((closed?s?)|(fix(es)?(ed)?)|(resolved?s?))\s\#(\d+)/ig; +const SQUASH_PREFIX_RE = /^squash! /i; +const REVERT_PREFIX_RE = /^revert:? /i; +const TYPE_SCOPE_RE = /^(\w+)(?:\(([^)]+)\))?\:\s(.+)$/; +const COMMIT_HEADER_RE = /^(.*)/i; +const COMMIT_BODY_RE = /^.*\n\n(.*)/i; + +/** Parse a full commit message into its composite parts. */ +export function parseCommitMessage(commitMsg: string) { + let header = ''; + let body = ''; + let bodyWithoutLinking = ''; + let type = ''; + let scope = ''; + let subject = ''; + + if (COMMIT_HEADER_RE.test(commitMsg)) { + header = COMMIT_HEADER_RE.exec(commitMsg)![1] + .replace(FIXUP_PREFIX_RE, '') + .replace(SQUASH_PREFIX_RE, ''); + } + if (COMMIT_BODY_RE.test(commitMsg)) { + body = COMMIT_BODY_RE.exec(commitMsg)![1]; + bodyWithoutLinking = body.replace(GITHUB_LINKING_RE, ''); + } + + if (TYPE_SCOPE_RE.test(header)) { + const parsedCommitHeader = TYPE_SCOPE_RE.exec(header)!; + type = parsedCommitHeader[1]; + scope = parsedCommitHeader[2]; + subject = parsedCommitHeader[3]; + } + return { + header, + body, + bodyWithoutLinking, + type, + scope, + subject, + isFixup: FIXUP_PREFIX_RE.test(commitMsg), + isSquash: SQUASH_PREFIX_RE.test(commitMsg), + isRevert: REVERT_PREFIX_RE.test(commitMsg), + }; +} + +/** Validate a commit message against using the local repo's config. */ +export function validateCommitMessage( + commitMsg: string, options: ValidateCommitMessageOptions = {}) { + function error(errorMessage: string) { + console.error( + `INVALID COMMIT MSG: \n` + + `${'─'.repeat(40)}\n` + + `${commitMsg}\n` + + `${'─'.repeat(40)}\n` + + `ERROR: \n` + + ` ${errorMessage}` + + `\n\n` + + `The expected format for a commit is: \n` + + `<type>(<scope>): <subject>\n\n<body>`); + } + + const config = getAngularDevConfig<'commitMessage', CommitMessageConfig>().commitMessage; + const commit = parseCommitMessage(commitMsg); + + if (commit.isRevert) { + return true; + } + + if (commit.isSquash && options.disallowSquash) { + error('The commit must be manually squashed into the target commit'); + return false; + } + + // If it is a fixup commit and `nonFixupCommitHeaders` is not empty, we only care to check whether + // there is a corresponding non-fixup commit (i.e. a commit whose header is identical to this + // commit's header after stripping the `fixup! ` prefix). + if (commit.isFixup && options.nonFixupCommitHeaders) { + if (!options.nonFixupCommitHeaders.includes(commit.header)) { + error( + 'Unable to find match for fixup commit among prior commits: ' + + (options.nonFixupCommitHeaders.map(x => `\n ${x}`).join('') || '-')); + return false; + } + + return true; + } + + if (commit.header.length > config.maxLineLength) { + error(`The commit message header is longer than ${config.maxLineLength} characters`); + return false; + } + + if (!commit.type) { + error(`The commit message header does not match the expected format.`); + return false; + } + + if (!config.types.includes(commit.type)) { + error(`'${commit.type}' is not an allowed type.\n => TYPES: ${config.types.join(', ')}`); + return false; + } + + if (commit.scope && !config.scopes.includes(commit.scope)) { + error(`'${commit.scope}' is not an allowed scope.\n => SCOPES: ${config.scopes.join(', ')}`); + return false; + } + + if (commit.bodyWithoutLinking.trim().length < config.minBodyLength) { + error(`The commit message body does not meet the minimum length of ${ + config.minBodyLength} characters`); + return false; + } + + const bodyByLine = commit.body.split('\n'); + if (bodyByLine.some(line => line.length > config.maxLineLength)) { + error( + `The commit messsage body contains lines greater than ${config.maxLineLength} characters`); + return false; + } + + return true; +} diff --git a/tools/validate-commit-message/index.js b/dev-infra/ng-dev old mode 100644 new mode 100755 similarity index 67% rename from tools/validate-commit-message/index.js rename to dev-infra/ng-dev index 6e45729380a00..819654870af25 --- a/tools/validate-commit-message/index.js +++ b/dev-infra/ng-dev @@ -1,3 +1,4 @@ +#!/usr/bin/env ts-node /** * @license * Copyright Google Inc. All Rights Reserved. @@ -6,4 +7,5 @@ * found in the LICENSE file at https://angular.io/license */ -module.exports = require('./validate-commit-message'); \ No newline at end of file +// Loads the ng-dev cli, automatically executing it. +require('./cli.ts'); diff --git a/dev-infra/pullapprove/BUILD.bazel b/dev-infra/pullapprove/BUILD.bazel index 5989f338e6446..6b341553ae16b 100644 --- a/dev-infra/pullapprove/BUILD.bazel +++ b/dev-infra/pullapprove/BUILD.bazel @@ -3,18 +3,25 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library") ts_library( name = "pullapprove", srcs = [ + "cli.ts", + "group.ts", + "logging.ts", + "parse-yaml.ts", "verify.ts", ], module_name = "@angular/dev-infra-private/pullapprove", visibility = ["//dev-infra:__subpackages__"], deps = [ + "//dev-infra/utils:config", "@npm//@types/minimatch", "@npm//@types/node", "@npm//@types/shelljs", "@npm//@types/yaml", + "@npm//@types/yargs", "@npm//minimatch", "@npm//shelljs", "@npm//tslib", "@npm//yaml", + "@npm//yargs", ], ) diff --git a/dev-infra/pullapprove/cli.ts b/dev-infra/pullapprove/cli.ts new file mode 100644 index 0000000000000..d1036e644e57e --- /dev/null +++ b/dev-infra/pullapprove/cli.ts @@ -0,0 +1,19 @@ +/** + * @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 * as yargs from 'yargs'; +import {verify} from './verify'; + +/** Build the parser for the pullapprove commands. */ +export function buildPullapproveParser(localYargs: yargs.Argv) { + return localYargs.help().strict().demandCommand().command( + 'verify', 'Verify the pullapprove config', {}, () => verify()); +} + +if (require.main === module) { + buildPullapproveParser(yargs).parse(); +} diff --git a/dev-infra/pullapprove/group.ts b/dev-infra/pullapprove/group.ts new file mode 100644 index 0000000000000..d901284497389 --- /dev/null +++ b/dev-infra/pullapprove/group.ts @@ -0,0 +1,166 @@ +/** + * @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 {IMinimatch, Minimatch} from 'minimatch'; + +import {PullApproveGroupConfig} from './parse-yaml'; + +/** A condition for a group. */ +interface GroupCondition { + glob: string; + matcher: IMinimatch; + matchedFiles: Set<string>; +} + +/** Result of testing files against the group. */ +export interface PullApproveGroupResult { + groupName: string; + matchedIncludes: GroupCondition[]; + matchedExcludes: GroupCondition[]; + matchedCount: number; + unmatchedIncludes: GroupCondition[]; + unmatchedExcludes: GroupCondition[]; + unmatchedCount: number; +} + +// Regex Matcher for contains_any_globs conditions +const CONTAINS_ANY_GLOBS_REGEX = /^'([^']+)',?$/; + +const CONDITION_TYPES = { + INCLUDE_GLOBS: /^contains_any_globs/, + EXCLUDE_GLOBS: /^not contains_any_globs/, + ATTR_LENGTH: /^len\(.*\)/, + GLOBAL_APPROVAL: /^"global-(docs-)?approvers" not in groups.approved$/, +}; + +/** A PullApprove group to be able to test files against. */ +export class PullApproveGroup { + // Lines which were not able to be parsed as expected. + private misconfiguredLines: string[] = []; + // Conditions for the group for including files. + private includeConditions: GroupCondition[] = []; + // Conditions for the group for excluding files. + private excludeConditions: GroupCondition[] = []; + // Whether the group has file matchers. + public hasMatchers = false; + + constructor(public groupName: string, group: PullApproveGroupConfig) { + if (group.conditions) { + for (let condition of group.conditions) { + condition = condition.trim(); + + if (condition.match(CONDITION_TYPES.INCLUDE_GLOBS)) { + const [conditions, misconfiguredLines] = getLinesForContainsAnyGlobs(condition); + conditions.forEach(globString => this.includeConditions.push({ + glob: globString, + matcher: new Minimatch(globString, {dot: true}), + matchedFiles: new Set<string>(), + })); + this.misconfiguredLines.push(...misconfiguredLines); + this.hasMatchers = true; + } else if (condition.match(CONDITION_TYPES.EXCLUDE_GLOBS)) { + const [conditions, misconfiguredLines] = getLinesForContainsAnyGlobs(condition); + conditions.forEach(globString => this.excludeConditions.push({ + glob: globString, + matcher: new Minimatch(globString, {dot: true}), + matchedFiles: new Set<string>(), + })); + this.misconfiguredLines.push(...misconfiguredLines); + this.hasMatchers = true; + } else if (condition.match(CONDITION_TYPES.ATTR_LENGTH)) { + // Currently a noop as we do not take any action on this condition type. + } else if (condition.match(CONDITION_TYPES.GLOBAL_APPROVAL)) { + // Currently a noop as we don't take any action for global approval conditions. + } else { + const errMessage = + `Unrecognized condition found, unable to parse the following condition: \n\n` + + `From the [${groupName}] group:\n` + + ` - ${condition}` + + `\n\n` + + `Known condition regexs:\n` + + `${Object.entries(CONDITION_TYPES).map(([k, v]) => ` ${k} - ${v}`).join('\n')}` + + `\n\n`; + console.error(errMessage); + process.exit(1); + } + } + } + } + + /** Retrieve all of the lines which were not able to be parsed. */ + getBadLines(): string[] { + return this.misconfiguredLines; + } + + /** Retrieve the results for the Group, all matched and unmatched conditions. */ + getResults(): PullApproveGroupResult { + const matchedIncludes = this.includeConditions.filter(c => !!c.matchedFiles.size); + const matchedExcludes = this.excludeConditions.filter(c => !!c.matchedFiles.size); + const unmatchedIncludes = this.includeConditions.filter(c => !c.matchedFiles.size); + const unmatchedExcludes = this.excludeConditions.filter(c => !c.matchedFiles.size); + const unmatchedCount = unmatchedIncludes.length + unmatchedExcludes.length; + const matchedCount = matchedIncludes.length + matchedExcludes.length; + return { + matchedIncludes, + matchedExcludes, + matchedCount, + unmatchedIncludes, + unmatchedExcludes, + unmatchedCount, + groupName: this.groupName, + }; + } + + /** + * Tests a provided file path to determine if it would be considered matched by + * the pull approve group's conditions. + */ + testFile(file: string) { + let matched = false; + this.includeConditions.forEach((includeCondition: GroupCondition) => { + if (includeCondition.matcher.match(file)) { + let matchedExclude = false; + this.excludeConditions.forEach((excludeCondition: GroupCondition) => { + if (excludeCondition.matcher.match(file)) { + // Add file as a discovered exclude as it is negating a matched + // include condition. + excludeCondition.matchedFiles.add(file); + matchedExclude = true; + } + }); + // An include condition is only considered matched if no exclude + // conditions are found to matched the file. + if (!matchedExclude) { + includeCondition.matchedFiles.add(file); + matched = true; + } + } + }); + return matched; + } +} + +/** + * Extract all of the individual globs from a group condition, + * providing both the valid and invalid lines. + */ +function getLinesForContainsAnyGlobs(lines: string) { + const invalidLines: string[] = []; + const validLines = lines.split('\n') + .slice(1, -1) + .map((glob: string) => { + const trimmedGlob = glob.trim(); + const match = trimmedGlob.match(CONTAINS_ANY_GLOBS_REGEX); + if (!match) { + invalidLines.push(trimmedGlob); + return ''; + } + return match[1]; + }) + .filter(globString => !!globString); + return [validLines, invalidLines]; +} diff --git a/dev-infra/pullapprove/logging.ts b/dev-infra/pullapprove/logging.ts new file mode 100644 index 0000000000000..af0f649094328 --- /dev/null +++ b/dev-infra/pullapprove/logging.ts @@ -0,0 +1,42 @@ +/** + * @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 {PullApproveGroupResult} from './group'; + +/** Create logs for each pullapprove group result. */ +export function logGroup(group: PullApproveGroupResult, matched = true) { + const includeConditions = matched ? group.matchedIncludes : group.unmatchedIncludes; + const excludeConditions = matched ? group.matchedExcludes : group.unmatchedExcludes; + console.groupCollapsed(`[${group.groupName}]`); + if (includeConditions.length) { + console.group('includes'); + includeConditions.forEach( + matcher => console.info(`${matcher.glob} - ${matcher.matchedFiles.size}`)); + console.groupEnd(); + } + if (excludeConditions.length) { + console.group('excludes'); + excludeConditions.forEach( + matcher => console.info(`${matcher.glob} - ${matcher.matchedFiles.size}`)); + console.groupEnd(); + } + console.groupEnd(); +} + +/** Logs a header within a text drawn box. */ +export function logHeader(...params: string[]) { + const totalWidth = 80; + const fillWidth = totalWidth - 2; + const headerText = params.join(' ').substr(0, fillWidth); + const leftSpace = Math.ceil((fillWidth - headerText.length) / 2); + const rightSpace = fillWidth - leftSpace - headerText.length; + const fill = (count: number, content: string) => content.repeat(count); + + console.info(`┌${fill(fillWidth, '─')}┐`); + console.info(`│${fill(leftSpace, ' ')}${headerText}${fill(rightSpace, ' ')}│`); + console.info(`└${fill(fillWidth, '─')}┘`); +} \ No newline at end of file diff --git a/dev-infra/pullapprove/parse-yaml.ts b/dev-infra/pullapprove/parse-yaml.ts new file mode 100644 index 0000000000000..5b3d89d47e955 --- /dev/null +++ b/dev-infra/pullapprove/parse-yaml.ts @@ -0,0 +1,35 @@ +/** + * @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 {parse as parseYaml} from 'yaml'; + +export interface PullApproveGroupConfig { + conditions?: string; + reviewers: { + users: string[], + teams?: string[], + }|{ + teams: string[], + }; +} + +export interface PullApproveConfig { + version: number; + github_api_version?: string; + pullapprove_conditions?: { + condition: string, + unmet_status: string, + explanation: string, + }[]; + groups: { + [key: string]: PullApproveGroupConfig, + }; +} + +export function parsePullApproveYaml(rawYaml: string): PullApproveConfig { + return parseYaml(rawYaml) as PullApproveConfig; +} diff --git a/dev-infra/pullapprove/verify.ts b/dev-infra/pullapprove/verify.ts index e43c4f8287edd..f92fd073a3308 100644 --- a/dev-infra/pullapprove/verify.ts +++ b/dev-infra/pullapprove/verify.ts @@ -6,210 +6,115 @@ * found in the LICENSE file at https://angular.io/license */ import {readFileSync} from 'fs'; -import {IMinimatch, Minimatch} from 'minimatch'; import * as path from 'path'; import {cd, exec, set} from 'shelljs'; -import {parse as parseYaml} from 'yaml'; -interface GlobMatcher { - group: string; - glob: string; - matcher: IMinimatch; - matchCount: number; -} +import {getRepoBaseDir} from '../utils/config'; + +import {PullApproveGroup} from './group'; +import {logGroup, logHeader} from './logging'; +import {parsePullApproveYaml} from './parse-yaml'; export function verify() { // Exit early on shelljs errors set('-e'); - - // Regex Matcher for contains_any_globs conditions - const CONTAINS_ANY_GLOBS_REGEX = /^'([^']+)',?$/; - // Full path of the angular project directory - const ANGULAR_PROJECT_DIR = process.cwd(); - // Change to the Angular project directory - cd(ANGULAR_PROJECT_DIR); - // Whether to log verbosely const VERBOSE_MODE = process.argv.includes('-v'); + // Full path of the angular project directory + const PROJECT_DIR = getRepoBaseDir(); + // Change to the Angular project directory + cd(PROJECT_DIR); // Full path to PullApprove config file - const PULL_APPROVE_YAML_PATH = path.resolve(ANGULAR_PROJECT_DIR, '.pullapprove.yml'); + const PULL_APPROVE_YAML_PATH = path.resolve(PROJECT_DIR, '.pullapprove.yml'); // All relative path file names in the git repo, this is retrieved using git rather // that a glob so that we only get files that are checked in, ignoring things like // node_modules, .bazelrc.user, etc - const ALL_FILES = exec('git ls-tree --full-tree -r --name-only HEAD', {silent: true}) - .trim() - .split('\n') - .filter((_: string) => !!_); - if (!ALL_FILES.length) { - console.error( - `No files were found to be in the git tree, did you run this command from \n` + - `inside the angular repository?`); - process.exit(1); - } - - /** Gets the glob matching information from each group's condition. */ - function getGlobMatchersFromCondition( - groupName: string, condition: string): [GlobMatcher[], string[]] { - const trimmedCondition = condition.trim(); - const globMatchers: GlobMatcher[] = []; - const badConditionLines: string[] = []; - - // If the condition starts with contains_any_globs, evaluate all of the globs - if (trimmedCondition.startsWith('contains_any_globs')) { - trimmedCondition.split('\n') - .slice(1, -1) - .map(glob => { - const trimmedGlob = glob.trim(); - const match = trimmedGlob.match(CONTAINS_ANY_GLOBS_REGEX); - if (!match) { - badConditionLines.push(trimmedGlob); - return ''; - } - return match[1]; - }) - .filter(globString => !!globString) - .forEach(globString => globMatchers.push({ - group: groupName, - glob: globString, - matcher: new Minimatch(globString, {dot: true}), - matchCount: 0, - })); - } - return [globMatchers, badConditionLines]; - } - - /** Create logs for each review group. */ - function logGroups(groups: Map<string, Map<string, GlobMatcher>>) { - Array.from(groups.entries()).sort().forEach(([groupName, globs]) => { - console.groupCollapsed(groupName); - Array.from(globs.values()) - .sort((a, b) => b.matchCount - a.matchCount) - .forEach(glob => console.info(`${glob.glob} - ${glob.matchCount}`)); - console.groupEnd(); + const REPO_FILES = + exec('git ls-files', {silent: true}).trim().split('\n').filter((_: string) => !!_); + // The pull approve config file. + const pullApproveYamlRaw = readFileSync(PULL_APPROVE_YAML_PATH, 'utf8'); + // JSON representation of the pullapprove yaml file. + const pullApprove = parsePullApproveYaml(pullApproveYamlRaw); + // All of the groups defined in the pullapprove yaml. + const groups = Object.entries(pullApprove.groups).map(([groupName, group]) => { + return new PullApproveGroup(groupName, group); + }); + // PullApprove groups without matchers. + const groupsWithoutMatchers = groups.filter(group => !group.hasMatchers); + // PullApprove groups with matchers. + const groupsWithMatchers = groups.filter(group => group.hasMatchers); + // All lines from group conditions which are not parsable. + const groupsWithBadLines = groups.filter(g => !!g.getBadLines().length); + // If any groups contains bad lines, log bad lines and exit failing. + if (groupsWithBadLines.length) { + logHeader('PullApprove config file parsing failure'); + console.info(`Discovered errors in ${groupsWithBadLines.length} groups`); + groupsWithBadLines.forEach(group => { + console.info(` - [${group.groupName}]`); + group.getBadLines().forEach(line => console.info(` ${line}`)); }); + console.info( + `Correct the invalid conditions, before PullApprove verification can be completed`); + process.exit(1); } - - /** Logs a header within a text drawn box. */ - function logHeader(...params: string[]) { - const totalWidth = 80; - const fillWidth = totalWidth - 2; - const headerText = params.join(' ').substr(0, fillWidth); - const leftSpace = Math.ceil((fillWidth - headerText.length) / 2); - const rightSpace = fillWidth - leftSpace - headerText.length; - const fill = (count: number, content: string) => content.repeat(count); - - console.info(`┌${fill(fillWidth, '─')}┐`); - console.info(`│${fill(leftSpace, ' ')}${headerText}${fill(rightSpace, ' ')}│`); - console.info(`└${fill(fillWidth, '─')}┘`); - } - - /** Runs the pull approve verification check on provided files. */ - function runVerification(files: string[]) { - // All of the globs created for each group's conditions. - const allGlobs: GlobMatcher[] = []; - // The pull approve config file. - const pullApprove = readFileSync(PULL_APPROVE_YAML_PATH, {encoding: 'utf8'}); - // All of the PullApprove groups, parsed from the PullApprove yaml file. - const parsedPullApproveGroups = - parseYaml(pullApprove).groups as{[key: string]: {conditions: string}}; - // All files which were found to match a condition in PullApprove. - const matchedFiles = new Set<string>(); - // All files which were not found to match a condition in PullApprove. - const unmatchedFiles = new Set<string>(); - // All PullApprove groups which matched at least one file. - const matchedGroups = new Map<string, Map<string, GlobMatcher>>(); - // All PullApprove groups which did not match at least one file. - const unmatchedGroups = new Map<string, Map<string, GlobMatcher>>(); - // All condition lines which were not able to be correctly parsed, by group. - const badConditionLinesByGroup = new Map<string, string[]>(); - // Total number of condition lines which were not able to be correctly parsed. - let badConditionLineCount = 0; - - // Get all of the globs from the PullApprove group conditions. - Object.entries(parsedPullApproveGroups).forEach(([groupName, group]) => { - for (const condition of group.conditions) { - const [matchers, badConditions] = getGlobMatchersFromCondition(groupName, condition); - if (badConditions.length) { - badConditionLinesByGroup.set(groupName, badConditions); - badConditionLineCount += badConditions.length; - } - allGlobs.push(...matchers); - } - }); - - if (badConditionLineCount) { - console.info(`Discovered ${badConditionLineCount} parsing errors in PullApprove conditions`); - console.info(`Attempted parsing using: ${CONTAINS_ANY_GLOBS_REGEX}`); - console.info(); - console.info(`Unable to properly parse the following line(s) by group:`); - badConditionLinesByGroup.forEach((badConditionLines, groupName) => { - console.info(`- ${groupName}:`); - badConditionLines.forEach(line => console.info(` ${line}`)); - }); - console.info(); - console.info( - `Please correct the invalid conditions, before PullApprove verification can be completed`); - process.exit(1); - } - - // Check each file for if it is matched by a PullApprove condition. - for (let file of files) { - const matched = allGlobs.filter(glob => glob.matcher.match(file)); - matched.length ? matchedFiles.add(file) : unmatchedFiles.add(file); - matched.forEach(glob => glob.matchCount++); - } - - // Add each glob for each group to a map either matched or unmatched. - allGlobs.forEach(glob => { - const groups = glob.matchCount ? matchedGroups : unmatchedGroups; - const globs = groups.get(glob.group) || new Map<string, GlobMatcher>(); - // Set the globs map in the groups map - groups.set(glob.group, globs); - // Set the glob in the globs map - globs.set(glob.glob, glob); - }); - - // PullApprove is considered verified if no files or groups are found to be unsed. - const verificationSucceeded = !(unmatchedFiles.size || unmatchedGroups.size); - - /** - * Overall result - */ - logHeader('Result'); - if (verificationSucceeded) { - console.info('PullApprove verification succeeded!'); + // Files which are matched by at least one group. + const matchedFiles: string[] = []; + // Files which are not matched by at least one group. + const unmatchedFiles: string[] = []; + + // Test each file in the repo against each group for being matched. + REPO_FILES.forEach((file: string) => { + if (groupsWithMatchers.filter(group => group.testFile(file)).length) { + matchedFiles.push(file); } else { - console.info(`PullApprove verification failed.\n`); - console.info(`Please update '.pullapprove.yml' to ensure that all necessary`); - console.info(`files/directories have owners and all patterns that appear in`); - console.info(`the file correspond to actual files/directories in the repo.`); + unmatchedFiles.push(file); } - /** - * File by file Summary - */ - logHeader('PullApprove file match results'); - console.groupCollapsed(`Matched Files (${matchedFiles.size} files)`); - VERBOSE_MODE && matchedFiles.forEach(file => console.info(file)); - console.groupEnd(); - console.groupCollapsed(`Unmatched Files (${unmatchedFiles.size} files)`); - unmatchedFiles.forEach(file => console.info(file)); - console.groupEnd(); - - /** - * Group by group Summary - */ - logHeader('PullApprove group matches'); - console.groupCollapsed(`Matched Groups (${matchedGroups.size} groups)`); - VERBOSE_MODE && logGroups(matchedGroups); - console.groupEnd(); - console.groupCollapsed(`Unmatched Groups (${unmatchedGroups.size} groups)`); - logGroups(unmatchedGroups); - console.groupEnd(); - - // Provide correct exit code based on verification success. - process.exit(verificationSucceeded ? 0 : 1); + }); + // Results for each group + const resultsByGroup = groupsWithMatchers.map(group => group.getResults()); + // Whether all group condition lines match at least one file and all files + // are matched by at least one group. + const verificationSucceeded = + resultsByGroup.every(r => !r.unmatchedCount) && !unmatchedFiles.length; + + /** + * Overall result + */ + logHeader('Overall Result'); + if (verificationSucceeded) { + console.info('PullApprove verification succeeded!'); + } else { + console.info(`PullApprove verification failed.\n`); + console.info(`Please update '.pullapprove.yml' to ensure that all necessary`); + console.info(`files/directories have owners and all patterns that appear in`); + console.info(`the file correspond to actual files/directories in the repo.`); } - - - runVerification(ALL_FILES); + /** + * File by file Summary + */ + logHeader('PullApprove results by file'); + console.groupCollapsed(`Matched Files (${matchedFiles.length} files)`); + VERBOSE_MODE && matchedFiles.forEach(file => console.info(file)); + console.groupEnd(); + console.groupCollapsed(`Unmatched Files (${unmatchedFiles.length} files)`); + unmatchedFiles.forEach(file => console.info(file)); + console.groupEnd(); + /** + * Group by group Summary + */ + logHeader('PullApprove results by group'); + console.groupCollapsed(`Groups without matchers (${groupsWithoutMatchers.length} groups)`); + VERBOSE_MODE && groupsWithoutMatchers.forEach(group => console.info(`${group.groupName}`)); + console.groupEnd(); + const matchedGroups = resultsByGroup.filter(group => !group.unmatchedCount); + console.groupCollapsed(`Matched conditions by Group (${matchedGroups.length} groups)`); + VERBOSE_MODE && matchedGroups.forEach(group => logGroup(group)); + console.groupEnd(); + const unmatchedGroups = resultsByGroup.filter(group => group.unmatchedCount); + console.groupCollapsed(`Unmatched conditions by Group (${unmatchedGroups.length} groups)`); + unmatchedGroups.forEach(group => logGroup(group, false)); + console.groupEnd(); + + // Provide correct exit code based on verification success. + process.exit(verificationSucceeded ? 0 : 1); } diff --git a/dev-infra/ts-circular-dependencies/BUILD.bazel b/dev-infra/ts-circular-dependencies/BUILD.bazel index d7c8d6f7e6968..d438b66bd58f9 100644 --- a/dev-infra/ts-circular-dependencies/BUILD.bazel +++ b/dev-infra/ts-circular-dependencies/BUILD.bazel @@ -6,6 +6,7 @@ ts_library( module_name = "@angular/dev-infra-private/ts-circular-dependencies", visibility = ["//dev-infra:__subpackages__"], deps = [ + "//dev-infra/utils:config", "@npm//@types/glob", "@npm//@types/node", "@npm//@types/yargs", diff --git a/dev-infra/ts-circular-dependencies/README.md b/dev-infra/ts-circular-dependencies/README.md new file mode 100644 index 0000000000000..2ed88cf1ba3c8 --- /dev/null +++ b/dev-infra/ts-circular-dependencies/README.md @@ -0,0 +1,82 @@ +### ts-circular-dependencies + +This tool requires a test configuration that declares a set of source files which +should be checked for cyclic dependencies. e.g. + +``` +yarn ts-circular-deps --config ./test-config.js <check|approve> +``` + +### Limitations + +In order to detect cycles, the tool currently visits each source file and runs +depth first search. If the DFS comes across any node that is part of the current +DFS path, then a cycle has been detected and the tool will capture it. + +This algorithm has limitations. For example, consider the following graph: + +![Example graph](./example-graph.png) + +Depending on which source file is considered first, the output of the circular dependency tool +will be different. This is because the tool does not recursively find _all_ possible cycles. This +would be too inefficient for large graphs (especially in the `angular/angular` repository). + +In this concrete example, the tool will visit `r3_test_bed` first. Then the first neighbour +(based on the import in the source file) will be visited. This is `test_bed`. Once done, the +tool will visit the first neighbour of `test_bed`. This is `r3_test_bed` again. The node has +already been visited, and also is part of the current DFS path. The tool captures this as cycle. + +As no more nodes can be visited within that path, the tool continues (as per DFS algorithm) +with visiting the remaining neighbours of `r3_test_bed`. It will visit `test_bed_common` and +then come across `test_bed`. The tool only knows that `test_bed` has already been visited, but +it does not know that it would close a cycle. The tool certainly could know this by recursively +checking neighbours of `test_bed` again, but this is inefficient and will cause the algorithm +to eventually degenerate into brute-force. + +In summary, the tool is unable to capture _all_ elementary cycles in the graph. This does not +mean though that the tool is incorrectly suggesting that there are _no_ cycles in a graph. The +tool is still able to correctly detect whether there are _any_ cycles in a graph or not. For +example, if edge from `r3_test_bed` to `test_bed` is removed, then the tool will be able to +capture at least one of the other cycles. The golden will change in an unexpected way, but it's +**expected** given the trade-off we take for an acceptable running time. + +Other algorithms exist which are proven to print out _all_ the elementary cycles in a directed +graph. For example: + +* [Johnson's algorithm for finding simple cycles][johnson-cycles]. +* [Tarjan's algorithm for enumerating elementary circuits][tarjan-cycles]. + +Experiments with these algorithms unveiled that usual source file graphs we have in Angular +repositories are too large to be processed in acceptable time. At the time of writing, the +source file graph of `angular/angular` consists of 3350 nodes and 8730 edges. + +Algorithms like the one from Donald B. Johnson, which first split the graph into strongly +connected components, and then search for elementary cycles in all components with at least +two vertices, are too inefficient for the source files graphs we have. Time complexity for +such algorithms is described to be `O((n + e)(c + 1))` where `c` is the number of elementary +circuits. Donald B. Johnson describes the number of elementary circuits the followed: + +> Thus the number of elementary circuits in a directed graph can grow faster with n than +the exponential 2" + +This shows quite well that these algorithms become quickly inefficient the more vertices, edges +and simple cycles a graph has. Finding elementary cycles of arbitrary length seems NP-complete as +finding a Hamiltonian cycle with length of `n` is NP-complete too. Below is a quote from a +[paper describing a randomized algorithm](np-complete-cycles) for finding simple cycles of a +_fixed_ length that seems to confirm this hypothesis: + +> It is well known that finding the longest cycle in a graph is a hard problem, since finding +a hamiltonian cycle is NP-complete. Hence finding a simple cycle of length k, for an arbitrary +k, is NP-complete. + +Other tools like `madge` or `dpdm` have the same limitations. + +**Resources**: + +* [Finding all the elementary circuits of a directed graph - Donald. B. Johnson][johnson-cycles] +* [Enumeration of the elementary circuits of a directed graph - Robert Tarjan][tarjan-cycles] +* [Once again: Finding simple cycles in graphs - Carsten Dorgerlohx; Jürgen Wirtgen][np-complete-cycles] + +[johnson-cycles]: https://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF +[tarjan-cycles]: https://ecommons.cornell.edu/bitstream/handle/1813/5941/72-145.pdf?sequence=1&isAllowed=y +[np-complete-cycles]: https://pdfs.semanticscholar.org/16b2/d1a3cf4a8a5dbcad10bb901724631ebead33.pdf diff --git a/dev-infra/ts-circular-dependencies/analyzer.ts b/dev-infra/ts-circular-dependencies/analyzer.ts index 76aa7ab0d324f..079602bebabe2 100644 --- a/dev-infra/ts-circular-dependencies/analyzer.ts +++ b/dev-infra/ts-circular-dependencies/analyzer.ts @@ -13,7 +13,7 @@ import * as ts from 'typescript'; import {getFileStatus} from './file_system'; import {getModuleReferences} from './parser'; -export type ModuleResolver = (specifier: string) => string | null; +export type ModuleResolver = (specifier: string) => string|null; /** * Reference chains describe a sequence of source files which are connected through imports. @@ -69,7 +69,7 @@ export class Analyzer { getSourceFile(filePath: string): ts.SourceFile { const resolvedPath = resolve(filePath); if (this._sourceFileCache.has(resolvedPath)) { - return this._sourceFileCache.get(resolvedPath) !; + return this._sourceFileCache.get(resolvedPath)!; } const fileContent = readFileSync(resolvedPath, 'utf8'); const sourceFile = @@ -105,7 +105,7 @@ export class Analyzer { if (!this.unresolvedFiles.has(originFilePath)) { this.unresolvedFiles.set(originFilePath, [specifier]); } - this.unresolvedFiles.get(originFilePath) !.push(specifier); + this.unresolvedFiles.get(originFilePath)!.push(specifier); } /** Resolves the given import specifier to the corresponding source file. */ diff --git a/dev-infra/ts-circular-dependencies/config.ts b/dev-infra/ts-circular-dependencies/config.ts new file mode 100644 index 0000000000000..cb06ef8171a6b --- /dev/null +++ b/dev-infra/ts-circular-dependencies/config.ts @@ -0,0 +1,59 @@ +/** + * @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 {dirname, isAbsolute, resolve} from 'path'; + +import {ModuleResolver} from './analyzer'; + + +/** Configuration for a circular dependencies test. */ +export interface CircularDependenciesTestConfig { + /** Base directory used for shortening paths in the golden file. */ + baseDir: string; + /** Path to the golden file that is used for checking and approving. */ + goldenFile: string; + /** Glob that resolves source files which should be checked. */ + glob: string + /** + * Optional module resolver function that can be used to resolve modules + * to absolute file paths. + */ + resolveModule?: ModuleResolver; + /** + * Optional command that will be displayed if the golden check failed. This can be used + * to consistently use script aliases for checking/approving the golden. + */ + approveCommand?: string; +} + +/** + * Loads the configuration for the circular dependencies test. If the config cannot be + * loaded, an error will be printed and the process exists with a non-zero exit code. + */ +export function loadTestConfig(configPath: string): CircularDependenciesTestConfig { + const configBaseDir = dirname(configPath); + const resolveRelativePath = (relativePath: string) => resolve(configBaseDir, relativePath); + + try { + const config = require(configPath) as CircularDependenciesTestConfig; + if (!isAbsolute(config.baseDir)) { + config.baseDir = resolveRelativePath(config.baseDir); + } + if (!isAbsolute(config.goldenFile)) { + config.goldenFile = resolveRelativePath(config.goldenFile); + } + if (!isAbsolute(config.glob)) { + config.glob = resolveRelativePath(config.glob); + } + return config; + } catch (e) { + console.error('Could not load test configuration file at: ' + configPath); + console.error(`Failed with: ${e.message}`); + process.exit(1); + } +} diff --git a/dev-infra/ts-circular-dependencies/example-graph.png b/dev-infra/ts-circular-dependencies/example-graph.png new file mode 100644 index 0000000000000..02adffbb86b0a Binary files /dev/null and b/dev-infra/ts-circular-dependencies/example-graph.png differ diff --git a/dev-infra/ts-circular-dependencies/golden.ts b/dev-infra/ts-circular-dependencies/golden.ts index 769975b3b87ce..49c5840fbe90b 100644 --- a/dev-infra/ts-circular-dependencies/golden.ts +++ b/dev-infra/ts-circular-dependencies/golden.ts @@ -20,8 +20,17 @@ export type Golden = CircularDependency[]; * the source file objects are mapped to their relative file names. */ export function convertReferenceChainToGolden(refs: ReferenceChain[], baseDir: string): Golden { - return refs.map( - chain => chain.map(({fileName}) => convertPathToForwardSlash(relative(baseDir, fileName)))); + return refs + .map( + // Normalize cycles as the paths can vary based on which node in the cycle is visited + // first in the analyzer. The paths represent cycles. Hence we can shift nodes in a + // deterministic way so that the goldens don't change unnecessarily and cycle comparison + // is simpler. + chain => normalizeCircularDependency( + chain.map(({fileName}) => convertPathToForwardSlash(relative(baseDir, fileName))))) + // Sort cycles so that the golden doesn't change unnecessarily when cycles are detected + // in different order (e.g. new imports cause cycles to be detected earlier or later). + .sort(compareCircularDependency); } /** @@ -44,6 +53,53 @@ export function compareGoldens(actual: Golden, expected: Golden) { return {newCircularDeps, fixedCircularDeps}; } +/** + * Normalizes the a circular dependency by ensuring that the path starts with the first + * node in alphabetical order. Since the path array represents a cycle, we can make a + * specific node the first element in the path that represents the cycle. + * + * This method is helpful because the path of circular dependencies changes based on which + * file in the path has been visited first by the analyzer. e.g. Assume we have a circular + * dependency represented as: `A -> B -> C`. The analyzer will detect this cycle when it + * visits `A`. Though when a source file that is analyzed before `A` starts importing `B`, + * the cycle path will detected as `B -> C -> A`. This represents the same cycle, but is just + * different due to a limitation of using a data structure that can be written to a text-based + * golden file. + * + * To account for this non-deterministic behavior in goldens, we shift the circular + * dependency path to the first node based on alphabetical order. e.g. `A` will always + * be the first node in the path that represents the cycle. + */ +function normalizeCircularDependency(path: CircularDependency): CircularDependency { + if (path.length <= 1) { + return path; + } + + let indexFirstNode: number = 0; + let valueFirstNode: string = path[0]; + + // Find a node in the cycle path that precedes all other elements + // in terms of alphabetical order. + for (let i = 1; i < path.length; i++) { + const value = path[i]; + if (value.localeCompare(valueFirstNode, 'en') < 0) { + indexFirstNode = i; + valueFirstNode = value; + } + } + + // If the alphabetically first node is already at start of the path, just + // return the actual path as no changes need to be made. + if (indexFirstNode === 0) { + return path; + } + + // Move the determined first node (as of alphabetical order) to the start of a new + // path array. The nodes before the first node in the old path are then concatenated + // to the end of the new path. This is possible because the path represents a cycle. + return [...path.slice(indexFirstNode), ...path.slice(0, indexFirstNode)]; +} + /** Checks whether the specified circular dependencies are equal. */ function isSameCircularDependency(actual: CircularDependency, expected: CircularDependency) { if (actual.length !== expected.length) { @@ -56,3 +112,20 @@ function isSameCircularDependency(actual: CircularDependency, expected: Circular } return true; } + +/** + * Compares two circular dependencies by respecting the alphabetic order of nodes in the + * cycle paths. The first nodes which don't match in both paths are decisive on the order. + */ +function compareCircularDependency(a: CircularDependency, b: CircularDependency): number { + // Go through nodes in both cycle paths and determine whether `a` should be ordered + // before `b`. The first nodes which don't match decide on the order. + for (let i = 0; i < Math.min(a.length, b.length); i++) { + const compareValue = a[i].localeCompare(b[i], 'en'); + if (compareValue !== 0) { + return compareValue; + } + } + // If all nodes are equal in the cycles, the order is based on the length of both cycles. + return a.length - b.length; +} diff --git a/dev-infra/ts-circular-dependencies/index.ts b/dev-infra/ts-circular-dependencies/index.ts index d3db5016b4edb..002fd707a3930 100644 --- a/dev-infra/ts-circular-dependencies/index.ts +++ b/dev-infra/ts-circular-dependencies/index.ts @@ -1,3 +1,4 @@ +#!/usr/bin/env node /** * @license * Copyright Google Inc. All Rights Reserved. @@ -8,7 +9,7 @@ import {existsSync, readFileSync, writeFileSync} from 'fs'; import {sync as globSync} from 'glob'; -import {join, relative, resolve} from 'path'; +import {isAbsolute, relative, resolve} from 'path'; import * as ts from 'typescript'; import * as yargs from 'yargs'; import chalk from 'chalk'; @@ -16,55 +17,44 @@ import chalk from 'chalk'; import {Analyzer, ReferenceChain} from './analyzer'; import {compareGoldens, convertReferenceChainToGolden, Golden} from './golden'; import {convertPathToForwardSlash} from './file_system'; - -const projectDir = join(__dirname, '../../'); -const packagesDir = join(projectDir, 'packages/'); -// The default glob does not capture deprecated packages such as http, or the webworker platform. -const defaultGlob = - join(packagesDir, '!(http|platform-webworker|platform-webworker-dynamic)/**/*.ts'); - -if (require.main === module) { - const {_: command, goldenFile, glob, baseDir, warnings} = - yargs.help() - .version(false) - .strict() - .command('check <golden-file>', 'Checks if the circular dependencies have changed.') - .command('approve <golden-file>', 'Approves the current circular dependencies.') - .demandCommand() - .option( - 'approve', - {type: 'boolean', description: 'Approves the current circular dependencies.'}) - .option('warnings', {type: 'boolean', description: 'Prints all warnings.'}) - .option('base-dir', { - type: 'string', - description: 'Base directory used for shortening paths in the golden file.', - default: projectDir, - defaultDescription: 'Project directory' +import {loadTestConfig, CircularDependenciesTestConfig} from './config'; + + +export function tsCircularDependenciesBuilder(localYargs: yargs.Argv) { + return localYargs.help() + .strict() + .demandCommand() + .option( + 'config', + {type: 'string', demandOption: true, description: 'Path to the configuration file.'}) + .option('warnings', {type: 'boolean', description: 'Prints all warnings.'}) + .command( + 'check', 'Checks if the circular dependencies have changed.', {}, + (argv: yargs.Arguments) => { + const {config: configArg, warnings} = argv; + const configPath = isAbsolute(configArg) ? configArg : resolve(configArg); + const config = loadTestConfig(configPath); + process.exit(main(false, config, warnings)); }) - .option('glob', { - type: 'string', - description: 'Glob that matches source files which should be checked.', - default: defaultGlob, - defaultDescription: 'All release packages' - }) - .argv; - const isApprove = command.includes('approve'); - process.exit(main(baseDir, isApprove, goldenFile, glob, warnings)); + .command( + 'approve', 'Approves the current circular dependencies.', {}, (argv: yargs.Arguments) => { + const {config: configArg, warnings} = argv; + const configPath = isAbsolute(configArg) ? configArg : resolve(configArg); + const config = loadTestConfig(configPath); + process.exit(main(true, config, warnings)); + }); } /** * Runs the ts-circular-dependencies tool. - * @param baseDir Base directory which is used to build up relative file paths in goldens. * @param approve Whether the detected circular dependencies should be approved. - * @param goldenFile Path to the golden file. - * @param glob Glob that is used to collect all source files which should be checked/approved. - * @param printWarnings Whether warnings should be printed. Warnings for unresolved modules/files - * are not printed by default. + * @param config Configuration for the current circular dependencies test. + * @param printWarnings Whether warnings should be printed out. * @returns Status code. */ export function main( - baseDir: string, approve: boolean, goldenFile: string, glob: string, - printWarnings: boolean): number { + approve: boolean, config: CircularDependenciesTestConfig, printWarnings: boolean): number { + const {baseDir, goldenFile, glob, resolveModule, approveCommand} = config; const analyzer = new Analyzer(resolveModule); const cycles: ReferenceChain[] = []; const checkedNodes = new WeakSet<ts.SourceFile>(); @@ -88,17 +78,23 @@ export function main( return 1; } + const warningsCount = analyzer.unresolvedFiles.size + analyzer.unresolvedModules.size; + // By default, warnings for unresolved files or modules are not printed. This is because // it's common that third-party modules are not resolved/visited. Also generated files // from the View Engine compiler (i.e. factories, summaries) cannot be resolved. - if (printWarnings && - (analyzer.unresolvedFiles.size !== 0 || analyzer.unresolvedModules.size !== 0)) { - console.info(chalk.yellow('The following imports could not be resolved:')); - analyzer.unresolvedModules.forEach(specifier => console.info(` • ${specifier}`)); + if (printWarnings && warningsCount !== 0) { + console.info(chalk.yellow('⚠ The following imports could not be resolved:')); + Array.from(analyzer.unresolvedModules) + .sort() + .forEach(specifier => console.info(` • ${specifier}`)); analyzer.unresolvedFiles.forEach((value, key) => { console.info(` • ${getRelativePath(baseDir, key)}`); - value.forEach(specifier => console.info(` ${specifier}`)); + value.sort().forEach(specifier => console.info(` ${specifier}`)); }); + } else { + console.info(chalk.yellow(`⚠ ${warningsCount} imports could not be resolved.`)); + console.info(chalk.yellow(` Please rerun with "--warnings" to inspect unresolved imports.`)); } const expected: Golden = JSON.parse(readFileSync(goldenFile, 'utf8')); @@ -114,22 +110,19 @@ export function main( if (newCircularDeps.length !== 0) { console.error(chalk.yellow(` New circular dependencies which are not allowed:`)); newCircularDeps.forEach(c => console.error(` • ${convertReferenceChainToString(c)}`)); + console.error(); } if (fixedCircularDeps.length !== 0) { console.error( chalk.yellow(` Fixed circular dependencies that need to be removed from the golden:`)); fixedCircularDeps.forEach(c => console.error(` • ${convertReferenceChainToString(c)}`)); - console.info(); - // Print the command for updating the golden. Note that we hard-code the script name for - // approving default packages golden in `goldens/`. We cannot infer the script name passed to - // Yarn automatically since script are launched in a child process where `argv0` is different. - if (resolve(goldenFile) === resolve(projectDir, 'goldens/packages-circular-deps.json')) { - console.info( - chalk.yellow(` Please approve the new golden with: yarn ts-circular-deps:approve`)); + console.error(); + if (approveCommand) { + console.info(chalk.yellow(` Please approve the new golden with: ${approveCommand}`)); } else { console.info(chalk.yellow( ` Please update the golden. The following command can be ` + - `run: yarn ts-circular-deps approve ${getRelativePath(baseDir, goldenFile)}.`)); + `run: yarn ts-circular-deps approve ${getRelativePath(process.cwd(), goldenFile)}.`)); } } return 1; @@ -145,13 +138,6 @@ function convertReferenceChainToString(chain: ReferenceChain<string>) { return chain.join(' → '); } -/** - * Custom module resolver that maps specifiers starting with `@angular/` to the - * local packages folder. - */ -function resolveModule(specifier: string) { - if (specifier.startsWith('@angular/')) { - return packagesDir + specifier.substr('@angular/'.length); - } - return null; +if (require.main === module) { + tsCircularDependenciesBuilder(yargs).parse(); } diff --git a/dev-infra/utils/BUILD.bazel b/dev-infra/utils/BUILD.bazel index 44785fa1b8aba..35637ade5598a 100644 --- a/dev-infra/utils/BUILD.bazel +++ b/dev-infra/utils/BUILD.bazel @@ -5,6 +5,7 @@ ts_library( srcs = [ "config.ts", ], + module_name = "@angular/dev-infra-private/utils", visibility = ["//dev-infra:__subpackages__"], deps = [ "@npm//@types/json5", diff --git a/dev-infra/utils/config.ts b/dev-infra/utils/config.ts index ad92b8946fd50..d3893d404c556 100644 --- a/dev-infra/utils/config.ts +++ b/dev-infra/utils/config.ts @@ -5,16 +5,15 @@ * 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 {parse} from 'json5'; import {readFileSync} from 'fs'; +import {parse} from 'json5'; import {join} from 'path'; import {exec} from 'shelljs'; - /** * Gets the path of the directory for the repository base. */ -function getRepoBaseDir() { +export function getRepoBaseDir() { const baseRepoDir = exec(`git rev-parse --show-toplevel`, {silent: true}); if (baseRepoDir.code) { throw Error( @@ -28,7 +27,7 @@ function getRepoBaseDir() { /** * Retrieve the configuration from the .dev-infra.json file. */ -export function getAngularDevConfig(): DevInfraConfig { +export function getAngularDevConfig<K, T>(): DevInfraConfig<K, T> { const configPath = join(getRepoBaseDir(), '.dev-infra.json'); let rawConfig = ''; try { @@ -41,5 +40,10 @@ export function getAngularDevConfig(): DevInfraConfig { return parse(rawConfig); } -// Interface exressing the expected structure of the DevInfraConfig. -export interface DevInfraConfig {} \ No newline at end of file +/** + * Interface exressing the expected structure of the DevInfraConfig. + * Allows for providing a typing for a part of the config to read. + */ +export interface DevInfraConfig<K, T> { + [K: string]: T; +} diff --git a/goldens/packages-circular-deps.json b/goldens/packages-circular-deps.json index bcad2a7b04ac8..808af2db232c8 100644 --- a/goldens/packages-circular-deps.json +++ b/goldens/packages-circular-deps.json @@ -1,27 +1,123 @@ [ [ - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts" + "packages/animations/browser/src/render/animation_driver.ts", + "packages/animations/browser/src/render/shared.ts" ], [ - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts" + "packages/compiler-cli/ngcc/src/packages/configuration.ts", + "packages/compiler-cli/ngcc/src/packages/entry_point.ts" ], [ - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_init.ts" + "packages/compiler-cli/src/metadata/bundle_index_host.ts", + "packages/compiler-cli/src/transformers/metadata_cache.ts", + "packages/compiler-cli/src/metadata/index.ts" + ], + [ + "packages/compiler-cli/src/metadata/bundle_index_host.ts", + "packages/compiler-cli/src/transformers/metadata_cache.ts", + "packages/compiler-cli/src/transformers/compiler_host.ts", + "packages/compiler-cli/src/metadata/index.ts" + ], + [ + "packages/compiler-cli/src/metadata/bundle_index_host.ts", + "packages/compiler-cli/src/transformers/metadata_cache.ts", + "packages/compiler-cli/src/transformers/compiler_host.ts", + "packages/compiler-cli/src/transformers/metadata_reader.ts", + "packages/compiler-cli/src/metadata/index.ts" + ], + [ + "packages/compiler-cli/src/metadata/collector.ts", + "packages/compiler-cli/src/metadata/evaluator.ts" + ], + [ + "packages/compiler-cli/src/ngtsc/partial_evaluator/src/interface.ts", + "packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts" + ], + [ + "packages/compiler-cli/src/ngtsc/routing/src/analyzer.ts", + "packages/compiler-cli/src/ngtsc/routing/src/lazy.ts" + ], + [ + "packages/compiler-cli/src/ngtsc/scope/src/component_scope.ts", + "packages/compiler-cli/src/ngtsc/scope/src/local.ts" + ], + [ + "packages/compiler-cli/src/ngtsc/typecheck/src/context.ts", + "packages/compiler-cli/src/ngtsc/typecheck/src/host.ts" + ], + [ + "packages/compiler-cli/test/helpers/index.ts", + "packages/compiler-cli/test/helpers/src/mock_file_loading.ts" + ], + [ + "packages/compiler/src/compile_metadata.ts", + "packages/compiler/src/lifecycle_reflector.ts", + "packages/compiler/src/compile_reflector.ts", + "packages/compiler/src/output/output_ast.ts", + "packages/compiler/src/parse_util.ts" + ], + [ + "packages/compiler/src/compile_metadata.ts", + "packages/compiler/src/ml_parser/parser.ts", + "packages/compiler/src/ml_parser/ast.ts", + "packages/compiler/src/i18n/i18n_ast.ts", + "packages/compiler/src/parse_util.ts" + ], + [ + "packages/compiler/src/compile_metadata.ts", + "packages/compiler/src/ml_parser/parser.ts", + "packages/compiler/src/ml_parser/ast.ts", + "packages/compiler/src/parse_util.ts" + ], + [ + "packages/compiler/src/compile_metadata.ts", + "packages/compiler/src/ml_parser/parser.ts", + "packages/compiler/src/ml_parser/lexer.ts", + "packages/compiler/src/parse_util.ts" + ], + [ + "packages/compiler/src/compile_metadata.ts", + "packages/compiler/src/ml_parser/parser.ts", + "packages/compiler/src/parse_util.ts" + ], + [ + "packages/compiler/src/compile_metadata.ts", + "packages/compiler/src/util.ts", + "packages/compiler/src/constant_pool.ts", + "packages/compiler/src/output/output_ast.ts", + "packages/compiler/src/parse_util.ts" + ], + [ + "packages/compiler/src/compile_metadata.ts", + "packages/compiler/src/util.ts", + "packages/compiler/src/output/output_ast.ts", + "packages/compiler/src/parse_util.ts" + ], + [ + "packages/compiler/src/compile_metadata.ts", + "packages/compiler/src/util.ts", + "packages/compiler/src/parse_util.ts" + ], + [ + "packages/compiler/src/output/output_ast.ts", + "packages/compiler/src/render3/view/i18n/meta.ts" + ], + [ + "packages/compiler/src/output/output_ast.ts", + "packages/compiler/src/render3/view/i18n/meta.ts", + "packages/compiler/src/render3/view/i18n/util.ts" + ], + [ + "packages/compiler/src/render3/r3_factory.ts", + "packages/compiler/src/render3/view/util.ts", + "packages/compiler/src/render3/view/api.ts" ], [ + "packages/compiler/src/render3/view/styling_builder.ts", + "packages/compiler/src/render3/view/template.ts" + ], + [ + "packages/core/src/application_init.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -32,14 +128,11 @@ "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts" - ], - [ - "packages/core/src/change_detection/differs/default_iterable_differ.ts", - "packages/core/src/change_detection/differs/iterable_differs.ts" + "packages/core/src/application_ref.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -49,62 +142,21 @@ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", - "packages/core/src/change_detection/differs/keyvalue_differs.ts" - ], - [ - "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", - "packages/core/src/change_detection/differs/keyvalue_differs.ts" - ], - [ - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts" + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts" - ], - [ - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts" - ], - [ "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/linker/element_ref.ts" - ], - [ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/linker/ng_module_factory.ts" + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -112,23 +164,24 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/linker/ng_module_factory.ts", - "packages/core/src/linker/component_factory_resolver.ts" + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/linker/ng_module_factory.ts", - "packages/core/src/linker/component_factory_resolver.ts" - ], - [ - "packages/core/src/linker/ng_module_factory.ts", - "packages/core/src/linker/component_factory_resolver.ts" - ], - [ - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/linker/template_ref.ts" + "packages/core/src/linker/component_factory_resolver.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ "packages/core/src/application_ref.ts", @@ -141,51 +194,40 @@ "packages/core/src/linker/view_ref.ts" ], [ - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/linker/template_ref.ts", - "packages/core/src/linker/view_ref.ts" - ], - [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/linker/view_container_ref.ts" - ], - [ - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/linker/view_container_ref.ts" + "packages/core/src/linker/view_container_ref.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/linker/view_container_ref.ts" - ], - [ - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render/api.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts" - ], - [ + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/container.ts", + "packages/core/src/render3/interfaces/node.ts", + "packages/core/src/render3/interfaces/definition.ts", "packages/core/src/core.ts", "packages/core/src/metadata.ts", "packages/core/src/di.ts", @@ -197,7 +239,9 @@ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -207,17 +251,14 @@ "packages/core/src/render3/assert.ts", "packages/core/src/render3/interfaces/container.ts", "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts" - ], - [ + "packages/core/src/render3/interfaces/definition.ts", + "packages/core/src/render3/interfaces/view.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -228,9 +269,7 @@ "packages/core/src/render3/interfaces/container.ts", "packages/core/src/render3/interfaces/node.ts", "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts" - ], - [ + "packages/core/src/render3/interfaces/view.ts", "packages/core/src/metadata.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", @@ -241,7 +280,9 @@ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -249,35 +290,15 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts" - ], - [ - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component_ref.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -285,26 +306,13 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -312,32 +320,12 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts" - ], - [ - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/view_ref.ts" ], [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -345,46 +333,11 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts" - ], - [ - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts" - ], - [ - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts" - ], - [ + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/core.ts", + "packages/core/src/metadata.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -394,7 +347,9 @@ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -402,15 +357,13 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ "packages/core/src/application_ref.ts", @@ -420,38 +373,31 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts" - ], - [ + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -459,39 +405,31 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -499,24 +437,22 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts" - ], - [ + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -524,19 +460,15 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts" - ], - [ + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/element.ts", + "packages/core/src/view/types.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -546,7 +478,9 @@ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -554,55 +488,43 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts" - ], - [ - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts" + "packages/core/src/view/element.ts", + "packages/core/src/view/util.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/util.ts" - ], - [ - "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/view/entrypoint.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -610,53 +532,28 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/util.ts" - ], - [ - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/util.ts", - "packages/core/src/view/errors.ts" - ], - [ - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/util.ts", - "packages/core/src/view/errors.ts" - ], - [ - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", - "packages/core/src/view/element.ts", - "packages/core/src/view/util.ts" - ], - [ + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts", + "packages/core/src/debug/debug_node.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -664,47 +561,44 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts" + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts", + "packages/core/src/debug/debug_node.ts", + "packages/core/src/render3/util/discovery_utils.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts" - ], - [ + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts", + "packages/core/src/debug/debug_node.ts", + "packages/core/src/render3/util/discovery_utils.ts", + "packages/core/src/render3/instructions/lview_debug.ts", + "packages/core/src/core.ts", + "packages/core/src/metadata.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -714,7 +608,9 @@ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -722,67 +618,44 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts" + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts" - ], - [ - "packages/core/src/core.ts", - "packages/core/src/metadata.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -792,7 +665,9 @@ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -800,33 +675,19 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts" + "packages/core/src/view/provider.ts", + "packages/core/src/view/refs.ts" ], [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -834,55 +695,23 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts" + "packages/core/src/view/provider.ts", + "packages/core/src/view/refs.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -890,33 +719,25 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts" - ], - [ + "packages/core/src/view/provider.ts", + "packages/core/src/view/refs.ts", + "packages/core/src/view/ng_module.ts", + "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -924,35 +745,64 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", + "packages/core/src/view/provider.ts", + "packages/core/src/view/refs.ts", + "packages/core/src/view/ng_module.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/node_manipulation.ts", + "packages/core/src/core.ts", + "packages/core/src/metadata.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -960,57 +810,46 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/di.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/di.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -1018,3275 +857,61 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts" + "packages/core/src/render3/features/providers_feature.ts", + "packages/core/src/render3/di_setup.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts" + "packages/core/src/render3/ng_module_ref.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts" + "packages/core/src/render3/ng_module_ref.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts", - "packages/core/src/render3/util/view_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts", - "packages/core/src/render3/util/view_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts", - "packages/core/src/render3/util/view_utils.ts", - "packages/core/src/render3/interfaces/context.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts", - "packages/core/src/render3/util/view_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts", - "packages/core/src/render3/util/view_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/hooks.ts", - "packages/core/src/render3/state.ts", - "packages/core/src/render3/util/view_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/interfaces/injector.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/interfaces/injector.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/node_assert.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/util/attrs_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/di.ts", - "packages/core/src/render3/util/injector_utils.ts" - ], - [ - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/sanitization/sanitization.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/context_discovery.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/context_discovery.ts" - ], - [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/errors.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/errors.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/node_selector_matcher.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/util/view_traversal_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/util/view_traversal_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/util/view_traversal_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/instructions/advance.ts" - ], - [ - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/instructions/lview_debug.ts" - ], - [ - "packages/core/src/core.ts", - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/instructions/lview_debug.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/instructions/lview_debug.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/instructions/lview_debug.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/instructions/lview_debug.ts" - ], - [ - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/instructions/lview_debug.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/instructions/shared.ts", - "packages/core/src/render3/instructions/lview_debug.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts" - ], - [ - "packages/core/src/core.ts", - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/node_manipulation.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/styling/static_styling.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/styling/static_styling.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/attribute.ts", - "packages/core/src/render3/bindings.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/attribute_interpolation.ts", - "packages/core/src/render3/instructions/interpolation.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/change_detection.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/container.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/container.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/container.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/container.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/container.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/container.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/storage.ts" - ], - [ - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/di.ts" - ], - [ - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/di.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/di.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element.ts", - "packages/core/src/render3/instructions/property.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element.ts", - "packages/core/src/render3/instructions/property.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element_container.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element_container.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element_container.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/element_container.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/embedded_view.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/embedded_view.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/embedded_view.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/embedded_view.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/embedded_view.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/get_current_view.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/listener.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/listener.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/listener.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/projection.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/projection.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/property_interpolation.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts", - "packages/core/src/render3/styling/style_binding_list.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts", - "packages/core/src/render3/styling/style_binding_list.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/styling.ts", - "packages/core/src/render3/styling/style_binding_list.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/text.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/text.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/instructions/all.ts", - "packages/core/src/render3/instructions/host_property.ts" - ], - [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/util/discovery_utils.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/util/discovery_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/util/discovery_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/util/discovery_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/util/discovery_utils.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/component.ts", - "packages/core/src/render3/util/global_utils.ts", - "packages/core/src/render3/util/change_detection_utils.ts", - "packages/core/src/render3/util/discovery_utils.ts" - ], - [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/copy_definition_feature.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/copy_definition_feature.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/copy_definition_feature.ts", - "packages/core/src/render3/features/inherit_definition_feature.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/copy_definition_feature.ts", - "packages/core/src/render3/features/inherit_definition_feature.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/copy_definition_feature.ts", - "packages/core/src/render3/features/inherit_definition_feature.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/ng_onchanges_feature.ts" - ], - [ - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/providers_feature.ts", - "packages/core/src/render3/di_setup.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/providers_feature.ts", - "packages/core/src/render3/di_setup.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/providers_feature.ts", - "packages/core/src/render3/di_setup.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/providers_feature.ts", - "packages/core/src/render3/di_setup.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/providers_feature.ts", - "packages/core/src/render3/di_setup.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/features/providers_feature.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/i18n.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/i18n.ts" - ], - [ - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/i18n.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/i18n.ts" - ], - [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/ng_module_ref.ts" - ], - [ - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/ng_module_ref.ts" - ], - [ - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/ng_module_ref.ts" - ], - [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/ng_module_ref.ts", - "packages/core/src/linker/ng_module_factory_registration.ts" - ], - [ - "packages/core/src/render3/ng_module_ref.ts", - "packages/core/src/linker/ng_module_factory_registration.ts" - ], - [ - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/ng_module_ref.ts" - ], - [ - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/ng_module_ref.ts" - ], - [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/ng_module_ref.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts" - ], - [ - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", @@ -4294,452 +919,215 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/pipe.ts" + "packages/core/src/render3/ng_module_ref.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/pipe.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/pipe.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/pipe.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/pipe.ts", - "packages/core/src/render3/pure_function.ts" - ], - [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts" - ], - [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts" - ], - [ - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts", - "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts" - ], - [ + "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/query.ts" + "packages/core/src/render3/ng_module_ref.ts", + "packages/core/src/linker/ng_module_factory_registration.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/view_engine_compatibility_prebound.ts" + "packages/core/src/render3/ng_module_ref.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/view_engine_compatibility_prebound.ts" + "packages/core/src/render3/ng_module_ref.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/view_engine_compatibility_prebound.ts" + "packages/core/src/render3/pipe.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", "packages/core/src/render3/interfaces/type_checks.ts", "packages/core/src/render3/index.ts", - "packages/core/src/render3/view_engine_compatibility_prebound.ts" + "packages/core/src/render3/pipe.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts" + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", + "packages/core/src/change_detection/differs/keyvalue_differs.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", + "packages/core/src/application_ref.ts", + "packages/core/src/application_tokens.ts", + "packages/core/src/linker/component_factory.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/console.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts" + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", + "packages/core/src/application_ref.ts", "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts", - "packages/core/src/render3/interfaces/type_checks.ts" + "packages/core/src/metadata.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", + "packages/core/src/application_ref.ts", "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts" + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/debug/debug_node.ts" + "packages/core/src/application_ref.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" + ], + [ + "packages/core/src/application_ref.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ + "packages/core/src/application_ref.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/module.ts", + "packages/core/src/metadata.ts", "packages/core/src/di.ts", "packages/core/src/di/index.ts", "packages/core/src/di/injectable.ts", @@ -4749,310 +1137,207 @@ "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" + "packages/core/src/application_ref.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/module.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" + "packages/core/src/application_ref.ts", + "packages/core/src/metadata/resource_loading.ts", + "packages/core/src/metadata/directives.ts", + "packages/core/src/render3/jit/directive.ts", + "packages/core/src/render3/jit/module.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" + "packages/core/src/application_ref.ts", + "packages/core/src/testability/testability.ts", + "packages/core/src/di.ts", + "packages/core/src/di/index.ts", + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts", + "packages/core/src/di/jit/environment.ts", + "packages/core/src/di/injector_compatibility.ts", + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts", + "packages/core/src/render3/definition.ts", + "packages/core/src/metadata/ng_module.ts" ], [ - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/query.ts" + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/linker/component_factory.ts" ], [ - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/linker/ng_module_factory.ts", + "packages/core/src/linker/component_factory_resolver.ts", + "packages/core/src/linker/component_factory.ts" ], [ - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts" + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/linker/view_container_ref.ts", + "packages/core/src/linker/component_factory.ts" ], [ - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/view.ts", - "packages/core/src/view/ng_content.ts" + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component_ref.ts", + "packages/core/src/linker/component_factory.ts" ], [ - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/view.ts" - ], - [ + "packages/core/src/view/element.ts", "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/view.ts", - "packages/core/src/view/pure_expression.ts" + "packages/core/src/linker/component_factory.ts" ], [ - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/view.ts" + "packages/core/src/view/element.ts", + "packages/core/src/view/util.ts" ], [ - "packages/core/src/view/types.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", - "packages/core/src/view/services.ts", - "packages/core/src/view/view.ts", - "packages/core/src/view/text.ts" + "packages/core/src/linker/component_factory.ts" ], [ - "packages/core/src/view/types.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", "packages/core/src/view/services.ts", - "packages/core/src/view/view.ts" + "packages/core/src/linker/component_factory.ts" ], [ - "packages/core/src/view/types.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", "packages/core/src/view/entrypoint.ts", "packages/core/src/view/services.ts", - "packages/core/src/view/view.ts", - "packages/core/src/view/view_attach.ts" + "packages/core/src/view/provider.ts" ], [ - "packages/core/src/view/types.ts", + "packages/core/src/change_detection/change_detection.ts", + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", "packages/core/src/error_handler.ts", "packages/core/src/errors.ts", "packages/core/src/view/index.ts", - "packages/core/src/view/entrypoint.ts" - ], - [ - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts" - ], - [ - "packages/core/src/view/provider.ts", - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts" - ], - [ - "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts" - ], - [ - "packages/core/src/view/types.ts", - "packages/core/src/error_handler.ts", - "packages/core/src/errors.ts", - "packages/core/src/view/index.ts" + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts", + "packages/core/src/view/provider.ts", + "packages/core/src/view/refs.ts" ], [ - "packages/core/src/linker/component_factory.ts", "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts", "packages/core/src/view/provider.ts", "packages/core/src/view/refs.ts", - "packages/core/src/view/ng_module.ts", - "packages/core/src/view/types.ts" + "packages/core/src/linker/component_factory.ts" ], [ - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts" + "packages/core/src/change_detection/change_detector_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts" ], [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts" - ], - [ - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts" - ], - [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts" - ], - [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts" + "packages/core/src/linker/template_ref.ts", + "packages/core/src/linker/view_ref.ts" ], [ + "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", "packages/core/src/render3/component_ref.ts" ], [ - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", "packages/core/src/render3/component_ref.ts", "packages/core/src/render3/view_ref.ts" ], @@ -5060,469 +1345,482 @@ "packages/core/src/change_detection/change_detector_ref.ts", "packages/core/src/render3/view_engine_compatibility.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/render3/view_ref.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/view_engine_compatibility_prebound.ts" ], [ - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/render3/view_ref.ts" + "packages/core/src/change_detection/differs/default_iterable_differ.ts", + "packages/core/src/change_detection/differs/iterable_differs.ts" ], [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/render3/view_ref.ts" + "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", + "packages/core/src/change_detection/differs/keyvalue_differs.ts" ], [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts", - "packages/core/src/render3/component_ref.ts", - "packages/core/src/render3/view_ref.ts" + "packages/core/src/debug/debug_node.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" ], [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/debug/debug_node.ts", + "packages/core/src/render3/util/discovery_utils.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" ], [ - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/compiler.ts" + "packages/core/src/debug/debug_node.ts", + "packages/core/src/render3/util/discovery_utils.ts", + "packages/core/src/render3/instructions/lview_debug.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" ], [ - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts" + "packages/core/src/debug/debug_node.ts", + "packages/core/src/render3/util/discovery_utils.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" ], [ - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", + "packages/core/src/debug/debug_node.ts", + "packages/core/src/render3/util/discovery_utils.ts", + "packages/core/src/render3/util/view_traversal_utils.ts", "packages/core/src/render3/assert.ts", - "packages/core/src/render3/interfaces/container.ts", - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts", - "packages/core/src/linker.ts", - "packages/core/src/linker/system_js_ng_module_factory_loader.ts" + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" ], [ - "packages/core/src/render3/interfaces/node.ts", - "packages/core/src/render3/interfaces/definition.ts", - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts" + "packages/core/src/debug/debug_node.ts", + "packages/core/src/render3/util/discovery_utils.ts", + "packages/core/src/render3/util/view_traversal_utils.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" ], [ - "packages/core/src/render3/interfaces/view.ts", - "packages/core/src/render3/interfaces/query.ts" + "packages/core/src/debug/debug_node.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" ], [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts" + "packages/core/src/di/injectable.ts", + "packages/core/src/di/jit/injectable.ts" ], [ - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/console.ts" + "packages/core/src/di/injector.ts" ], [ - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts" + "packages/core/src/di/r3_injector.ts" ], [ - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", "packages/core/src/di/injector_compatibility.ts", "packages/core/src/di/injector.ts", "packages/core/src/di/r3_injector.ts", "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/metadata/resource_loading.ts", - "packages/core/src/metadata/directives.ts" + "packages/core/src/di/util.ts" ], [ - "packages/core/src/metadata/directives.ts", - "packages/core/src/render3/jit/directive.ts" + "packages/core/src/di/injector.ts", + "packages/core/src/di/r3_injector.ts" ], [ - "packages/core/src/metadata/resource_loading.ts", - "packages/core/src/metadata/directives.ts", - "packages/core/src/render3/jit/directive.ts" + "packages/core/src/di/reflective_errors.ts", + "packages/core/src/di/reflective_injector.ts" ], [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/metadata/resource_loading.ts", - "packages/core/src/metadata/directives.ts", - "packages/core/src/render3/jit/directive.ts" + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/element.ts", + "packages/core/src/view/types.ts" ], [ - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/metadata/resource_loading.ts", - "packages/core/src/metadata/directives.ts", - "packages/core/src/render3/jit/directive.ts", - "packages/core/src/render3/jit/environment.ts" + "packages/core/src/error_handler.ts", + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" ], [ - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/metadata/resource_loading.ts", - "packages/core/src/metadata/directives.ts", - "packages/core/src/render3/jit/directive.ts", - "packages/core/src/render3/jit/module.ts" + "packages/core/src/errors.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/element.ts", + "packages/core/src/view/util.ts", + "packages/core/src/view/errors.ts" + ], + [ + "packages/core/src/linker/component_factory_resolver.ts", + "packages/core/src/linker/ng_module_factory.ts" + ], + [ + "packages/core/src/linker/element_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts" + ], + [ + "packages/core/src/linker/ng_module_factory_registration.ts", + "packages/core/src/render3/ng_module_ref.ts" + ], + [ + "packages/core/src/linker/template_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts" + ], + [ + "packages/core/src/linker/view_container_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts" ], [ - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/metadata/resource_loading.ts", "packages/core/src/metadata/directives.ts", - "packages/core/src/render3/jit/directive.ts", - "packages/core/src/render3/jit/module.ts" + "packages/core/src/render3/jit/directive.ts" ], [ - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/metadata/resource_loading.ts", "packages/core/src/metadata/directives.ts", "packages/core/src/render3/jit/directive.ts", - "packages/core/src/render3/jit/module.ts" + "packages/core/src/metadata/resource_loading.ts" ], [ "packages/core/src/metadata/directives.ts", "packages/core/src/render3/jit/pipe.ts" ], [ - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/application_ref.ts", - "packages/core/src/testability/testability.ts" - ], - [ - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts", - "packages/core/src/di/util.ts" + "packages/core/src/render/api.ts", + "packages/core/src/render3/view_engine_compatibility.ts" ], [ - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component_ref.ts" ], [ - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/view_engine_compatibility.ts" ], [ - "packages/core/src/di/reflective_injector.ts", - "packages/core/src/di/reflective_errors.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts" ], [ - "packages/animations/browser/src/render/animation_driver.ts", - "packages/animations/browser/src/render/shared.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts" ], [ - "packages/core/testing/src/test_bed.ts", - "packages/core/testing/src/r3_test_bed.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/render3/hooks.ts" ], [ - "packages/core/testing/src/test_bed.ts", - "packages/core/testing/src/r3_test_bed.ts", - "packages/core/testing/src/test_bed_common.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/render3/hooks.ts", + "packages/core/src/render3/state.ts" ], [ - "packages/compiler/src/output/output_ast.ts", - "packages/compiler/src/parse_util.ts", - "packages/compiler/src/compile_metadata.ts", - "packages/compiler/src/lifecycle_reflector.ts", - "packages/compiler/src/compile_reflector.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/render3/hooks.ts", + "packages/core/src/render3/state.ts", + "packages/core/src/render3/util/view_utils.ts" ], [ - "packages/compiler/src/parse_util.ts", - "packages/compiler/src/compile_metadata.ts", - "packages/compiler/src/ml_parser/parser.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts" ], [ - "packages/compiler/src/parse_util.ts", - "packages/compiler/src/compile_metadata.ts", - "packages/compiler/src/ml_parser/parser.ts", - "packages/compiler/src/ml_parser/ast.ts", - "packages/compiler/src/i18n/i18n_ast.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/node_manipulation.ts" ], [ - "packages/compiler/src/parse_util.ts", - "packages/compiler/src/compile_metadata.ts", - "packages/compiler/src/ml_parser/parser.ts", - "packages/compiler/src/ml_parser/ast.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/styling/static_styling.ts" ], [ - "packages/compiler/src/parse_util.ts", - "packages/compiler/src/compile_metadata.ts", - "packages/compiler/src/ml_parser/parser.ts", - "packages/compiler/src/ml_parser/lexer.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/container.ts" ], [ - "packages/compiler/src/constant_pool.ts", - "packages/compiler/src/output/output_ast.ts", - "packages/compiler/src/parse_util.ts", - "packages/compiler/src/compile_metadata.ts", - "packages/compiler/src/util.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/element_container.ts" ], [ - "packages/compiler/src/output/output_ast.ts", - "packages/compiler/src/parse_util.ts", - "packages/compiler/src/compile_metadata.ts", - "packages/compiler/src/util.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/element.ts" ], [ - "packages/compiler/src/parse_util.ts", - "packages/compiler/src/compile_metadata.ts", - "packages/compiler/src/util.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/embedded_view.ts" ], [ - "packages/compiler/src/output/output_ast.ts", - "packages/compiler/src/render3/view/i18n/meta.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/styling.ts" ], [ - "packages/compiler/src/output/output_ast.ts", - "packages/compiler/src/render3/view/i18n/meta.ts", - "packages/compiler/src/render3/view/i18n/util.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/styling.ts", + "packages/core/src/render3/styling/style_binding_list.ts" ], [ - "packages/compiler/src/render3/r3_factory.ts", - "packages/compiler/src/render3/view/util.ts", - "packages/compiler/src/render3/view/api.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/query.ts" ], [ - "packages/compiler/src/render3/view/styling_builder.ts", - "packages/compiler/src/render3/view/template.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/query.ts", + "packages/core/src/render3/view_engine_compatibility.ts" ], [ - "packages/compiler-cli/src/metadata/collector.ts", - "packages/compiler-cli/src/metadata/evaluator.ts" + "packages/core/src/render3/assert.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/view_engine_compatibility_prebound.ts", + "packages/core/src/render3/view_engine_compatibility.ts" ], [ - "packages/compiler-cli/src/metadata/index.ts", - "packages/compiler-cli/src/metadata/bundle_index_host.ts", - "packages/compiler-cli/src/transformers/metadata_cache.ts" + "packages/core/src/render3/component_ref.ts", + "packages/core/src/render3/view_ref.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/compiler-cli/src/metadata/index.ts", - "packages/compiler-cli/src/metadata/bundle_index_host.ts", - "packages/compiler-cli/src/transformers/metadata_cache.ts", - "packages/compiler-cli/src/transformers/compiler_host.ts" + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/render3/hooks.ts", + "packages/core/src/render3/state.ts", + "packages/core/src/render3/util/view_utils.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/compiler-cli/src/metadata/index.ts", - "packages/compiler-cli/src/metadata/bundle_index_host.ts", - "packages/compiler-cli/src/transformers/metadata_cache.ts", - "packages/compiler-cli/src/transformers/compiler_host.ts", - "packages/compiler-cli/src/transformers/metadata_reader.ts" + "packages/core/src/render3/component.ts", + "packages/core/src/render3/di.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/compiler-cli/src/ngtsc/partial_evaluator/src/interface.ts", - "packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts" + "packages/core/src/render3/component.ts", + "packages/core/src/render3/instructions/shared.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/compiler-cli/src/ngtsc/scope/src/component_scope.ts", - "packages/compiler-cli/src/ngtsc/scope/src/local.ts" + "packages/core/src/render3/component.ts", + "packages/core/src/render3/node_manipulation.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/compiler-cli/src/ngtsc/typecheck/src/context.ts", - "packages/compiler-cli/src/ngtsc/typecheck/src/host.ts" + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/container.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/compiler-cli/src/ngtsc/routing/src/analyzer.ts", - "packages/compiler-cli/src/ngtsc/routing/src/lazy.ts" + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/element_container.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/router/src/config.ts", - "packages/router/src/router_state.ts" + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/element.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/router/src/config.ts", - "packages/router/src/router_state.ts", - "packages/router/src/shared.ts" + "packages/core/src/render3/component.ts", + "packages/core/src/render3/util/global_utils.ts", + "packages/core/src/render3/util/change_detection_utils.ts", + "packages/core/src/render3/instructions/all.ts", + "packages/core/src/render3/instructions/listener.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/router/src/shared.ts", - "packages/router/src/url_tree.ts" + "packages/core/src/render3/di_setup.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts", + "packages/core/src/render3/features/providers_feature.ts" ], [ - "packages/router/src/shared.ts", - "packages/router/src/url_tree.ts", - "packages/router/src/utils/collection.ts" + "packages/core/src/render3/features/copy_definition_feature.ts", + "packages/core/src/render3/features/inherit_definition_feature.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/router/src/router_outlet_context.ts", - "packages/router/src/directives/router_outlet.ts" + "packages/core/src/render3/features/copy_definition_feature.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/router/src/router.ts", - "packages/router/src/operators/activate_routes.ts" + "packages/core/src/render3/i18n.ts", + "packages/core/src/render3/interfaces/type_checks.ts", + "packages/core/src/render3/index.ts" ], [ - "packages/router/src/router.ts", - "packages/router/src/operators/apply_redirects.ts" + "packages/core/src/render3/interfaces/container.ts", + "packages/core/src/render3/interfaces/node.ts", + "packages/core/src/render3/interfaces/definition.ts", + "packages/core/src/render3/interfaces/view.ts" ], [ - "packages/router/src/router.ts", - "packages/router/src/operators/check_guards.ts" + "packages/core/src/render3/interfaces/definition.ts", + "packages/core/src/render3/interfaces/node.ts" ], [ - "packages/router/src/router.ts", - "packages/router/src/operators/recognize.ts" + "packages/core/src/render3/interfaces/definition.ts", + "packages/core/src/render3/interfaces/view.ts" ], [ - "packages/router/src/router.ts", - "packages/router/src/operators/resolve_data.ts" + "packages/core/src/render3/interfaces/definition.ts", + "packages/core/src/render3/interfaces/view.ts", + "packages/core/src/render3/interfaces/node.ts" ], [ - "packages/forms/src/model.ts", - "packages/forms/src/directives/shared.ts" + "packages/core/src/render3/interfaces/definition.ts", + "packages/core/src/render3/interfaces/view.ts", + "packages/core/src/render3/interfaces/query.ts", + "packages/core/src/render3/interfaces/node.ts" ], [ - "packages/forms/src/model.ts", - "packages/forms/src/directives/shared.ts", - "packages/forms/src/validators.ts", - "packages/forms/src/directives/validators.ts" + "packages/core/src/render3/interfaces/query.ts", + "packages/core/src/render3/interfaces/view.ts" ], [ - "packages/forms/src/validators.ts", - "packages/forms/src/directives/validators.ts" + "packages/core/testing/src/r3_test_bed_compiler.ts", + "packages/core/testing/src/test_bed_common.ts", + "packages/core/testing/src/test_bed.ts", + "packages/core/testing/src/r3_test_bed.ts" ], [ - "packages/forms/src/model.ts", - "packages/forms/src/directives/shared.ts", - "packages/forms/src/validators.ts" + "packages/core/testing/src/r3_test_bed.ts", + "packages/core/testing/src/test_bed.ts" ], [ "packages/forms/src/directives/abstract_control_directive.ts", "packages/forms/src/model.ts", "packages/forms/src/directives/shared.ts" ], - [ - "packages/forms/src/model.ts", - "packages/forms/src/directives/shared.ts", - "packages/forms/src/directives/abstract_form_group_directive.ts" - ], [ "packages/forms/src/directives/abstract_control_directive.ts", "packages/forms/src/model.ts", @@ -5531,11 +1829,13 @@ "packages/forms/src/directives/control_container.ts" ], [ + "packages/forms/src/directives/abstract_control_directive.ts", "packages/forms/src/model.ts", "packages/forms/src/directives/shared.ts", "packages/forms/src/directives/abstract_form_group_directive.ts", "packages/forms/src/directives/control_container.ts", - "packages/forms/src/directives/form_interface.ts" + "packages/forms/src/directives/form_interface.ts", + "packages/forms/src/directives/ng_control.ts" ], [ "packages/forms/src/directives/abstract_form_group_directive.ts", @@ -5543,13 +1843,20 @@ "packages/forms/src/directives/form_interface.ts" ], [ - "packages/forms/src/directives/abstract_control_directive.ts", - "packages/forms/src/model.ts", - "packages/forms/src/directives/shared.ts", "packages/forms/src/directives/abstract_form_group_directive.ts", "packages/forms/src/directives/control_container.ts", "packages/forms/src/directives/form_interface.ts", - "packages/forms/src/directives/ng_control.ts" + "packages/forms/src/model.ts", + "packages/forms/src/directives/shared.ts" + ], + [ + "packages/forms/src/directives/abstract_form_group_directive.ts", + "packages/forms/src/directives/shared.ts" + ], + [ + "packages/forms/src/directives/abstract_form_group_directive.ts", + "packages/forms/src/model.ts", + "packages/forms/src/directives/shared.ts" ], [ "packages/forms/src/directives/control_container.ts", @@ -5557,108 +1864,149 @@ "packages/forms/src/directives/ng_control.ts" ], [ - "packages/forms/src/directives/shared.ts", - "packages/forms/src/directives/abstract_form_group_directive.ts" + "packages/forms/src/directives/ng_form.ts", + "packages/forms/src/directives/ng_model.ts" ], [ - "packages/forms/src/model.ts", - "packages/forms/src/directives/shared.ts", - "packages/forms/src/directives/normalize_validator.ts" + "packages/forms/src/directives/ng_form.ts", + "packages/forms/src/directives/ng_model.ts", + "packages/forms/src/directives/ng_model_group.ts" ], [ + "packages/forms/src/directives/normalize_validator.ts", "packages/forms/src/model.ts", - "packages/forms/src/directives/shared.ts", - "packages/forms/src/directives/reactive_directives/form_group_name.ts" + "packages/forms/src/directives/shared.ts" ], [ + "packages/forms/src/directives/reactive_directives/form_control_directive.ts", "packages/forms/src/directives/shared.ts", - "packages/forms/src/directives/reactive_directives/form_group_name.ts" + "packages/forms/src/directives/reactive_directives/form_group_name.ts", + "packages/forms/src/directives/reactive_directives/form_group_directive.ts", + "packages/forms/src/directives/reactive_directives/form_control_name.ts" ], [ + "packages/forms/src/directives/reactive_directives/form_control_directive.ts", "packages/forms/src/model.ts", "packages/forms/src/directives/shared.ts", "packages/forms/src/directives/reactive_directives/form_group_name.ts", - "packages/forms/src/directives/reactive_directives/form_group_directive.ts" + "packages/forms/src/directives/reactive_directives/form_group_directive.ts", + "packages/forms/src/directives/reactive_directives/form_control_name.ts" ], [ - "packages/forms/src/directives/shared.ts", - "packages/forms/src/directives/reactive_directives/form_group_name.ts", + "packages/forms/src/directives/reactive_directives/form_control_name.ts", "packages/forms/src/directives/reactive_directives/form_group_directive.ts" ], [ - "packages/forms/src/model.ts", - "packages/forms/src/directives/shared.ts", + "packages/forms/src/directives/reactive_directives/form_control_name.ts", "packages/forms/src/directives/reactive_directives/form_group_name.ts", - "packages/forms/src/directives/reactive_directives/form_group_directive.ts", - "packages/forms/src/directives/reactive_directives/form_control_name.ts" + "packages/forms/src/directives/reactive_directives/form_group_directive.ts" ], [ + "packages/forms/src/directives/reactive_directives/form_control_name.ts", "packages/forms/src/directives/shared.ts", "packages/forms/src/directives/reactive_directives/form_group_name.ts", - "packages/forms/src/directives/reactive_directives/form_group_directive.ts", - "packages/forms/src/directives/reactive_directives/form_control_name.ts" + "packages/forms/src/directives/reactive_directives/form_group_directive.ts" ], [ + "packages/forms/src/directives/reactive_directives/form_control_name.ts", "packages/forms/src/model.ts", "packages/forms/src/directives/shared.ts", "packages/forms/src/directives/reactive_directives/form_group_name.ts", - "packages/forms/src/directives/reactive_directives/form_group_directive.ts", - "packages/forms/src/directives/reactive_directives/form_control_name.ts", - "packages/forms/src/directives/reactive_directives/form_control_directive.ts" + "packages/forms/src/directives/reactive_directives/form_group_directive.ts" ], [ - "packages/forms/src/directives/shared.ts", - "packages/forms/src/directives/reactive_directives/form_group_name.ts", "packages/forms/src/directives/reactive_directives/form_group_directive.ts", - "packages/forms/src/directives/reactive_directives/form_control_name.ts", - "packages/forms/src/directives/reactive_directives/form_control_directive.ts" + "packages/forms/src/directives/reactive_directives/form_group_name.ts" ], [ "packages/forms/src/directives/reactive_directives/form_group_directive.ts", - "packages/forms/src/directives/reactive_directives/form_control_name.ts" + "packages/forms/src/directives/shared.ts", + "packages/forms/src/directives/reactive_directives/form_group_name.ts" ], [ - "packages/forms/src/directives/reactive_directives/form_group_name.ts", "packages/forms/src/directives/reactive_directives/form_group_directive.ts", - "packages/forms/src/directives/reactive_directives/form_control_name.ts" + "packages/forms/src/model.ts", + "packages/forms/src/directives/shared.ts", + "packages/forms/src/directives/reactive_directives/form_group_name.ts" ], [ "packages/forms/src/directives/reactive_directives/form_group_name.ts", - "packages/forms/src/directives/reactive_directives/form_group_directive.ts" - ], - [ - "packages/forms/src/directives/ng_form.ts", - "packages/forms/src/directives/ng_model.ts" + "packages/forms/src/directives/shared.ts" ], [ - "packages/forms/src/directives/ng_form.ts", - "packages/forms/src/directives/ng_model.ts", - "packages/forms/src/directives/ng_model_group.ts" + "packages/forms/src/directives/reactive_directives/form_group_name.ts", + "packages/forms/src/model.ts", + "packages/forms/src/directives/shared.ts" ], [ - "packages/compiler-cli/ngcc/src/packages/configuration.ts", - "packages/compiler-cli/ngcc/src/packages/entry_point.ts" + "packages/forms/src/directives/shared.ts", + "packages/forms/src/model.ts" ], [ - "packages/compiler-cli/test/helpers/index.ts", - "packages/compiler-cli/test/helpers/src/mock_file_loading.ts" + "packages/forms/src/directives/shared.ts", + "packages/forms/src/validators.ts", + "packages/forms/src/directives/validators.ts", + "packages/forms/src/model.ts" ], [ - "packages/language-service/src/common.ts", - "packages/language-service/src/types.ts" + "packages/forms/src/directives/shared.ts", + "packages/forms/src/validators.ts", + "packages/forms/src/model.ts" ], [ - "packages/language-service/src/expression_diagnostics.ts", - "packages/language-service/src/utils.ts" + "packages/forms/src/directives/validators.ts", + "packages/forms/src/validators.ts" ], [ - "packages/language-service/src/language_service.ts", "packages/language-service/src/completions.ts", "packages/language-service/src/template.ts", - "packages/language-service/src/typescript_host.ts" + "packages/language-service/src/typescript_host.ts", + "packages/language-service/src/language_service.ts" ], [ "packages/language-service/src/template.ts", "packages/language-service/src/typescript_host.ts" + ], + [ + "packages/router/src/config.ts", + "packages/router/src/router_state.ts" + ], + [ + "packages/router/src/config.ts", + "packages/router/src/router_state.ts", + "packages/router/src/shared.ts" + ], + [ + "packages/router/src/directives/router_outlet.ts", + "packages/router/src/router_outlet_context.ts" + ], + [ + "packages/router/src/operators/activate_routes.ts", + "packages/router/src/router.ts" + ], + [ + "packages/router/src/operators/apply_redirects.ts", + "packages/router/src/router.ts" + ], + [ + "packages/router/src/operators/check_guards.ts", + "packages/router/src/router.ts" + ], + [ + "packages/router/src/operators/recognize.ts", + "packages/router/src/router.ts" + ], + [ + "packages/router/src/operators/resolve_data.ts", + "packages/router/src/router.ts" + ], + [ + "packages/router/src/shared.ts", + "packages/router/src/url_tree.ts" + ], + [ + "packages/router/src/shared.ts", + "packages/router/src/url_tree.ts", + "packages/router/src/utils/collection.ts" ] ] \ No newline at end of file diff --git a/goldens/public-api/common/common.d.ts b/goldens/public-api/common/common.d.ts index 3cbe877311fb5..c36711e0fd71c 100644 --- a/goldens/public-api/common/common.d.ts +++ b/goldens/public-api/common/common.d.ts @@ -139,10 +139,17 @@ export declare class KeyValuePipe implements PipeTransform { transform<V>(input: { [key: string]: V; } | Map<string, V>, compareFn?: (a: KeyValue<string, V>, b: KeyValue<string, V>) => number): Array<KeyValue<string, V>>; + transform<V>(input: { + [key: string]: V; + } | Map<string, V> | null, compareFn?: (a: KeyValue<string, V>, b: KeyValue<string, V>) => number): Array<KeyValue<string, V>> | null; transform<V>(input: { [key: number]: V; } | Map<number, V>, compareFn?: (a: KeyValue<number, V>, b: KeyValue<number, V>) => number): Array<KeyValue<number, V>>; + transform<V>(input: { + [key: number]: V; + } | Map<number, V> | null, compareFn?: (a: KeyValue<number, V>, b: KeyValue<number, V>) => number): Array<KeyValue<number, V>> | null; transform<K, V>(input: Map<K, V>, compareFn?: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number): Array<KeyValue<K, V>>; + transform<K, V>(input: Map<K, V> | null, compareFn?: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number): Array<KeyValue<K, V>> | null; } export declare class Location { diff --git a/goldens/public-api/core/core.d.ts b/goldens/public-api/core/core.d.ts index f47b73703b5bd..b123b36d35284 100644 --- a/goldens/public-api/core/core.d.ts +++ b/goldens/public-api/core/core.d.ts @@ -715,7 +715,7 @@ export declare type ɵɵComponentDefWithMeta<T, Selector extends String, ExportA [key: string]: string; }, OutputMap extends { [key: string]: string; -}, QueryFields extends string[]> = ɵComponentDef<T>; +}, QueryFields extends string[], NgContentSelectors extends string[]> = ɵComponentDef<T>; export declare function ɵɵcomponentHostSyntheticListener(eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean, eventTargetResolver?: GlobalTargetResolver): typeof ɵɵcomponentHostSyntheticListener; @@ -834,7 +834,7 @@ export declare function ɵɵembeddedViewStart(viewBlockId: number, decls: number export declare function ɵɵenableBindings(): void; -export declare type ɵɵFactoryDef<T> = () => T; +export declare type ɵɵFactoryDef<T, CtorDependencies extends CtorDependency[]> = () => T; export declare function ɵɵgetCurrentView(): OpaqueViewState; @@ -1064,7 +1064,7 @@ export declare function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): v export declare function ɵɵtemplate(index: number, templateFn: ComponentTemplate<any> | null, decls: number, vars: number, tagName?: string | null, attrsIndex?: number | null, localRefsIndex?: number | null, localRefExtractor?: LocalRefExtractor): void; -export declare function ɵɵtemplateRefExtractor(tNode: TNode, currentView: ɵangular_packages_core_core_bp): TemplateRef<unknown> | null; +export declare function ɵɵtemplateRefExtractor(tNode: TNode, currentView: ɵangular_packages_core_core_bo): TemplateRef<unknown> | null; export declare function ɵɵtext(index: number, value?: string): void; diff --git a/goldens/public-api/router/router.d.ts b/goldens/public-api/router/router.d.ts index 58fda6ec4781e..a09f01f799582 100644 --- a/goldens/public-api/router/router.d.ts +++ b/goldens/public-api/router/router.d.ts @@ -500,7 +500,7 @@ export declare abstract class UrlHandlingStrategy { abstract shouldProcessUrl(url: UrlTree): boolean; } -export declare type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) => UrlMatchResult; +export declare type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) => UrlMatchResult | null; export declare type UrlMatchResult = { consumed: UrlSegment[]; diff --git a/gulpfile.js b/gulpfile.js index 49157e466e6e6..eed70c10b73f0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -26,27 +26,30 @@ function loadTask(fileName, taskName) { return task(gulp); } -// Check source code for formatting errors in all source files. -gulp.task('format:enforce', loadTask('format', 'enforce')); +//####################################################### +// A format and enforce task for different sets of files. +//####################################################### -// Format all source files. +// All source files. gulp.task('format:all', loadTask('format', 'format')); +gulp.task('format:all:enforce', loadTask('format', 'enforce')); -// Format only untracked source code files. +// Untracked source code files. gulp.task('format:untracked', loadTask('format', 'format-untracked')); +gulp.task('format:untracked:enforce', loadTask('format', 'enforce-untracked')); -// Format only the changed, tracked source code files. +// Changed, tracked source code files. gulp.task('format:diff', loadTask('format', 'format-diff')); +gulp.task('format:diff:enforce', loadTask('format', 'enforce-diff')); -// Format only changed lines based on the diff from the provided --branch -// argument (or `master` by default). +// Changed, both tracked and untracked, source code files. gulp.task('format:changed', ['format:untracked', 'format:diff']); +gulp.task('format:changed:enforce', ['format:untracked:enforce', 'format:diff:enforce']); // Alias for `format:changed` that formerly formatted all files. gulp.task('format', ['format:changed']); -gulp.task('lint', ['format:enforce', 'validate-commit-messages']); -gulp.task('validate-commit-messages', loadTask('validate-commit-message')); +gulp.task('lint', ['format:changed:enforce']); gulp.task('source-map-test', loadTask('source-map-test')); gulp.task('changelog', loadTask('changelog')); gulp.task('changelog:zonejs', loadTask('changelog-zonejs')); diff --git a/integration/_payload-limits.json b/integration/_payload-limits.json index 54704a01b4002..0bff4575c24b8 100644 --- a/integration/_payload-limits.json +++ b/integration/_payload-limits.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 141569, + "main-es2015": 142073, "polyfills-es2015": 36657 } } @@ -21,7 +21,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 147647, + "main-es2015": 148196, "polyfills-es2015": 36657 } } @@ -39,7 +39,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 247198, + "main-es2015": 247727, "polyfills-es2015": 36657, "5-es2015": 751 } @@ -49,7 +49,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 226144, + "main-es2015": 226728, "polyfills-es2015": 36657, "5-es2015": 779 } diff --git a/integration/bazel/WORKSPACE b/integration/bazel/WORKSPACE index 0e1727733210a..38cfce17a63a2 100644 --- a/integration/bazel/WORKSPACE +++ b/integration/bazel/WORKSPACE @@ -5,8 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Fetch rules_nodejs so we can install our npm dependencies http_archive( name = "build_bazel_rules_nodejs", - sha256 = "2eca5b934dee47b5ff304f502ae187c40ec4e33e12bcbce872a2eeb786e23269", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.4.1/rules_nodejs-1.4.1.tar.gz"], + sha256 = "f9e7b9f42ae202cc2d2ce6d698ccb49a9f7f7ea572a78fd451696d03ef2ee116", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.6.0/rules_nodejs-1.6.0.tar.gz"], ) # Fetch sass rules for compiling sass files diff --git a/integration/bazel/package.json b/integration/bazel/package.json index b367c3fb328f5..ed91345527953 100644 --- a/integration/bazel/package.json +++ b/integration/bazel/package.json @@ -16,18 +16,18 @@ "reflect-metadata": "0.1.12", "rxjs": "file:../../node_modules/rxjs", "tslib": "file:../../node_modules/tslib", - "zone.js": "0.10.2" + "zone.js": "0.10.3" }, "devDependencies": { "@angular/bazel": "file:../../dist/packages-dist/bazel", "@angular/compiler": "file:../../dist/packages-dist/compiler", "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", "@bazel/bazelisk": "file:../../node_modules/@bazel/bazelisk", - "@bazel/karma": "1.4.1", - "@bazel/protractor": "1.4.1", - "@bazel/rollup": "1.4.1", - "@bazel/terser": "1.4.1", - "@bazel/typescript": "1.4.1", + "@bazel/karma": "1.6.0", + "@bazel/protractor": "1.6.0", + "@bazel/rollup": "1.6.0", + "@bazel/terser": "1.6.0", + "@bazel/typescript": "1.6.0", "@types/jasmine": "2.8.8", "http-server": "0.12.0", "karma": "4.4.1", diff --git a/integration/bazel/yarn.lock b/integration/bazel/yarn.lock index 9e305a7d4906d..a87a6e1478df6 100644 --- a/integration/bazel/yarn.lock +++ b/integration/bazel/yarn.lock @@ -3,10 +3,10 @@ "@angular/animations@file:../../dist/packages-dist/animations": - version "9.1.0-next.4" + version "10.0.0-next.1" "@angular/bazel@file:../../dist/packages-dist/bazel": - version "9.1.0-next.4" + version "10.0.0-next.1" dependencies: "@microsoft/api-extractor" "^7.3.9" shelljs "0.8.2" @@ -22,10 +22,10 @@ parse5 "^5.0.0" "@angular/common@file:../../dist/packages-dist/common": - version "9.1.0-next.4" + version "10.0.0-next.1" "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": - version "9.1.0-next.4" + version "10.0.0-next.1" dependencies: canonical-path "1.0.0" chokidar "^3.0.0" @@ -41,13 +41,13 @@ yargs "15.3.0" "@angular/compiler@file:../../dist/packages-dist/compiler": - version "9.1.0-next.4" + version "10.0.0-next.1" "@angular/core@file:../../dist/packages-dist/core": - version "9.1.0-next.4" + version "10.0.0-next.1" "@angular/forms@file:../../dist/packages-dist/forms": - version "9.1.0-next.4" + version "10.0.0-next.1" "@angular/material@8.0.1": version "8.0.1" @@ -57,43 +57,43 @@ tslib "^1.7.1" "@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic": - version "9.1.0-next.4" + version "10.0.0-next.1" "@angular/platform-browser@file:../../dist/packages-dist/platform-browser": - version "9.1.0-next.4" + version "10.0.0-next.1" "@angular/router@file:../../dist/packages-dist/router": - version "9.1.0-next.4" + version "10.0.0-next.1" "@bazel/bazelisk@file:../../node_modules/@bazel/bazelisk": version "1.3.0" -"@bazel/karma@1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.4.1.tgz#12106dfd50ffd8ce98a35a0000badcb9d730c5c4" - integrity sha512-6VecxKGyMam1kA4mGX1GHPgustw8hdyR6v9LTvPWJQ7P5drZbvXUTMz+MLEzbElWdB2KDMljkJRlspbCXuTWew== +"@bazel/karma@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.6.0.tgz#98950b71114dd9ec169e6778a35d31ae1f578655" + integrity sha512-9cX0E1SiMWwA70ZMFnMzeqSRn3biduGx03bGV77FSUYKocZpyfU2cOEygYGfxAqHnyM7x4cS8nflRv3+ZE0Aqg== dependencies: tmp "0.1.0" -"@bazel/protractor@1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.4.1.tgz#fb668429ddc7d1d725d9ce9cf16a29e0e39ab2e0" - integrity sha512-AkgKdd+6cv6tSmCyop11jgfkpCGWsmykgBOAUftBPK2qtn5iHDSpI2xrZUQQv1/F9vhFFvy+09sIslGWP/TyEA== +"@bazel/protractor@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.6.0.tgz#cf095a1dbc038def7031c513a3b87f4e79bedb00" + integrity sha512-gPiRv0oUJbVPpQ9nrwe5vjkffAc8VsYJhpTGgG+8aPdOaTLWgmBP/sy4BdfijU9O1Z/mNYojQCZgMzQz6kAvdg== -"@bazel/rollup@1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.4.1.tgz#395fbd2611867a54be154ecff1239fcde261371b" - integrity sha512-CU0D+xPQbspuVsKhv2sP0kWhIuEQo8eYjhQXP9BXVZ02uc+YCOkW/5JHIhF946QYljp++W6mcVUGFdBYcrVZow== +"@bazel/rollup@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.6.0.tgz#c0bdad0ad0ba5c5b2e21d1634dc2ce48840ca044" + integrity sha512-MLF7laHX3CSAJH+RbIEVWgnQdz3U8dPkdJWJqiX/z9mUSEgC47LNsMBPKlRy1TpOJOpw1j0vLaJv0qN/bgq9NQ== -"@bazel/terser@1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.4.1.tgz#af4d63c768e72d57674a4514857fea5d9984378d" - integrity sha512-JGhv22d2IOGePE0VwmF6c88GOP3Ij621qtLoI7ZVLDRTElSRAknqj09qX6Zni2DkjT2c/+Ldm2qE8P6hOCXpEg== +"@bazel/terser@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.6.0.tgz#63ccd20dd6c9793e7b3b23fb5ea82b55b3ef6eb2" + integrity sha512-csBrN4XfX/hYTtDVcH/ulVO9K4Ca/IlrCWk5o/l7JBJq/cHoTj5AWIA7PKJ4QgnxXeEjso4CmLFgUMEVKVYV3Q== -"@bazel/typescript@1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.4.1.tgz#1c718ce6de694e1bb90f894fa6320a0cadd90850" - integrity sha512-+R/cZcQpuvGfxqc9s/qu/LBWGNV93iSPTt/mvXq6hfH+sABmrrC/E0+WxapuQaaPdKqR3zSdDIIBV70FUMozsg== +"@bazel/typescript@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.6.0.tgz#8dfd29e71bcf917d5f9cb67f19ac4dcfc9082439" + integrity sha512-vAKuwy1Hgl+t3M3sH/G0oqHRYN35TdENj+0lsCI3x7EbSzyI6cbA3YQrLrlyvdScksqOpZa3PZ3UBGqfJJq2DA== dependencies: protobufjs "6.8.8" semver "5.6.0" @@ -2084,7 +2084,7 @@ rollup@1.27.5: acorn "^7.1.0" "rxjs@file:../../node_modules/rxjs": - version "6.5.3" + version "6.5.4" dependencies: tslib "^1.9.0" @@ -2437,7 +2437,7 @@ tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0: integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== "tslib@file:../../node_modules/tslib": - version "1.10.0" + version "1.11.1" tsutils@2.27.2: version "2.27.2" @@ -2700,7 +2700,7 @@ z-schema@~3.18.3: optionalDependencies: commander "^2.7.1" -zone.js@0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.2.tgz#67ca084b3116fc33fc40435e0d5ea40a207e392e" - integrity sha512-UAYfiuvxLN4oyuqhJwd21Uxb4CNawrq6fPS/05Su5L4G+1TN+HVDJMUHNMobVQDFJRir2cLAODXwluaOKB7HFg== +zone.js@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16" + integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg== diff --git a/integration/cli-hello-world-ivy-compat/yarn.lock b/integration/cli-hello-world-ivy-compat/yarn.lock index 4b3eb4f1f90ef..a9b3ba8f71af9 100644 --- a/integration/cli-hello-world-ivy-compat/yarn.lock +++ b/integration/cli-hello-world-ivy-compat/yarn.lock @@ -9470,4 +9470,4 @@ yn@^3.0.0: integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/cli-hello-world-ivy-i18n/yarn.lock b/integration/cli-hello-world-ivy-i18n/yarn.lock index 3e1ccb8077246..db8afee3a5800 100644 --- a/integration/cli-hello-world-ivy-i18n/yarn.lock +++ b/integration/cli-hello-world-ivy-i18n/yarn.lock @@ -9393,4 +9393,4 @@ yn@^3.0.0: integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/cli-hello-world-ivy-minimal/yarn.lock b/integration/cli-hello-world-ivy-minimal/yarn.lock index 4b3eb4f1f90ef..a9b3ba8f71af9 100644 --- a/integration/cli-hello-world-ivy-minimal/yarn.lock +++ b/integration/cli-hello-world-ivy-minimal/yarn.lock @@ -9470,4 +9470,4 @@ yn@^3.0.0: integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/cli-hello-world-lazy-rollup/yarn.lock b/integration/cli-hello-world-lazy-rollup/yarn.lock index 064f1f2696a6c..f622c33cac979 100644 --- a/integration/cli-hello-world-lazy-rollup/yarn.lock +++ b/integration/cli-hello-world-lazy-rollup/yarn.lock @@ -9045,4 +9045,4 @@ yn@^3.0.0: integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/cli-hello-world-lazy/yarn.lock b/integration/cli-hello-world-lazy/yarn.lock index 064f1f2696a6c..f622c33cac979 100644 --- a/integration/cli-hello-world-lazy/yarn.lock +++ b/integration/cli-hello-world-lazy/yarn.lock @@ -9045,4 +9045,4 @@ yn@^3.0.0: integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/cli-hello-world/yarn.lock b/integration/cli-hello-world/yarn.lock index b790bbb84f8a6..5d07c26fe88d8 100644 --- a/integration/cli-hello-world/yarn.lock +++ b/integration/cli-hello-world/yarn.lock @@ -10007,4 +10007,4 @@ yn@^3.0.0: integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/dynamic-compiler/yarn.lock b/integration/dynamic-compiler/yarn.lock index 1970e297e6f36..9bd3256ed91ba 100644 --- a/integration/dynamic-compiler/yarn.lock +++ b/integration/dynamic-compiler/yarn.lock @@ -3777,4 +3777,4 @@ yeast@0.1.2: integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/hello_world__closure/yarn.lock b/integration/hello_world__closure/yarn.lock index ce10897b7f593..adb6bfa7c7174 100644 --- a/integration/hello_world__closure/yarn.lock +++ b/integration/hello_world__closure/yarn.lock @@ -4501,4 +4501,4 @@ yeast@0.1.2: integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/hello_world__systemjs_umd/yarn.lock b/integration/hello_world__systemjs_umd/yarn.lock index eaa4596ddaf74..b2cb772e30c0e 100644 --- a/integration/hello_world__systemjs_umd/yarn.lock +++ b/integration/hello_world__systemjs_umd/yarn.lock @@ -2533,4 +2533,4 @@ yeast@0.1.2: resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/i18n/yarn.lock b/integration/i18n/yarn.lock index 83ab2e0abcd93..5bd6915145f9b 100644 --- a/integration/i18n/yarn.lock +++ b/integration/i18n/yarn.lock @@ -3734,4 +3734,4 @@ yeast@0.1.2: integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/injectable-def/yarn.lock b/integration/injectable-def/yarn.lock index 560d8e352912d..6226f0a31ca57 100644 --- a/integration/injectable-def/yarn.lock +++ b/integration/injectable-def/yarn.lock @@ -2860,4 +2860,4 @@ yeast@0.1.2: resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/ivy-i18n/yarn.lock b/integration/ivy-i18n/yarn.lock index 3e1ccb8077246..db8afee3a5800 100644 --- a/integration/ivy-i18n/yarn.lock +++ b/integration/ivy-i18n/yarn.lock @@ -9393,4 +9393,4 @@ yn@^3.0.0: integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/ng_elements/yarn.lock b/integration/ng_elements/yarn.lock index 56d748992c530..fa26bd58643ac 100644 --- a/integration/ng_elements/yarn.lock +++ b/integration/ng_elements/yarn.lock @@ -3527,4 +3527,4 @@ yeast@0.1.2: integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/ng_update/yarn.lock b/integration/ng_update/yarn.lock index 5fc4704d53453..5fc241b94f720 100644 --- a/integration/ng_update/yarn.lock +++ b/integration/ng_update/yarn.lock @@ -640,4 +640,4 @@ yargs@13.1.0: yargs-parser "^13.0.0" "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/ng_update_migrations/src/app/migration-tests/undecorated-classes-with-fields.ts b/integration/ng_update_migrations/src/app/migration-tests/undecorated-classes-with-fields.ts new file mode 100644 index 0000000000000..c3ea7d460e25c --- /dev/null +++ b/integration/ng_update_migrations/src/app/migration-tests/undecorated-classes-with-fields.ts @@ -0,0 +1,62 @@ +import { + Component, + Directive, + ElementRef, + HostBinding, + HostListener, + Input, + NgModule +} from '@angular/core'; + +export class NonAngularBaseClass { + greet() {} +} + +export class BaseClass extends NonAngularBaseClass { + @Input() enabled = true; +} + +export class SecondBaseClass extends BaseClass { + toggleEnabled() { + this.enabled = !this.enabled; + } +} + +export class ThirdBaseClass extends SecondBaseClass { + @HostListener('focus') onFocus() {} +} + +export class FourthBaseClass extends ThirdBaseClass { + focus() { + this.onFocus(); + } +} + +export class FifthBaseClass { + constructor(private _elementRef: ElementRef) {} + protected calculatePosition(): any {} +} + +export class MyCompSuperBase { + @HostBinding('class.hello') hello = true; +} + +export class MyCompBase extends MyCompSuperBase {} + +@Component({ + selector: 'my-comp', + template: '', +}) +export class MyComp extends MyCompBase {} + +export class WrappedMyComp extends MyComp {} + +@NgModule({declarations: [MyComp, WrappedMyComp]}) +export class TestModule {} + +@Directive({selector: null}) +export class AbstractDir {} + +export class DerivedAbstractDir extends AbstractDir {} + +export class WrappedDerivedAbstractDir extends DerivedAbstractDir {} diff --git a/integration/ng_update_migrations/src/app/migration-tests/undecorated-classes-with-fields_expected.ts b/integration/ng_update_migrations/src/app/migration-tests/undecorated-classes-with-fields_expected.ts new file mode 100644 index 0000000000000..ebc267f02adc7 --- /dev/null +++ b/integration/ng_update_migrations/src/app/migration-tests/undecorated-classes-with-fields_expected.ts @@ -0,0 +1,74 @@ +import { + Component, + Directive, + ElementRef, + HostBinding, + HostListener, + Input, + NgModule +} from '@angular/core'; + +export class NonAngularBaseClass { + greet() {} +} + +@Directive() +export class BaseClass extends NonAngularBaseClass { + @Input() enabled = true; +} + +@Directive() +export class SecondBaseClass extends BaseClass { + toggleEnabled() { + this.enabled = !this.enabled; + } +} + +@Directive() +export class ThirdBaseClass extends SecondBaseClass { + @HostListener('focus') onFocus() {} +} + +@Directive() +export class FourthBaseClass extends ThirdBaseClass { + focus() { + this.onFocus(); + } +} + +export class FifthBaseClass { + constructor(private _elementRef: ElementRef) {} + protected calculatePosition(): any {} +} + +@Directive() +export class MyCompSuperBase { + @HostBinding('class.hello') hello = true; +} + +@Directive() +export class MyCompBase extends MyCompSuperBase {} + +@Component({ + selector: 'my-comp', + template: '', +}) +export class MyComp extends MyCompBase {} + +@Component({ + selector: 'my-comp', + template: '', +}) +export class WrappedMyComp extends MyComp {} + +@NgModule({declarations: [MyComp, WrappedMyComp]}) +export class TestModule {} + +@Directive({selector: null}) +export class AbstractDir {} + +@Directive() +export class DerivedAbstractDir extends AbstractDir {} + +@Directive() +export class WrappedDerivedAbstractDir extends DerivedAbstractDir {} diff --git a/integration/ng_update_migrations/yarn.lock b/integration/ng_update_migrations/yarn.lock index 6ef1f8a8d162f..74b2fe5c60e63 100644 --- a/integration/ng_update_migrations/yarn.lock +++ b/integration/ng_update_migrations/yarn.lock @@ -2,25 +2,25 @@ # yarn lockfile v1 -"@angular-devkit/architect@0.900.0-rc.11": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.900.0-rc.11.tgz#e9f3e5e372d467a220027cf53231b88e8e857fbc" - integrity sha512-rRbq4ipppnY4FvVo89Cv+yC7rlt1/VFE/jaB77Ra2tI6zVlFWCTjnMzuc9TYz/3jK1ssThzgEA2sebPDmjH47w== +"@angular-devkit/architect@0.900.0-rc.14": + version "0.900.0-rc.14" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.900.0-rc.14.tgz#cfdbee7899b9addfd9d43c638b0dbc21188f0a79" + integrity sha512-dgEc/zYE0uzv+m8JTdv3FDc7bViJKbxs8FY3zdkArc6MXIXpoMzgvveEEHvhrpO0iu6njW/xRSZYtYnTIY4xlw== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.14" rxjs "6.5.3" "@angular-devkit/build-angular@file:../../node_modules/@angular-devkit/build-angular": - version "0.900.0-rc.11" + version "0.900.0-rc.14" dependencies: - "@angular-devkit/architect" "0.900.0-rc.11" - "@angular-devkit/build-optimizer" "0.900.0-rc.11" - "@angular-devkit/build-webpack" "0.900.0-rc.11" - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/architect" "0.900.0-rc.14" + "@angular-devkit/build-optimizer" "0.900.0-rc.14" + "@angular-devkit/build-webpack" "0.900.0-rc.14" + "@angular-devkit/core" "9.0.0-rc.14" "@babel/core" "7.7.7" "@babel/generator" "7.7.7" "@babel/preset-env" "7.7.7" - "@ngtools/webpack" "9.0.0-rc.11" + "@ngtools/webpack" "9.0.0-rc.14" ajv "6.10.2" autoprefixer "9.7.1" babel-loader "8.0.6" @@ -75,10 +75,10 @@ webpack-subresource-integrity "1.3.4" worker-plugin "3.2.0" -"@angular-devkit/build-optimizer@0.900.0-rc.11": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.900.0-rc.11.tgz#96c2446fa9cd2e90700ab8a68312b28b3907f6d9" - integrity sha512-GJC+7H7ER6bxDC2UdAGwW357EYHpv8ISKKmS19wdJV5gZPMPANcpbg9FIpl27SDhUyZX9C2DOrcATvYYFoYgDQ== +"@angular-devkit/build-optimizer@0.900.0-rc.14": + version "0.900.0-rc.14" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.900.0-rc.14.tgz#e90e955e1daf5689ad198a5253134187c99b7b5a" + integrity sha512-MA2g8N9/cvzMvudEEjeaNV6STwSr8NI/znpv+nU6sQa4PdegIotBbqxGUmHMKtLH5cOwDy9hI47ANN+XADbIbQ== dependencies: loader-utils "1.2.3" source-map "0.7.3" @@ -86,19 +86,19 @@ typescript "3.6.4" webpack-sources "1.4.3" -"@angular-devkit/build-webpack@0.900.0-rc.11": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.900.0-rc.11.tgz#d9a91c2b67a629f6adfe87980d26e495f2e30e0a" - integrity sha512-utBAnkO6WLi323Rto1s7TJpaDRqDNR8jkD0C0PG5Zm3y1U9ARbAjTkugkrB/7bc4gEIqWZD+1dLYaaJCidye2Q== +"@angular-devkit/build-webpack@0.900.0-rc.14": + version "0.900.0-rc.14" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.900.0-rc.14.tgz#b932148f14ed0056f1885c98c330411a80d31331" + integrity sha512-HvrDapIKr4k2a7Tf7MdW0miGOUUzHxRkGyDIWJBDXLbfOIt9BnhKfFGxP/SyDlwJnLyat9yYZjcDu1AI0E1Dmw== dependencies: - "@angular-devkit/architect" "0.900.0-rc.11" - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/architect" "0.900.0-rc.14" + "@angular-devkit/core" "9.0.0-rc.14" rxjs "6.5.3" -"@angular-devkit/core@9.0.0-rc.11": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.0.0-rc.11.tgz#9e69545eb21284a573ad78e4c33003f2ea25afd5" - integrity sha512-ki7Sln+mQdCctJNBalzy70tiFn2hOCY2Yyte8B0xKWVHnofZySvG+ANzoLgodnKFOBH18AQy35FhgzZM++N9tQ== +"@angular-devkit/core@9.0.0-rc.14": + version "9.0.0-rc.14" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.0.0-rc.14.tgz#5cff058750c0b063a6f5fa1a2e830f1f48ffa56b" + integrity sha512-hFiKoAPtnOqqYv97Y22OgyeTFxjNU/6WDhmuXkfbZDKychvuBLDOdgUhL43heEzavSfCCl23E0JmilwCUcepmw== dependencies: ajv "6.10.2" fast-json-stable-stringify "2.0.0" @@ -106,12 +106,12 @@ rxjs "6.5.3" source-map "0.7.3" -"@angular-devkit/schematics@9.0.0-rc.11": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.0.0-rc.11.tgz#e0d4d271d8d783ebf05eced576262f20e6c3562c" - integrity sha512-aJqOLzsoAkVj3AVTf1ehH2hA9wHHz1+7TTtfqI+Yx+S3jFyvGmnKrNBCKtMuIV5JdEHiXmhhuGbNBHwRFWpOow== +"@angular-devkit/schematics@9.0.0-rc.14": + version "9.0.0-rc.14" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.0.0-rc.14.tgz#4605b1ee9f18b772094697f9b35258fdac76d4b3" + integrity sha512-i/XTxo7hTXTFKus51Qry3rRGHL2uS4SgufGqdvOcy2qZbjyh/wt0UDOGJu/w8dcA9pJm+vFBLAbnro5oFyIsVw== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.14" ora "4.0.2" rxjs "6.5.3" @@ -119,13 +119,13 @@ version "9.0.0-rc.1" "@angular/cli@file:../../node_modules/@angular/cli": - version "9.0.0-rc.11" + version "9.0.0-rc.14" dependencies: - "@angular-devkit/architect" "0.900.0-rc.11" - "@angular-devkit/core" "9.0.0-rc.11" - "@angular-devkit/schematics" "9.0.0-rc.11" - "@schematics/angular" "9.0.0-rc.11" - "@schematics/update" "0.900.0-rc.11" + "@angular-devkit/architect" "0.900.0-rc.14" + "@angular-devkit/core" "9.0.0-rc.14" + "@angular-devkit/schematics" "9.0.0-rc.14" + "@schematics/angular" "9.0.0-rc.14" + "@schematics/update" "0.900.0-rc.14" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" debug "^4.1.1" @@ -216,16 +216,16 @@ source-map "^0.5.0" "@babel/core@^7.7.5": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.3.tgz#30b0ebb4dd1585de6923a0b4d179e0b9f5d82941" - integrity sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA== + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e" + integrity sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.3" - "@babel/helpers" "^7.8.3" - "@babel/parser" "^7.8.3" + "@babel/generator" "^7.8.4" + "@babel/helpers" "^7.8.4" + "@babel/parser" "^7.8.4" "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" + "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" convert-source-map "^1.7.0" debug "^4.1.0" @@ -256,10 +256,10 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.7.7", "@babel/generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.3.tgz#0e22c005b0a94c1c74eafe19ef78ce53a4d45c03" - integrity sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug== +"@babel/generator@^7.7.7", "@babel/generator@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e" + integrity sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA== dependencies: "@babel/types" "^7.8.3" jsesc "^2.5.1" @@ -464,13 +464,13 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helpers@^7.7.4", "@babel/helpers@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.3.tgz#382fbb0382ce7c4ce905945ab9641d688336ce85" - integrity sha512-LmU3q9Pah/XyZU89QvBgGt+BCsTPoQa+73RxAQh8fb8qkDyIfeQnmgs+hvzhTCKTzqOyk7JTkS3MS1S8Mq5yrQ== +"@babel/helpers@^7.7.4", "@babel/helpers@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" + integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w== dependencies: "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" + "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" "@babel/highlight@^7.0.0": @@ -496,10 +496,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.4.tgz#75ab2d7110c2cf2fa949959afb05fa346d2231bb" integrity sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g== -"@babel/parser@^7.7.5", "@babel/parser@^7.7.7", "@babel/parser@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" - integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== +"@babel/parser@^7.7.5", "@babel/parser@^7.7.7", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" + integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw== "@babel/plugin-proposal-async-generator-functions@^7.7.4": version "7.8.3" @@ -689,9 +689,9 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-for-of@^7.7.4": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.3.tgz#15f17bce2fc95c7d59a24b299e83e81cedc22e18" - integrity sha512-ZjXznLNTxhpf4Q5q3x1NsngzGA38t9naWH8Gt+0qYZEJAcvPI9waSStSh56u19Ofjr7QmD0wUsQ8hw8s/p1VnA== + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.4.tgz#6fe8eae5d6875086ee185dd0b098a8513783b47d" + integrity sha512-iAXNlOWvcYUYoV8YIxwS7TxGRJcxyl8eQCfT+A5j8sKUzRFvJdcyjp97jL2IghWSRDaL2PU2O2tX8Cu9dTBq5A== dependencies: "@babel/helper-plugin-utils" "^7.8.3" @@ -777,9 +777,9 @@ "@babel/helper-replace-supers" "^7.8.3" "@babel/plugin-transform-parameters@^7.7.7": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.3.tgz#7890576a13b17325d8b7d44cb37f21dc3bbdda59" - integrity sha512-/pqngtGb54JwMBZ6S/D3XYylQDFtGjWrnoCF4gXZOUpFV/ujbxnoNGNvDGu6doFWRPBveE72qTx/RRU44j5I/Q== + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz#1d5155de0b65db0ccf9971165745d3bb990d77d3" + integrity sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA== dependencies: "@babel/helper-call-delegate" "^7.8.3" "@babel/helper-get-function-arity" "^7.8.3" @@ -837,9 +837,9 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-typeof-symbol@^7.7.4": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.3.tgz#5cffb216fb25c8c64ba6bf5f76ce49d3ab079f4d" - integrity sha512-3TrkKd4LPqm4jHs6nPtSDI/SV9Cm5PRJkHLUgTcqRQQTMAZ44ZaAdDZJtvWFSaRcvT0a1rTmJ5ZA5tDKjleF3g== + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" + integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== dependencies: "@babel/helper-plugin-utils" "^7.8.3" @@ -941,16 +941,16 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.3.tgz#a826215b011c9b4f73f3a893afbc05151358bf9a" - integrity sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg== +"@babel/traverse@^7.8.3", "@babel/traverse@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" + integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.3" + "@babel/generator" "^7.8.4" "@babel/helper-function-name" "^7.8.3" "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.8.3" + "@babel/parser" "^7.8.4" "@babel/types" "^7.8.3" debug "^4.1.0" globals "^11.1.0" @@ -979,31 +979,31 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@ngtools/webpack@9.0.0-rc.11": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-9.0.0-rc.11.tgz#10b5997bec7cf48d1b144c8b4d46ffd0039c522a" - integrity sha512-qeW81ISiO8GVEndOaCYv0k6fzRIxzZs6jrXGl1pcLH1H6qv2mxhA5DA0vC/9TN6wenrS43RGjDIQpp+RvkiLwA== +"@ngtools/webpack@9.0.0-rc.14": + version "9.0.0-rc.14" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-9.0.0-rc.14.tgz#6a764138347d2ff3846753a141eaacdab3b0658c" + integrity sha512-G98u14exE36kR4LflWB20ClJ6ql9B0qMcyokepjwpVAgaoDfpJRR7mKAZliMPd0KFXktdnct2QEyF6pbLevO7w== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.14" enhanced-resolve "4.1.1" rxjs "6.5.3" webpack-sources "1.4.3" -"@schematics/angular@9.0.0-rc.11": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.0.0-rc.11.tgz#d544c0d4e7b3dd59ed56be5183e038ebe06a165e" - integrity sha512-9InC+F71KiPXE0jl7Ow4iPFJ2AZZDbfTM6yWZoYLk3hzTCohAZZciBl00Tfyu2uerGshx8akbJMLySjXtf+q0g== +"@schematics/angular@9.0.0-rc.14": + version "9.0.0-rc.14" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.0.0-rc.14.tgz#4112aeb6c76144893d65cab9f53ca8422adc3bbb" + integrity sha512-NWxDLym2Sst5lTvV7QYkVUjy3N0CfCohhdWC4DGRtjUyTD2eUepAg1PZCQcq8U2iRtd78Lm1n4obDWO1tW3pXQ== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" - "@angular-devkit/schematics" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.14" + "@angular-devkit/schematics" "9.0.0-rc.14" -"@schematics/update@0.900.0-rc.11": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.900.0-rc.11.tgz#d22df30f13a6f38970b759db61ad84d3f9b03a78" - integrity sha512-nV0oCPzzd0vi2Exo1910rWXwz/RnMc4zF9FxSOCZzsIv+AkwIehhL815OKyjUSCzU9+IM0/o1LKkPPrSWK7QEA== +"@schematics/update@0.900.0-rc.14": + version "0.900.0-rc.14" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.900.0-rc.14.tgz#1debea3eb90559d25838e7b256b48bac216e19d9" + integrity sha512-ZlsneHwpvrtpt0D10g4S8JftLaSFQtSO+kOD1uP26OxNMg9w54jrlr7xWSwAmT69/ETjNy8BFKXdcU9yvYixPA== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" - "@angular-devkit/schematics" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.14" + "@angular-devkit/schematics" "9.0.0-rc.14" "@yarnpkg/lockfile" "1.1.0" ini "1.3.5" npm-package-arg "^7.0.0" @@ -2029,9 +2029,9 @@ caniuse-lite@1.0.30001020: integrity sha512-yWIvwA68wRHKanAVS1GjN8vajAv7MBFshullKCeq/eKpK7pJBVDgFFEqvgWTkcP2+wIDeQGYFRXECjKZnLkUjA== caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001017, caniuse-lite@^1.0.30001023: - version "1.0.30001023" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz#b82155827f3f5009077bdd2df3d8968bcbcc6fc4" - integrity sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA== + version "1.0.30001027" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz#283e2ef17d94889cc216a22c6f85303d78ca852d" + integrity sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg== caniuse-lite@^1.0.30000984: version "1.0.30000989" @@ -3037,9 +3037,9 @@ electron-to-chromium@^1.3.306: integrity sha512-7GH6RKCzziLzJ9ejmbiBEdzHZsc6C3eRpav14dmRfTWMpNgMqpP1ukw/FU/Le2fR+ep642naq7a23xNdmh2s+A== electron-to-chromium@^1.3.322, electron-to-chromium@^1.3.341: - version "1.3.342" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.342.tgz#138317aa7399e268735b9269fe374a8566425090" - integrity sha512-An/MLhGLIG/g7lZ5vqs4lar96zv74agd3ZcADDHLpjAa16T7Y/pO/33Q31JOwpmHeyjithtHtUcn7XLuaz78lw== + version "1.3.348" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.348.tgz#61fa7d43f6c4fd8b046b0b69213cb55b7a4c76a5" + integrity sha512-6O0IInybavGdYtcbI4ryF/9e3Qi8/soi6C68ELRseJuTwQPKq39uGgVVeQHG28t69Sgsky09nXBRhUiFXsZyFQ== elliptic@^6.0.0: version "6.5.0" @@ -4626,9 +4626,9 @@ istanbul-lib-coverage@^3.0.0: integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== istanbul-lib-instrument@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.0.tgz#53321a7970f076262fd3292c8f9b2e4ac544aae1" - integrity sha512-Nm4wVHdo7ZXSG30KjZ2Wl5SU/Bw7bDx1PdaiIFzEStdjs0H12mOTncn1GVYuqQSaZxpg87VGBRsVRPGD2cD1AQ== + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" + integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== dependencies: "@babel/core" "^7.7.5" "@babel/parser" "^7.7.5" @@ -5498,9 +5498,9 @@ node-releases@^1.1.40: semver "^6.3.0" node-releases@^1.1.44, node-releases@^1.1.47: - version "1.1.47" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.47.tgz#c59ef739a1fd7ecbd9f0b7cf5b7871e8a8b591e4" - integrity sha512-k4xjVPx5FpwBUj0Gw7uvFOTF4Ep8Hok1I6qjwL3pLfwe7Y0REQSAqOwwv9TWBCUtMHxcXfY4PgRLRozcChvTcA== + version "1.1.48" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.48.tgz#7f647f0c453a0495bcd64cbd4778c26035c2f03a" + integrity sha512-Hr8BbmUl1ujAST0K0snItzEA5zkJTQup8VNTKNfT6Zw8vTJkIiagUPNfxHmgDOyfFYNfKAul40sD0UEYTvwebw== dependencies: semver "^6.3.0" @@ -8662,4 +8662,4 @@ yeast@0.1.2: integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/ngcc/test.sh b/integration/ngcc/test.sh index d4729a2dc31d8..b750c55aaa598 100755 --- a/integration/ngcc/test.sh +++ b/integration/ngcc/test.sh @@ -173,8 +173,7 @@ assertSucceeded "Expected 'ngcc' to log 'Compiling'." assertEquals 1 `cat node_modules/@angular/material/button/button.d.ts | grep 'import \* as ɵngcc0' | wc -l` # Re-compile packages (which requires cleaning up those compiled by a different ngcc version). - # (Use sync mode to ensure all tasks share the same `CachedFileSystem` instance.) - ngcc --no-async --properties main + ngcc --properties main assertSucceeded "Expected 'ngcc' to successfully re-compile the packages." # Ensure previously compiled packages were correctly cleaned up (i.e. no multiple diff --git a/integration/ngcc/yarn.lock b/integration/ngcc/yarn.lock index 2e1b49e3671e3..bb53f3ac796c6 100644 --- a/integration/ngcc/yarn.lock +++ b/integration/ngcc/yarn.lock @@ -3677,4 +3677,4 @@ yeast@0.1.2: integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/platform-server/yarn.lock b/integration/platform-server/yarn.lock index e57c215266865..134fd16666705 100644 --- a/integration/platform-server/yarn.lock +++ b/integration/platform-server/yarn.lock @@ -4590,4 +4590,4 @@ yauzl@2.4.1: fd-slicer "~1.0.1" "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/service-worker-schema/yarn.lock b/integration/service-worker-schema/yarn.lock index bd15cb087cf8b..e6488235d8636 100644 --- a/integration/service-worker-schema/yarn.lock +++ b/integration/service-worker-schema/yarn.lock @@ -22,4 +22,4 @@ tslib@^1.9.0: integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/terser/yarn.lock b/integration/terser/yarn.lock index 078eecb5ee0d2..6125bf4471dec 100644 --- a/integration/terser/yarn.lock +++ b/integration/terser/yarn.lock @@ -629,4 +629,4 @@ yargs@13.1.0: yargs-parser "^13.0.0" "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/typings_test_ts36/yarn.lock b/integration/typings_test_ts36/yarn.lock index 6bf9eca15c64e..5d1b417ce890c 100644 --- a/integration/typings_test_ts36/yarn.lock +++ b/integration/typings_test_ts36/yarn.lock @@ -659,4 +659,4 @@ yargs@13.1.0: yargs-parser "^13.0.0" "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/typings_test_ts37/yarn.lock b/integration/typings_test_ts37/yarn.lock index 447a2a2ef8128..22c5a59ec65dd 100644 --- a/integration/typings_test_ts37/yarn.lock +++ b/integration/typings_test_ts37/yarn.lock @@ -659,4 +659,4 @@ yargs@13.1.0: yargs-parser "^13.0.0" "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/integration/typings_test_ts38/yarn.lock b/integration/typings_test_ts38/yarn.lock index 0105c7d5f5261..6c3fd3dfdbdc3 100644 --- a/integration/typings_test_ts38/yarn.lock +++ b/integration/typings_test_ts38/yarn.lock @@ -660,4 +660,4 @@ yargs@13.1.0: yargs-parser "^13.0.0" "zone.js@file:../../dist/zone.js-dist/zone.js": - version "0.10.2" + version "0.10.3" diff --git a/modules/benchmarks/src/bootstrap_ng2.ts b/modules/benchmarks/src/bootstrap_ng2.ts index ff8eb4d105ff6..5bd0a5c803424 100644 --- a/modules/benchmarks/src/bootstrap_ng2.ts +++ b/modules/benchmarks/src/bootstrap_ng2.ts @@ -8,77 +8,79 @@ (function(global: any) { - writeScriptTag('/all/benchmarks/vendor/core.js'); - writeScriptTag('/all/benchmarks/vendor/zone.js'); - writeScriptTag('/all/benchmarks/vendor/long-stack-trace-zone.js'); - writeScriptTag('/all/benchmarks/vendor/system.src.js'); - writeScriptTag('/all/benchmarks/vendor/Reflect.js', 'benchmarksBootstrap()'); +writeScriptTag('/all/benchmarks/vendor/core.js'); +writeScriptTag('/all/benchmarks/vendor/zone.js'); +writeScriptTag('/all/benchmarks/vendor/long-stack-trace-zone.js'); +writeScriptTag('/all/benchmarks/vendor/system.src.js'); +writeScriptTag('/all/benchmarks/vendor/Reflect.js', 'benchmarksBootstrap()'); - (<any>global).benchmarksBootstrap = benchmarksBootstrap; +(<any>global).benchmarksBootstrap = benchmarksBootstrap; - function benchmarksBootstrap() { - // check query param - const useBundles = location.search.indexOf('bundles=false') == -1; - if (useBundles) { - System.config({ - defaultJSExtensions: true, - map: { - '@angular/core': '/packages-dist/core/bundles/core.umd.js', - '@angular/animations': '/packages-dist/common/bundles/animations.umd.js', - '@angular/platform-browser/animations': - '/packages-dist/platform-browser/bundles/platform-browser-animations.umd.js', - '@angular/common': '/packages-dist/common/bundles/common.umd.js', - '@angular/forms': '/packages-dist/forms/bundles/forms.umd.js', - '@angular/compiler': '/packages-dist/compiler/bundles/compiler.umd.js', - '@angular/platform-browser': - '/packages-dist/platform-browser/bundles/platform-browser.umd.js', - '@angular/platform-browser-dynamic': - '/packages-dist/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', - '@angular/http': '/packages-dist/http/bundles/http.umd.js', - '@angular/upgrade': '/packages-dist/upgrade/bundles/upgrade.umd.js', - '@angular/router': '/packages-dist/router/bundles/router.umd.js', - 'rxjs': '/all/benchmarks/vendor/rxjs', - }, - packages: { - 'rxjs/ajax': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs/operators': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs/testing': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs/websocket': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs': {main: 'index.js', defaultExtension: 'js'}, - } - }); - } else { - console.warn( - 'Not using the Angular bundles. Don\'t use this configuration for e2e/performance tests!'); +function benchmarksBootstrap() { + // check query param + const useBundles = location.search.indexOf('bundles=false') == -1; + if (useBundles) { + System.config({ + defaultJSExtensions: true, + map: { + '@angular/core': '/packages-dist/core/bundles/core.umd.js', + '@angular/animations': '/packages-dist/common/bundles/animations.umd.js', + '@angular/platform-browser/animations': + '/packages-dist/platform-browser/bundles/platform-browser-animations.umd.js', + '@angular/common': '/packages-dist/common/bundles/common.umd.js', + '@angular/forms': '/packages-dist/forms/bundles/forms.umd.js', + '@angular/compiler': '/packages-dist/compiler/bundles/compiler.umd.js', + '@angular/platform-browser': + '/packages-dist/platform-browser/bundles/platform-browser.umd.js', + '@angular/platform-browser-dynamic': + '/packages-dist/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', + '@angular/http': '/packages-dist/http/bundles/http.umd.js', + '@angular/upgrade': '/packages-dist/upgrade/bundles/upgrade.umd.js', + '@angular/router': '/packages-dist/router/bundles/router.umd.js', + 'rxjs': '/all/benchmarks/vendor/rxjs', + }, + packages: { + 'rxjs/ajax': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs/operators': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs/testing': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs/websocket': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs': {main: 'index.js', defaultExtension: 'js'}, + } + }); + } else { + console.warn( + 'Not using the Angular bundles. Don\'t use this configuration for e2e/performance tests!'); - System.config({ - defaultJSExtensions: true, - map: {'@angular': '/all/@angular', 'rxjs': '/all/benchmarks/vendor/rxjs'}, - packages: { - '@angular/core': {main: 'index.js', defaultExtension: 'js'}, - '@angular/animations': {main: 'index.js', defaultExtension: 'js'}, - '@angular/platform-browser/animations': {main: 'index.js', defaultExtension: 'js'}, - '@angular/compiler': {main: 'index.js', defaultExtension: 'js'}, - '@angular/router': {main: 'index.js', defaultExtension: 'js'}, - '@angular/common': {main: 'index.js', defaultExtension: 'js'}, - '@angular/forms': {main: 'index.js', defaultExtension: 'js'}, - '@angular/platform-browser': {main: 'index.js', defaultExtension: 'js'}, - '@angular/platform-browser-dynamic': {main: 'index.js', defaultExtension: 'js'}, - '@angular/upgrade': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs/ajax': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs/operators': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs/testing': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs/websocket': {main: 'index.js', defaultExtension: 'js'}, - 'rxjs': {main: 'index.js', defaultExtension: 'js'}, - } - }); - } - - // BOOTSTRAP the app! - System.import('index').then(function(m: any) { m.main(); }, console.error.bind(console)); + System.config({ + defaultJSExtensions: true, + map: {'@angular': '/all/@angular', 'rxjs': '/all/benchmarks/vendor/rxjs'}, + packages: { + '@angular/core': {main: 'index.js', defaultExtension: 'js'}, + '@angular/animations': {main: 'index.js', defaultExtension: 'js'}, + '@angular/platform-browser/animations': {main: 'index.js', defaultExtension: 'js'}, + '@angular/compiler': {main: 'index.js', defaultExtension: 'js'}, + '@angular/router': {main: 'index.js', defaultExtension: 'js'}, + '@angular/common': {main: 'index.js', defaultExtension: 'js'}, + '@angular/forms': {main: 'index.js', defaultExtension: 'js'}, + '@angular/platform-browser': {main: 'index.js', defaultExtension: 'js'}, + '@angular/platform-browser-dynamic': {main: 'index.js', defaultExtension: 'js'}, + '@angular/upgrade': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs/ajax': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs/operators': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs/testing': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs/websocket': {main: 'index.js', defaultExtension: 'js'}, + 'rxjs': {main: 'index.js', defaultExtension: 'js'}, + } + }); } - function writeScriptTag(scriptUrl: string, onload?: string) { - document.write(`<script src="${scriptUrl}" onload="${onload}"></script>`); - } + // BOOTSTRAP the app! + System.import('index').then(function(m: any) { + m.main(); + }, console.error.bind(console)); +} + +function writeScriptTag(scriptUrl: string, onload?: string) { + document.write(`<script src="${scriptUrl}" onload="${onload}"></script>`); +} }(window)); diff --git a/modules/benchmarks/src/bootstrap_plain.ts b/modules/benchmarks/src/bootstrap_plain.ts index b2c62c53ae679..dcc711f93273c 100644 --- a/modules/benchmarks/src/bootstrap_plain.ts +++ b/modules/benchmarks/src/bootstrap_plain.ts @@ -8,24 +8,24 @@ (function(global: any) { - writeScriptTag('/all/benchmarks/vendor/core.js'); - writeScriptTag('/all/benchmarks/vendor/system.src.js', 'benchmarksBootstrap()'); +writeScriptTag('/all/benchmarks/vendor/core.js'); +writeScriptTag('/all/benchmarks/vendor/system.src.js', 'benchmarksBootstrap()'); - (<any>global).benchmarksBootstrap = benchmarksBootstrap; +(<any>global).benchmarksBootstrap = benchmarksBootstrap; - function benchmarksBootstrap() { - System.config({ - defaultJSExtensions: true, - map: {'incremental-dom': '/all/benchmarks/vendor/incremental-dom-cjs.js'} - }); +function benchmarksBootstrap() { + System.config({ + defaultJSExtensions: true, + map: {'incremental-dom': '/all/benchmarks/vendor/incremental-dom-cjs.js'} + }); - // BOOTSTRAP the app! - System.import('index').then(function(m: any) { - m.main && m.main(); - }, console.error.bind(console)); - } + // BOOTSTRAP the app! + System.import('index').then(function(m: any) { + m.main && m.main(); + }, console.error.bind(console)); +} - function writeScriptTag(scriptUrl: string, onload?: string) { - document.write(`<script src="${scriptUrl}" onload="${onload}"></script>`); - } +function writeScriptTag(scriptUrl: string, onload?: string) { + document.write(`<script src="${scriptUrl}" onload="${onload}"></script>`); +} }(window)); diff --git a/modules/benchmarks/src/change_detection/change_detection.e2e-spec.ts b/modules/benchmarks/src/change_detection/change_detection.e2e-spec.ts index 4872bfee5352a..33ba44bce599f 100644 --- a/modules/benchmarks/src/change_detection/change_detection.e2e-spec.ts +++ b/modules/benchmarks/src/change_detection/change_detection.e2e-spec.ts @@ -13,7 +13,7 @@ import {openBrowser, verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('change detection benchmark', () => { afterEach(verifyNoBrowserErrors); - it(`should render and update`, async() => { + it(`should render and update`, async () => { openBrowser({ url: '', ignoreBrowserSynchronization: true, diff --git a/modules/benchmarks/src/change_detection/change_detection.perf-spec.ts b/modules/benchmarks/src/change_detection/change_detection.perf-spec.ts index 6251659446592..f450dc5bc8e1c 100644 --- a/modules/benchmarks/src/change_detection/change_detection.perf-spec.ts +++ b/modules/benchmarks/src/change_detection/change_detection.perf-spec.ts @@ -31,15 +31,14 @@ const UpdateWorker: Worker = { // name. We determine the name of the Bazel package where this test runs from the current test // target. The Bazel target // looks like: "//modules/benchmarks/src/change_detection/{pkg_name}:{target_name}". -const testPackageName = process.env['BAZEL_TARGET'] !.split(':')[0].split('/').pop(); +const testPackageName = process.env['BAZEL_TARGET']!.split(':')[0].split('/').pop(); describe('change detection benchmark perf', () => { - afterEach(verifyNoBrowserErrors); [UpdateWorker].forEach((worker) => { describe(worker.id, () => { - it(`should run benchmark for ${testPackageName}`, async() => { + it(`should run benchmark for ${testPackageName}`, async () => { await runChangeDetectionBenchmark({ id: `change_detection.${testPackageName}.${worker.id}`, url: '/', diff --git a/modules/benchmarks/src/change_detection/transplanted_views/init.ts b/modules/benchmarks/src/change_detection/transplanted_views/init.ts index 600d862438194..53b6ef336a004 100644 --- a/modules/benchmarks/src/change_detection/transplanted_views/init.ts +++ b/modules/benchmarks/src/change_detection/transplanted_views/init.ts @@ -35,7 +35,9 @@ export function init(moduleRef: NgModuleRef<TransplantedViewsModule>) { appRef.tick(); } - function detectChanges() { appRef.tick(); } + function detectChanges() { + appRef.tick(); + } function noop() {} } diff --git a/modules/benchmarks/src/change_detection/transplanted_views/transplanted_views.ts b/modules/benchmarks/src/change_detection/transplanted_views/transplanted_views.ts index b74e47b8c7089..da7746640c267 100644 --- a/modules/benchmarks/src/change_detection/transplanted_views/transplanted_views.ts +++ b/modules/benchmarks/src/change_detection/transplanted_views/transplanted_views.ts @@ -40,10 +40,14 @@ export class InsertionComponent { @Input() template !: TemplateRef<{}>; views: any[] = []; @Input() - set viewCount(n: number) { this.views = n > 0 ? newArray<any>(n) : []; } + set viewCount(n: number) { + this.views = n > 0 ? newArray<any>(n) : []; + } // use trackBy to ensure profile isn't affected by the cost to refresh ngFor. - trackByIndex(index: number, item: any) { return index; } + trackByIndex(index: number, item: any) { + return index; + } } @NgModule({ diff --git a/modules/benchmarks/src/change_detection/util.ts b/modules/benchmarks/src/change_detection/util.ts index 7205f7245479d..25944f727c16c 100644 --- a/modules/benchmarks/src/change_detection/util.ts +++ b/modules/benchmarks/src/change_detection/util.ts @@ -15,7 +15,7 @@ export function newArray<T>(size: number, value: T): T[]; export function newArray<T>(size: number, value?: T): T[] { const list: T[] = []; for (let i = 0; i < size; i++) { - list.push(value !); + list.push(value!); } return list; } diff --git a/modules/benchmarks/src/class_bindings/app.component.ts b/modules/benchmarks/src/class_bindings/app.component.ts index 73ffcaae3b077..27aa5e20f9521 100644 --- a/modules/benchmarks/src/class_bindings/app.component.ts +++ b/modules/benchmarks/src/class_bindings/app.component.ts @@ -27,12 +27,16 @@ export class AppComponent { } } - create() { this.show = true; } + create() { + this.show = true; + } update() { this.msg = this.msg === 'hello' ? 'bye' : 'hello'; this.list[0].text = this.msg; } - destroy() { this.show = false; } + destroy() { + this.show = false; + } } diff --git a/modules/benchmarks/src/class_bindings/class_bindings.perf-spec.ts b/modules/benchmarks/src/class_bindings/class_bindings.perf-spec.ts index 41486c5ce64ac..e21d27dadf2a8 100644 --- a/modules/benchmarks/src/class_bindings/class_bindings.perf-spec.ts +++ b/modules/benchmarks/src/class_bindings/class_bindings.perf-spec.ts @@ -10,8 +10,7 @@ import {$, browser} from 'protractor'; import {runBenchmark} from '../../../e2e_util/perf_util'; describe('class bindings perf', () => { - - it('should work for update', async() => { + it('should work for update', async () => { browser.rootEl = '#root'; await runBenchmark({ id: 'create', @@ -23,7 +22,7 @@ describe('class bindings perf', () => { }); }); - it('should work for update', async() => { + it('should work for update', async () => { browser.rootEl = '#root'; await runBenchmark({ id: 'update', @@ -34,5 +33,4 @@ describe('class bindings perf', () => { work: () => $('#update').click() }); }); - }); diff --git a/modules/benchmarks/src/expanding_rows/benchmark.ts b/modules/benchmarks/src/expanding_rows/benchmark.ts index 2fc9492f0a8a7..df18ff4a28a70 100644 --- a/modules/benchmarks/src/expanding_rows/benchmark.ts +++ b/modules/benchmarks/src/expanding_rows/benchmark.ts @@ -31,23 +31,32 @@ import {BenchmarkableExpandingRowModule} from './benchmarkable_expanding_row_mod </benchmark-area>`, }) export class InitializationRoot implements AfterViewInit { - @ViewChild(BenchmarkableExpandingRow, {static: true}) - expandingRow !: BenchmarkableExpandingRow; + @ViewChild(BenchmarkableExpandingRow, {static: true}) expandingRow!: BenchmarkableExpandingRow; ngAfterViewInit() {} - reset() { this.expandingRow.reset(); } + reset() { + this.expandingRow.reset(); + } - init() { this.expandingRow.init(); } + init() { + this.expandingRow.init(); + } async runAll() { - await execTimed('initialization_benchmark', async() => { await this.doInit(); }); + await execTimed('initialization_benchmark', async () => { + await this.doInit(); + }); } - async handleInitClick() { await this.doInit(); } + async handleInitClick() { + await this.doInit(); + } private async doInit() { - await execTimed('initial_load', async() => { this.expandingRow.init(); }); + await execTimed('initial_load', async () => { + this.expandingRow.init(); + }); } } @@ -74,5 +83,9 @@ export async function execTimed(description: string, func: () => Promise<void>) } export async function nextTick(delay = 1) { - return new Promise((res, rej) => { setTimeout(() => { res(); }, delay); }); + return new Promise((res, rej) => { + setTimeout(() => { + res(); + }, delay); + }); } diff --git a/modules/benchmarks/src/expanding_rows/benchmark_module.ts b/modules/benchmarks/src/expanding_rows/benchmark_module.ts index 6f13981f6067d..6a51767018b12 100644 --- a/modules/benchmarks/src/expanding_rows/benchmark_module.ts +++ b/modules/benchmarks/src/expanding_rows/benchmark_module.ts @@ -26,7 +26,9 @@ import {Component, ErrorHandler, Injectable, NgModule} from '@angular/core'; export class BenchmarkArea { } -declare interface ExtendedWindow extends Window { benchmarkErrors?: string[]; } +declare interface ExtendedWindow extends Window { + benchmarkErrors?: string[]; +} const extendedWindow = window as ExtendedWindow; @Injectable({providedIn: 'root'}) diff --git a/modules/benchmarks/src/expanding_rows/benchmarkable_expanding_row.ts b/modules/benchmarks/src/expanding_rows/benchmarkable_expanding_row.ts index 8c70934388bbc..a677ef152e4f3 100644 --- a/modules/benchmarks/src/expanding_rows/benchmarkable_expanding_row.ts +++ b/modules/benchmarks/src/expanding_rows/benchmarkable_expanding_row.ts @@ -44,12 +44,12 @@ export interface MlbTeam { }) export class BenchmarkableExpandingRow { // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. - showExpandingRow !: boolean; + showExpandingRow!: boolean; // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. - teams !: MlbTeam[]; + teams!: MlbTeam[]; // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. - private fakeTeams !: MlbTeam[]; + private fakeTeams!: MlbTeam[]; init(): void { this.teams = this.fakeTeams; diff --git a/modules/benchmarks/src/expanding_rows/expanding_row.ts b/modules/benchmarks/src/expanding_rows/expanding_row.ts index da6da38bb8543..aadfded4e639a 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row.ts @@ -112,15 +112,14 @@ export class ExpandingRow { * The identifier for this node provided by the user code. We need this * while we are emitting onToggle event. */ - @Input() rowId !: string; + @Input() rowId!: string; /** * An ElementRef to the main element in this component. We need a reference * to this element to compute the height. The height of cfc-expanding-row * is used in [cfcExpandingRowHost] directive for scroll adjustments. */ - @ViewChild('expandingRowMainElement', {static: true}) - expandingRowMainElement !: ElementRef; + @ViewChild('expandingRowMainElement', {static: true}) expandingRowMainElement!: ElementRef; /** * This @Output event emitter will be triggered when the user expands or @@ -145,7 +144,9 @@ export class ExpandingRow { } /** TS getter for isExpanded property. */ - get isExpanded(): boolean { return this.isExpandedInternal; } + get isExpanded(): boolean { + return this.isExpandedInternal; + } /** Triggered when isExpanded property changes. */ isExpandedChange = new EventEmitter<void>(); @@ -164,7 +165,9 @@ export class ExpandingRow { } /** TS getter for isFocused property. */ - get isFocused(): boolean { return this.isFocusedInternal; } + get isFocused(): boolean { + return this.isFocusedInternal; + } /** The index of the row in the context of the entire collection. */ set index(value: number) { @@ -178,7 +181,9 @@ export class ExpandingRow { } /** TS getter for index property. */ - get index(): number { return this.indexInternal; } + get index(): number { + return this.indexInternal; + } /** * We should probably rename this to summaryContentChild. Because technically @@ -188,7 +193,7 @@ export class ExpandingRow { * component is not in the same file as ExpandingRow. */ // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. - summaryViewChild !: ExpandingRowSummary; + summaryViewChild!: ExpandingRowSummary; /** * We compute the collapsed height (which is just height of @@ -205,7 +210,7 @@ export class ExpandingRow { /** Internal storage for index public property. */ // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. - private indexInternal !: number; + private indexInternal!: number; /** * This holds a reference to [cfcExpandingRowHost] directive. We need @@ -233,7 +238,9 @@ export class ExpandingRow { * When user tabs into child cfc-expanding-row-summary component. This method * will make sure we focuse on this row, and blur on previously focused row. */ - handleSummaryFocus(): void { this.focus(); } + handleSummaryFocus(): void { + this.focus(); + } /** * cfc-expanding-row-details-caption component will call this function to @@ -256,7 +263,9 @@ export class ExpandingRow { * Gets the height of this component. This height is used in parent * [cfcExpandingRowHost] directive to compute scroll adjustment. */ - getHeight(): number { return this.expandingRowMainElement.nativeElement.offsetHeight; } + getHeight(): number { + return this.expandingRowMainElement.nativeElement.offsetHeight; + } /** * Expands this row. This will notify the host so that it can collapse @@ -268,7 +277,9 @@ export class ExpandingRow { this.expandingRowHost.handleRowExpand(this); // setTimeout here makes sure we scroll this row into view after animation. - setTimeout(() => { this.expandingRowMainElement.nativeElement.focus(); }); + setTimeout(() => { + this.expandingRowMainElement.nativeElement.focus(); + }); this.onToggle.emit({rowId: this.rowId, isExpand: true}); } @@ -305,7 +316,9 @@ export class ExpandingRow { // Summary child is not present currently. We need to NG2 to update the // template. - setTimeout(() => { this.summaryViewChild.focus(); }); + setTimeout(() => { + this.summaryViewChild.focus(); + }); } /** diff --git a/modules/benchmarks/src/expanding_rows/expanding_row_details_caption.ts b/modules/benchmarks/src/expanding_rows/expanding_row_details_caption.ts index 15f77c95db13b..00cbe534fa5e9 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row_details_caption.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row_details_caption.ts @@ -49,5 +49,7 @@ export class ExpandingRowDetailsCaption implements OnDestroy { } /** When component is destroyed, unlisten to isExpanded. */ - ngOnDestroy(): void { this.onDestroy.next(); } + ngOnDestroy(): void { + this.onDestroy.next(); + } } diff --git a/modules/benchmarks/src/expanding_rows/expanding_row_details_content.ts b/modules/benchmarks/src/expanding_rows/expanding_row_details_content.ts index 2ab182a39ca3f..557f6f2731966 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row_details_content.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row_details_content.ts @@ -35,10 +35,13 @@ export class ExpandingRowDetailsContent implements OnDestroy { * hide this component if the row is collapsed. */ constructor(@Host() public expandingRow: ExpandingRow, changeDetectorRef: ChangeDetectorRef) { - this.isExpandedChangeSubscription = - this.expandingRow.isExpandedChange.subscribe(() => { changeDetectorRef.markForCheck(); }); + this.isExpandedChangeSubscription = this.expandingRow.isExpandedChange.subscribe(() => { + changeDetectorRef.markForCheck(); + }); } /** Unsubscribe from changes in parent isExpanded property. */ - ngOnDestroy(): void { this.isExpandedChangeSubscription.unsubscribe(); } + ngOnDestroy(): void { + this.isExpandedChangeSubscription.unsubscribe(); + } } diff --git a/modules/benchmarks/src/expanding_rows/expanding_row_host.ts b/modules/benchmarks/src/expanding_rows/expanding_row_host.ts index 52bf88ecb8f52..195013c43004a 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row_host.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row_host.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AfterContentInit, AfterViewInit, ChangeDetectionStrategy, Component, ContentChildren, ElementRef, EventEmitter, HostListener, Input, OnDestroy, Output, QueryList, ViewChild, forwardRef} from '@angular/core'; +import {AfterContentInit, AfterViewInit, ChangeDetectionStrategy, Component, ContentChildren, ElementRef, EventEmitter, forwardRef, HostListener, Input, OnDestroy, Output, QueryList, ViewChild} from '@angular/core'; import {Subscription} from 'rxjs'; import {EXPANDING_ROW_HOST_INJECTION_TOKEN, ExpandingRow, ExpandingRowHostBase} from './expanding_row'; @@ -26,7 +26,7 @@ export const EXPANDING_ROW_KEYPRESS_THORTTLE_MS = 50; * This type union is created to make arguments of handleUpOrDownPress* * methods in ExpandingRowHost class more readable. */ -type UpOrDown = 'up' | 'down'; +type UpOrDown = 'up'|'down'; /** * This is the wrapper directive for the cfc-expanding-row components. Note that @@ -48,8 +48,7 @@ type UpOrDown = 'up' | 'down'; changeDetection: ChangeDetectionStrategy.OnPush, providers: [{provide: EXPANDING_ROW_HOST_INJECTION_TOKEN, useExisting: ExpandingRowHost}], }) -export class ExpandingRowHost implements AfterViewInit, - OnDestroy, ExpandingRowHostBase { +export class ExpandingRowHost implements AfterViewInit, OnDestroy, ExpandingRowHostBase { /** * An HTML selector (e.g. "body") for the scroll element. We need this to * make some scroll adjustments. @@ -71,11 +70,10 @@ export class ExpandingRowHost implements AfterViewInit, @Output() onPrepend = new EventEmitter<void>(); /** A reference to the last focusable element in list of expanding rows. */ - @ViewChild('lastFocusable', {static: true}) lastFocusableElement !: ElementRef; + @ViewChild('lastFocusable', {static: true}) lastFocusableElement!: ElementRef; /** A reference to the first focusable element in list of expanding rows. */ - @ViewChild('firstFocusable', {static: true}) - firstFocusableElement !: ElementRef; + @ViewChild('firstFocusable', {static: true}) firstFocusableElement!: ElementRef; /** * A reference to all child cfc-expanding-row elements. We will need for @@ -83,7 +81,7 @@ export class ExpandingRowHost implements AfterViewInit, * which row is previous row when user presses "left arrow" on a focused row. */ @ContentChildren(forwardRef(() => ExpandingRow), {descendants: true}) - contentRows !: QueryList<ExpandingRow>; + contentRows!: QueryList<ExpandingRow>; /** * Keeps track of the last row that had focus before focus left the list @@ -122,7 +120,7 @@ export class ExpandingRowHost implements AfterViewInit, /** Subscription to changes in the expanding rows. */ // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. - private rowChangeSubscription !: Subscription; + private rowChangeSubscription!: Subscription; /** * When component initializes we need to attach click listener to the root @@ -138,8 +136,9 @@ export class ExpandingRowHost implements AfterViewInit, clickRootElement.addEventListener('mouseup', this.handleRootMouseUpBound); - this.rowChangeSubscription = - this.contentRows.changes.subscribe(() => { this.recalcRowIndexes(); }); + this.rowChangeSubscription = this.contentRows.changes.subscribe(() => { + this.recalcRowIndexes(); + }); this.recalcRowIndexes(); } @@ -256,13 +255,17 @@ export class ExpandingRowHost implements AfterViewInit, * Function that is called by expanding row summary to focus on the last * focusable element before the list of expanding rows. */ - focusOnPreviousFocusableElement(): void { this.lastFocusedRow = this.focusedRow; } + focusOnPreviousFocusableElement(): void { + this.lastFocusedRow = this.focusedRow; + } /** * Function that is called by expanding row summary to focus on the next * focusable element after the list of expanding rows. */ - focusOnNextFocusableElement(): void { this.lastFocusedRow = this.focusedRow; } + focusOnNextFocusableElement(): void { + this.lastFocusedRow = this.focusedRow; + } /** * Handles keydown event on the host. We are just concerned with up, @@ -275,7 +278,8 @@ export class ExpandingRowHost implements AfterViewInit, * - Enter: Expands the focused row. */ @HostListener('keydown', ['$event']) - handleKeyDown(event: KeyboardEvent) {} + handleKeyDown(event: KeyboardEvent) { + } /** * Recursively returns true if target HTMLElement is within a @@ -491,7 +495,10 @@ export class ExpandingRowHost implements AfterViewInit, // Updates all of the rows with their new index. private recalcRowIndexes() { let index = 0; - setTimeout( - () => { this.contentRows.forEach((row: ExpandingRow) => { row.index = index++; }); }); + setTimeout(() => { + this.contentRows.forEach((row: ExpandingRow) => { + row.index = index++; + }); + }); } } diff --git a/modules/benchmarks/src/expanding_rows/expanding_row_summary.ts b/modules/benchmarks/src/expanding_rows/expanding_row_summary.ts index e8ffb31534aa1..673e796271fc5 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row_summary.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row_summary.ts @@ -48,8 +48,7 @@ export class ExpandingRowSummary implements OnDestroy { * reference to compute collapsed height of the row. We also use this * reference for focus and blur methods below. */ - @ViewChild('expandingRowSummaryMainElement') - mainElementRef !: ElementRef; + @ViewChild('expandingRowSummaryMainElement') mainElementRef!: ElementRef; /** Subscription for changes in parent isExpanded property. */ private isExpandedSubscription: Subscription; @@ -65,11 +64,13 @@ export class ExpandingRowSummary implements OnDestroy { */ constructor(@Host() public expandingRow: ExpandingRow, changeDetectorRef: ChangeDetectorRef) { this.expandingRow.summaryViewChild = this; - this.isExpandedSubscription = - this.expandingRow.isExpandedChange.subscribe(() => { changeDetectorRef.markForCheck(); }); + this.isExpandedSubscription = this.expandingRow.isExpandedChange.subscribe(() => { + changeDetectorRef.markForCheck(); + }); - this.indexSubscription = - this.expandingRow.indexChange.subscribe(() => { changeDetectorRef.markForCheck(); }); + this.indexSubscription = this.expandingRow.indexChange.subscribe(() => { + changeDetectorRef.markForCheck(); + }); } @@ -203,5 +204,7 @@ export class ExpandingRowSummary implements OnDestroy { } /** Returns array of focusable elements within this component. */ - private getFocusableChildren(): HTMLElement[] { return []; } + private getFocusableChildren(): HTMLElement[] { + return []; + } } diff --git a/modules/benchmarks/src/expanding_rows/expanding_rows.perf-spec.ts b/modules/benchmarks/src/expanding_rows/expanding_rows.perf-spec.ts index ee4bab2193c83..316de6c5abd49 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_rows.perf-spec.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_rows.perf-spec.ts @@ -10,8 +10,7 @@ import {$, browser} from 'protractor'; import {runBenchmark} from '../../../e2e_util/perf_util'; describe('benchmarks', () => { - - it('should work for create', async() => { + it('should work for create', async () => { browser.rootEl = '#root'; await runBenchmark({ id: 'create', @@ -22,5 +21,4 @@ describe('benchmarks', () => { work: () => $('#init').click() }); }); - }); diff --git a/modules/benchmarks/src/expanding_rows/index_aot.ts b/modules/benchmarks/src/expanding_rows/index_aot.ts index d2318d8818f59..367e8753327cc 100644 --- a/modules/benchmarks/src/expanding_rows/index_aot.ts +++ b/modules/benchmarks/src/expanding_rows/index_aot.ts @@ -18,5 +18,5 @@ enableProdMode(); platformBrowser().bootstrapModuleFactory(ExpandingRowBenchmarkModuleNgFactory); function setMode(name: string): void { - document.querySelector('#rendererMode') !.textContent = `Render Mode: ${name}`; + document.querySelector('#rendererMode')!.textContent = `Render Mode: ${name}`; } diff --git a/modules/benchmarks/src/js-web-frameworks/js-web-frameworks.perf-spec.ts b/modules/benchmarks/src/js-web-frameworks/js-web-frameworks.perf-spec.ts index 0ed6859aa6fb0..200590588e9bd 100644 --- a/modules/benchmarks/src/js-web-frameworks/js-web-frameworks.perf-spec.ts +++ b/modules/benchmarks/src/js-web-frameworks/js-web-frameworks.perf-spec.ts @@ -24,19 +24,25 @@ const Create1KWorker: Worker = { const Delete1KWorker: Worker = { id: 'delete1K', prepare: () => $('#create1KRows').click(), - work: () => { $('#deleteAll').click(); } + work: () => { + $('#deleteAll').click(); + } }; const UpdateWorker: Worker = { id: 'update', prepare: () => $('#create1KRows').click(), - work: () => { $('#update').click(); } + work: () => { + $('#update').click(); + } }; const SwapWorker: Worker = { id: 'swap', prepare: () => $('#create1KRows').click(), - work: () => { $('#swap').click(); } + work: () => { + $('#swap').click(); + } }; // In order to make sure that we don't change the ids of the benchmarks, we need to @@ -45,15 +51,14 @@ const SwapWorker: Worker = { // name. e.g. "largeTable.ng2_switch.createDestroy". We determine the name of the // Bazel package where this test runs from the current test target. The Bazel target // looks like: "//modules/benchmarks/src/largetable/{pkg_name}:{target_name}". -const testPackageName = process.env['BAZEL_TARGET'] !.split(':')[0].split('/').pop(); +const testPackageName = process.env['BAZEL_TARGET']!.split(':')[0].split('/').pop(); describe('js-web-frameworks benchmark perf', () => { - afterEach(verifyNoBrowserErrors); [Create1KWorker, Delete1KWorker, UpdateWorker, SwapWorker].forEach((worker) => { describe(worker.id, () => { - it(`should run benchmark for ${testPackageName}`, async() => { + it(`should run benchmark for ${testPackageName}`, async () => { await runTableBenchmark({ id: `js-web-frameworks.${testPackageName}.${worker.id}`, url: '/', diff --git a/modules/benchmarks/src/js-web-frameworks/ng2/rows.ts b/modules/benchmarks/src/js-web-frameworks/ng2/rows.ts index 655c2385cddc0..e691da507c131 100644 --- a/modules/benchmarks/src/js-web-frameworks/ng2/rows.ts +++ b/modules/benchmarks/src/js-web-frameworks/ng2/rows.ts @@ -42,14 +42,16 @@ export class JsWebFrameworksComponent { constructor(private _appRef: ApplicationRef) {} - itemById(index: number, item: RowData) { return item.id; } + itemById(index: number, item: RowData) { + return item.id; + } select(itemId: number) { this.selected = itemId; this._appRef.tick(); } - delete (itemId: number) { + delete(itemId: number) { const data = this.data; for (let i = 0, l = data.length; i < l; i++) { if (data[i].id === itemId) { diff --git a/modules/benchmarks/src/largeform/largeform.e2e-spec.ts b/modules/benchmarks/src/largeform/largeform.e2e-spec.ts index 5cd7516a27689..de55e1600263a 100644 --- a/modules/benchmarks/src/largeform/largeform.e2e-spec.ts +++ b/modules/benchmarks/src/largeform/largeform.e2e-spec.ts @@ -11,10 +11,9 @@ import {$, By, element} from 'protractor'; import {openBrowser, verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('largeform benchmark', () => { - afterEach(verifyNoBrowserErrors); - it('should work for ng2', async() => { + it('should work for ng2', async () => { openBrowser({ url: '/', params: [{name: 'copies', value: 1}], diff --git a/modules/benchmarks/src/largeform/largeform.perf-spec.ts b/modules/benchmarks/src/largeform/largeform.perf-spec.ts index fe06cd8ef361a..ccab69f06521d 100644 --- a/modules/benchmarks/src/largeform/largeform.perf-spec.ts +++ b/modules/benchmarks/src/largeform/largeform.perf-spec.ts @@ -26,12 +26,11 @@ const CreateAndDestroyWorker = { }; describe('largeform benchmark spec', () => { - afterEach(verifyNoBrowserErrors); [CreateAndDestroyWorker].forEach((worker) => { describe(worker.id, () => { - it('should run for ng2', async() => { + it('should run for ng2', async () => { await runLargeFormBenchmark({url: '/', id: `largeform.ng2.${worker.id}`, worker: worker}); }); }); diff --git a/modules/benchmarks/src/largetable/incremental_dom/table.ts b/modules/benchmarks/src/largetable/incremental_dom/table.ts index 7d50f8ff2731a..6a9e60024d985 100644 --- a/modules/benchmarks/src/largetable/incremental_dom/table.ts +++ b/modules/benchmarks/src/largetable/incremental_dom/table.ts @@ -17,7 +17,9 @@ const {patch, elementOpen, elementClose, elementOpenStart, elementOpenEnd, attr, export class TableComponent { constructor(private _rootEl: any) {} - set data(data: TableCell[][]) { patch(this._rootEl, () => this._render(data)); } + set data(data: TableCell[][]) { + patch(this._rootEl, () => this._render(data)); + } private _render(data: TableCell[][]) { elementOpen('table'); diff --git a/modules/benchmarks/src/largetable/largetable.e2e-spec.ts b/modules/benchmarks/src/largetable/largetable.e2e-spec.ts index 0068b33b2a521..3b6da53085fa0 100644 --- a/modules/benchmarks/src/largetable/largetable.e2e-spec.ts +++ b/modules/benchmarks/src/largetable/largetable.e2e-spec.ts @@ -13,7 +13,7 @@ import {openBrowser, verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('largetable benchmark', () => { afterEach(verifyNoBrowserErrors); - it(`should render the table`, async() => { + it(`should render the table`, async () => { openBrowser({ url: '', ignoreBrowserSynchronization: true, diff --git a/modules/benchmarks/src/largetable/largetable.perf-spec.ts b/modules/benchmarks/src/largetable/largetable.perf-spec.ts index b78931a009f96..ffd96ece13c65 100644 --- a/modules/benchmarks/src/largetable/largetable.perf-spec.ts +++ b/modules/benchmarks/src/largetable/largetable.perf-spec.ts @@ -40,15 +40,14 @@ const UpdateWorker: Worker = { // name. e.g. "largeTable.ng2_switch.createDestroy". We determine the name of the // Bazel package where this test runs from the current test target. The Bazel target // looks like: "//modules/benchmarks/src/largetable/{pkg_name}:{target_name}". -const testPackageName = process.env['BAZEL_TARGET'] !.split(':')[0].split('/').pop(); +const testPackageName = process.env['BAZEL_TARGET']!.split(':')[0].split('/').pop(); describe('largetable benchmark perf', () => { - afterEach(verifyNoBrowserErrors); [CreateOnlyWorker, CreateAndDestroyWorker, UpdateWorker].forEach((worker) => { describe(worker.id, () => { - it(`should run benchmark for ${testPackageName}`, async() => { + it(`should run benchmark for ${testPackageName}`, async () => { await runTableBenchmark({ id: `largeTable.${testPackageName}.${worker.id}`, url: '/', diff --git a/modules/benchmarks/src/largetable/ng2/table.ts b/modules/benchmarks/src/largetable/ng2/table.ts index 55028bf29a3f4..d00f811563312 100644 --- a/modules/benchmarks/src/largetable/ng2/table.ts +++ b/modules/benchmarks/src/largetable/ng2/table.ts @@ -9,7 +9,7 @@ import {Component, Input, NgModule} from '@angular/core'; import {BrowserModule, DomSanitizer, SafeStyle} from '@angular/platform-browser'; -import {TableCell, emptyTable} from '../util'; +import {emptyTable, TableCell} from '../util'; let trustedEmptyColor: SafeStyle; let trustedGreyColor: SafeStyle; @@ -25,12 +25,15 @@ let trustedGreyColor: SafeStyle; </tbody></table>`, }) export class TableComponent { - @Input() - data: TableCell[][] = emptyTable; + @Input() data: TableCell[][] = emptyTable; - trackByIndex(index: number, item: any) { return index; } + trackByIndex(index: number, item: any) { + return index; + } - getColor(row: number) { return row % 2 ? trustedEmptyColor : trustedGreyColor; } + getColor(row: number) { + return row % 2 ? trustedEmptyColor : trustedGreyColor; + } } @NgModule({imports: [BrowserModule], bootstrap: [TableComponent], declarations: [TableComponent]}) diff --git a/modules/benchmarks/src/largetable/ng2_switch/table.ts b/modules/benchmarks/src/largetable/ng2_switch/table.ts index 10b73f6539be0..8e1c5a7fe2bfd 100644 --- a/modules/benchmarks/src/largetable/ng2_switch/table.ts +++ b/modules/benchmarks/src/largetable/ng2_switch/table.ts @@ -9,7 +9,7 @@ import {Component, Input, NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; -import {TableCell, emptyTable} from '../util'; +import {emptyTable, TableCell} from '../util'; @Component({ selector: 'largetable', @@ -22,10 +22,11 @@ import {TableCell, emptyTable} from '../util'; </tbody></table>` }) export class TableComponent { - @Input() - data: TableCell[][] = emptyTable; + @Input() data: TableCell[][] = emptyTable; - trackByIndex(index: number, item: any) { return index; } + trackByIndex(index: number, item: any) { + return index; + } } @NgModule({imports: [BrowserModule], bootstrap: [TableComponent], declarations: [TableComponent]}) diff --git a/modules/benchmarks/src/largetable/render3/index_aot.ts b/modules/benchmarks/src/largetable/render3/index_aot.ts index 77c3c8610dc89..6abeee67421bb 100644 --- a/modules/benchmarks/src/largetable/render3/index_aot.ts +++ b/modules/benchmarks/src/largetable/render3/index_aot.ts @@ -9,7 +9,7 @@ import {ɵrenderComponent as renderComponent} from '@angular/core'; import {bindAction, profile} from '../../util'; -import {LargeTableComponent, createDom, destroyDom} from './table'; +import {createDom, destroyDom, LargeTableComponent} from './table'; function noop() {} diff --git a/modules/benchmarks/src/largetable/render3/table.ts b/modules/benchmarks/src/largetable/render3/table.ts index 5634f68f375f7..85339a976e935 100644 --- a/modules/benchmarks/src/largetable/render3/table.ts +++ b/modules/benchmarks/src/largetable/render3/table.ts @@ -9,7 +9,7 @@ import {CommonModule} from '@angular/common'; import {Component, Input, NgModule, ɵdetectChanges} from '@angular/core'; -import {TableCell, buildTable, emptyTable} from '../util'; +import {buildTable, emptyTable, TableCell} from '../util'; @Component({ selector: 'largetable', @@ -26,12 +26,15 @@ import {TableCell, buildTable, emptyTable} from '../util'; `, }) export class LargeTableComponent { - @Input() - data: TableCell[][] = emptyTable; + @Input() data: TableCell[][] = emptyTable; - trackByIndex(index: number, item: any) { return index; } + trackByIndex(index: number, item: any) { + return index; + } - getColor(row: number) { return row % 2 ? '' : 'grey'; } + getColor(row: number) { + return row % 2 ? '' : 'grey'; + } } @NgModule({declarations: [LargeTableComponent], imports: [CommonModule]}) diff --git a/modules/benchmarks/src/old/compiler/compiler_benchmark.ts b/modules/benchmarks/src/old/compiler/compiler_benchmark.ts index 51cf1d0ffab45..9dd4572bc84b2 100644 --- a/modules/benchmarks/src/old/compiler/compiler_benchmark.ts +++ b/modules/benchmarks/src/old/compiler/compiler_benchmark.ts @@ -6,28 +6,23 @@ * found in the LICENSE file at https://angular.io/license */ +import {CompilerConfig, DirectiveResolver} from '@angular/compiler'; +import {Component, ComponentResolver, Directive, ViewContainerRef,} from '@angular/core'; +import {ViewMetadata} from '@angular/core/src/metadata/view'; import {PromiseWrapper} from '@angular/facade/src/async'; -import {Type, print} from '@angular/facade/src/lang'; +import {print, Type} from '@angular/facade/src/lang'; import {bootstrap} from '@angular/platform-browser'; import {BrowserDomAdapter} from '@angular/platform-browser/src/browser/browser_adapter'; import {DOM} from '@angular/platform-browser/src/dom/dom_adapter'; - -import {ComponentResolver, Component, Directive, ViewContainerRef,} from '@angular/core'; - -import {ViewMetadata} from '@angular/core/src/metadata/view'; - -import {CompilerConfig, DirectiveResolver} from '@angular/compiler'; - -import {getIntParameter, bindAction} from '@angular/testing/src/benchmark_util'; +import {bindAction, getIntParameter} from '@angular/testing/src/benchmark_util'; function _createBindings(): any[] { const multiplyTemplatesBy = getIntParameter('elements'); return [ { provide: DirectiveResolver, - useFactory: - () => new MultiplyDirectiveResolver( - multiplyTemplatesBy, [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]), + useFactory: () => new MultiplyDirectiveResolver( + multiplyTemplatesBy, [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]), deps: [] }, // Use interpretative mode as Dart does not support JIT and @@ -57,7 +52,9 @@ function measureWrapper(func, desc) { const elapsedMs = new Date().getTime() - begin.getTime(); print(`[${desc}] ...done, took ${elapsedMs} ms`); }; - const onError = function(e) { DOM.logError(e); }; + const onError = function(e) { + DOM.logError(e); + }; PromiseWrapper.then(func(), onSuccess, onError); }; } diff --git a/modules/benchmarks/src/old/compiler/selector_benchmark.ts b/modules/benchmarks/src/old/compiler/selector_benchmark.ts index d1812a2d6d20d..028728eaa6eef 100644 --- a/modules/benchmarks/src/old/compiler/selector_benchmark.ts +++ b/modules/benchmarks/src/old/compiler/selector_benchmark.ts @@ -47,7 +47,9 @@ export function main() { function match() { let matchCount = 0; for (let i = 0; i < count; i++) { - fixedMatcher.match(fixedSelectors[i][0], (selector, selected) => { matchCount += selected; }); + fixedMatcher.match(fixedSelectors[i][0], (selector, selected) => { + matchCount += selected; + }); } return matchCount; } diff --git a/modules/benchmarks/src/old/costs/index.ts b/modules/benchmarks/src/old/costs/index.ts index 74023d5610766..2d3929b8fb492 100644 --- a/modules/benchmarks/src/old/costs/index.ts +++ b/modules/benchmarks/src/old/costs/index.ts @@ -10,7 +10,7 @@ import {NgFor, NgIf} from '@angular/common'; import {Component, Directive, DynamicComponentLoader, ViewContainerRef} from '@angular/core'; import {ApplicationRef} from '@angular/core/src/application_ref'; import {ListWrapper} from '@angular/facade/src/lang'; -import {BrowserModule, bootstrap} from '@angular/platform-browser'; +import {bootstrap, BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {bindAction, getIntParameter} from '@angular/testing/src/benchmark_util'; @@ -89,7 +89,9 @@ class AppComponent { testingWithDirectives: boolean; testingDynamicComponents: boolean; - constructor() { this.reset(); } + constructor() { + this.reset(); + } reset(): void { this.list = []; diff --git a/modules/benchmarks/src/old/di/di_benchmark.ts b/modules/benchmarks/src/old/di/di_benchmark.ts index 6d1e8fcc48752..ca00d579639af 100644 --- a/modules/benchmarks/src/old/di/di_benchmark.ts +++ b/modules/benchmarks/src/old/di/di_benchmark.ts @@ -98,30 +98,42 @@ export function main() { @Injectable() class A { - constructor() { count++; } + constructor() { + count++; + } } @Injectable() class B { - constructor(a: A) { count++; } + constructor(a: A) { + count++; + } } @Injectable() class C { - constructor(b: B) { count++; } + constructor(b: B) { + count++; + } } @Injectable() class D { - constructor(c: C, b: B) { count++; } + constructor(c: C, b: B) { + count++; + } } @Injectable() class E { - constructor(d: D, c: C) { count++; } + constructor(d: D, c: C) { + count++; + } } @Injectable() class F { - constructor(e: E, d: D) { count++; } + constructor(e: E, d: D) { + count++; + } } diff --git a/modules/benchmarks/src/old/naive_infinite_scroll/app.ts b/modules/benchmarks/src/old/naive_infinite_scroll/app.ts index 4456bf3fe1c99..8d4297d7b5113 100644 --- a/modules/benchmarks/src/old/naive_infinite_scroll/app.ts +++ b/modules/benchmarks/src/old/naive_infinite_scroll/app.ts @@ -45,7 +45,9 @@ export class App { for (let i = 0; i < appSize; i++) { this.scrollAreas.push(i); } - bindAction('#run-btn', () => { this.runBenchmark(); }); + bindAction('#run-btn', () => { + this.runBenchmark(); + }); bindAction('#reset-btn', () => { this._getScrollDiv().scrollTop = 0; const existingMarker = this._locateFinishedMarker(); @@ -88,7 +90,11 @@ export class App { }, 0); } - private _locateFinishedMarker() { return DOM.querySelector(document.body, '#done'); } + private _locateFinishedMarker() { + return DOM.querySelector(document.body, '#done'); + } - private _getScrollDiv() { return DOM.query('body /deep/ #scrollDiv'); } + private _getScrollDiv() { + return DOM.query('body /deep/ #scrollDiv'); + } } diff --git a/modules/benchmarks/src/old/naive_infinite_scroll/cells.ts b/modules/benchmarks/src/old/naive_infinite_scroll/cells.ts index 747ff2fd2a205..1f4de9cb6f102 100644 --- a/modules/benchmarks/src/old/naive_infinite_scroll/cells.ts +++ b/modules/benchmarks/src/old/naive_infinite_scroll/cells.ts @@ -16,7 +16,9 @@ export class HasStyle { constructor() {} - set width(w: number) { this.cellWidth = w; } + set width(w: number) { + this.cellWidth = w; + } } @Component({ @@ -74,7 +76,9 @@ export class StageButtonsComponent extends HasStyle { private _offering: Offering; stages: Stage[]; - get offering(): Offering { return this._offering; } + get offering(): Offering { + return this._offering; + } set offering(offering: Offering) { this._offering = offering; diff --git a/modules/benchmarks/src/old/naive_infinite_scroll/common.ts b/modules/benchmarks/src/old/naive_infinite_scroll/common.ts index 4d0a269a051ec..2279def26466d 100644 --- a/modules/benchmarks/src/old/naive_infinite_scroll/common.ts +++ b/modules/benchmarks/src/old/naive_infinite_scroll/common.ts @@ -56,13 +56,17 @@ export class CustomDate { return new CustomDate(newYear, newMonth, newDay); } - static now(): CustomDate { return new CustomDate(2014, 1, 28); } + static now(): CustomDate { + return new CustomDate(2014, 1, 28); + } } export class RawEntity { private _data: Map<any, any>; - constructor() { this._data = new Map(); } + constructor() { + this._data = new Map(); + } get(key: string) { if (key.indexOf('.') == -1) { @@ -114,51 +118,107 @@ export class RawEntity { } export class Company extends RawEntity { - get name(): string { return this.get('name'); } - set name(val: string) { this.set('name', val); } + get name(): string { + return this.get('name'); + } + set name(val: string) { + this.set('name', val); + } } export class Offering extends RawEntity { - get name(): string { return this.get('name'); } - set name(val: string) { this.set('name', val); } + get name(): string { + return this.get('name'); + } + set name(val: string) { + this.set('name', val); + } - get company(): Company { return this.get('company'); } - set company(val: Company) { this.set('company', val); } + get company(): Company { + return this.get('company'); + } + set company(val: Company) { + this.set('company', val); + } - get opportunity(): Opportunity { return this.get('opportunity'); } - set opportunity(val: Opportunity) { this.set('opportunity', val); } + get opportunity(): Opportunity { + return this.get('opportunity'); + } + set opportunity(val: Opportunity) { + this.set('opportunity', val); + } - get account(): Account { return this.get('account'); } - set account(val: Account) { this.set('account', val); } + get account(): Account { + return this.get('account'); + } + set account(val: Account) { + this.set('account', val); + } - get basePoints(): number { return this.get('basePoints'); } - set basePoints(val: number) { this.set('basePoints', val); } + get basePoints(): number { + return this.get('basePoints'); + } + set basePoints(val: number) { + this.set('basePoints', val); + } - get kickerPoints(): number { return this.get('kickerPoints'); } - set kickerPoints(val: number) { this.set('kickerPoints', val); } + get kickerPoints(): number { + return this.get('kickerPoints'); + } + set kickerPoints(val: number) { + this.set('kickerPoints', val); + } - get status(): string { return this.get('status'); } - set status(val: string) { this.set('status', val); } + get status(): string { + return this.get('status'); + } + set status(val: string) { + this.set('status', val); + } - get bundles(): string { return this.get('bundles'); } - set bundles(val: string) { this.set('bundles', val); } + get bundles(): string { + return this.get('bundles'); + } + set bundles(val: string) { + this.set('bundles', val); + } - get dueDate(): CustomDate { return this.get('dueDate'); } - set dueDate(val: CustomDate) { this.set('dueDate', val); } + get dueDate(): CustomDate { + return this.get('dueDate'); + } + set dueDate(val: CustomDate) { + this.set('dueDate', val); + } - get endDate(): CustomDate { return this.get('endDate'); } - set endDate(val: CustomDate) { this.set('endDate', val); } + get endDate(): CustomDate { + return this.get('endDate'); + } + set endDate(val: CustomDate) { + this.set('endDate', val); + } - get aatStatus(): string { return this.get('aatStatus'); } - set aatStatus(val: string) { this.set('aatStatus', val); } + get aatStatus(): string { + return this.get('aatStatus'); + } + set aatStatus(val: string) { + this.set('aatStatus', val); + } } export class Opportunity extends RawEntity { - get name(): string { return this.get('name'); } - set name(val: string) { this.set('name', val); } + get name(): string { + return this.get('name'); + } + set name(val: string) { + this.set('name', val); + } } export class Account extends RawEntity { - get accountId(): number { return this.get('accountId'); } - set accountId(val: number) { this.set('accountId', val); } + get accountId(): number { + return this.get('accountId'); + } + set accountId(val: number) { + this.set('accountId', val); + } } diff --git a/modules/benchmarks/src/old/naive_infinite_scroll/scroll_area.ts b/modules/benchmarks/src/old/naive_infinite_scroll/scroll_area.ts index f0b71725d3ced..807c29a166563 100644 --- a/modules/benchmarks/src/old/naive_infinite_scroll/scroll_area.ts +++ b/modules/benchmarks/src/old/naive_infinite_scroll/scroll_area.ts @@ -9,7 +9,7 @@ import {NgFor} from '@angular/common'; import {Component, Directive} from '@angular/core'; -import {HEIGHT, ITEMS, ITEM_HEIGHT, Offering, ROW_WIDTH, VIEW_PORT_HEIGHT, VISIBLE_ITEMS} from './common'; +import {HEIGHT, ITEM_HEIGHT, ITEMS, Offering, ROW_WIDTH, VIEW_PORT_HEIGHT, VISIBLE_ITEMS} from './common'; import {generateOfferings} from './random_data'; import {ScrollItemComponent} from './scroll_item'; diff --git a/modules/benchmarks/src/old/naive_infinite_scroll/scroll_item.ts b/modules/benchmarks/src/old/naive_infinite_scroll/scroll_item.ts index b88862454449c..4604384676a67 100644 --- a/modules/benchmarks/src/old/naive_infinite_scroll/scroll_item.ts +++ b/modules/benchmarks/src/old/naive_infinite_scroll/scroll_item.ts @@ -9,7 +9,7 @@ import {Component, Directive} from '@angular/core'; import {AccountCellComponent, CompanyNameComponent, FormattedCellComponent, OfferingNameComponent, OpportunityNameComponent, StageButtonsComponent} from './cells'; -import {AAT_STATUS_WIDTH, ACCOUNT_CELL_WIDTH, BASE_POINTS_WIDTH, BUNDLES_WIDTH, COMPANY_NAME_WIDTH, DUE_DATE_WIDTH, END_DATE_WIDTH, ITEM_HEIGHT, KICKER_POINTS_WIDTH, OFFERING_NAME_WIDTH, OPPORTUNITY_NAME_WIDTH, Offering, STAGE_BUTTONS_WIDTH} from './common'; +import {AAT_STATUS_WIDTH, ACCOUNT_CELL_WIDTH, BASE_POINTS_WIDTH, BUNDLES_WIDTH, COMPANY_NAME_WIDTH, DUE_DATE_WIDTH, END_DATE_WIDTH, ITEM_HEIGHT, KICKER_POINTS_WIDTH, Offering, OFFERING_NAME_WIDTH, OPPORTUNITY_NAME_WIDTH, STAGE_BUTTONS_WIDTH} from './common'; @Component({ selector: 'scroll-item', @@ -63,17 +63,41 @@ export class ScrollItemComponent { itemHeight: number; - constructor() { this.itemHeight = ITEM_HEIGHT; } + constructor() { + this.itemHeight = ITEM_HEIGHT; + } - get companyNameWidth() { return COMPANY_NAME_WIDTH; } - get opportunityNameWidth() { return OPPORTUNITY_NAME_WIDTH; } - get offeringNameWidth() { return OFFERING_NAME_WIDTH; } - get accountCellWidth() { return ACCOUNT_CELL_WIDTH; } - get basePointsWidth() { return BASE_POINTS_WIDTH; } - get kickerPointsWidth() { return KICKER_POINTS_WIDTH; } - get stageButtonsWidth() { return STAGE_BUTTONS_WIDTH; } - get bundlesWidth() { return BUNDLES_WIDTH; } - get dueDateWidth() { return DUE_DATE_WIDTH; } - get endDateWidth() { return END_DATE_WIDTH; } - get aatStatusWidth() { return AAT_STATUS_WIDTH; } + get companyNameWidth() { + return COMPANY_NAME_WIDTH; + } + get opportunityNameWidth() { + return OPPORTUNITY_NAME_WIDTH; + } + get offeringNameWidth() { + return OFFERING_NAME_WIDTH; + } + get accountCellWidth() { + return ACCOUNT_CELL_WIDTH; + } + get basePointsWidth() { + return BASE_POINTS_WIDTH; + } + get kickerPointsWidth() { + return KICKER_POINTS_WIDTH; + } + get stageButtonsWidth() { + return STAGE_BUTTONS_WIDTH; + } + get bundlesWidth() { + return BUNDLES_WIDTH; + } + get dueDateWidth() { + return DUE_DATE_WIDTH; + } + get endDateWidth() { + return END_DATE_WIDTH; + } + get aatStatusWidth() { + return AAT_STATUS_WIDTH; + } } diff --git a/modules/benchmarks/src/styling/ng2/init.ts b/modules/benchmarks/src/styling/ng2/init.ts index e7816d80f8cd4..b697223019c09 100644 --- a/modules/benchmarks/src/styling/ng2/init.ts +++ b/modules/benchmarks/src/styling/ng2/init.ts @@ -23,7 +23,7 @@ export function init(moduleRef: NgModuleRef<StylingModule>) { const componentRef = appRef.components[0]; const component = componentRef.instance; const componentHostEl = componentRef.location.nativeElement; - const select = document.querySelector('#scenario-select') !as HTMLSelectElement; + const select = document.querySelector('#scenario-select')! as HTMLSelectElement; function create(tplRefIdx: number) { component.tplRefIdx = tplRefIdx; @@ -41,7 +41,9 @@ export function init(moduleRef: NgModuleRef<StylingModule>) { appRef.tick(); } - function detectChanges() { appRef.tick(); } + function detectChanges() { + appRef.tick(); + } function modifyExternally() { const buttonEls = componentHostEl.querySelectorAll('button') as HTMLButtonElement[]; diff --git a/modules/benchmarks/src/styling/ng2/styling.ts b/modules/benchmarks/src/styling/ng2/styling.ts index b669654ae7f05..87c65829f99df 100644 --- a/modules/benchmarks/src/styling/ng2/styling.ts +++ b/modules/benchmarks/src/styling/ng2/styling.ts @@ -35,7 +35,9 @@ export class StylingComponent { tplRefIdx: number = 0; staticStyle = {width: '10px'}; - getTplRef(...tplRefs): TemplateRef<any> { return tplRefs[this.tplRefIdx]; } + getTplRef(...tplRefs): TemplateRef<any> { + return tplRefs[this.tplRefIdx]; + } } @NgModule({ diff --git a/modules/benchmarks/src/styling/styling_perf.spec.ts b/modules/benchmarks/src/styling/styling_perf.spec.ts index edf69fd43d4bd..779ef33bf8c70 100644 --- a/modules/benchmarks/src/styling/styling_perf.spec.ts +++ b/modules/benchmarks/src/styling/styling_perf.spec.ts @@ -28,7 +28,7 @@ const SCENARIOS = [ describe('styling benchmark spec', () => { afterEach(verifyNoBrowserErrors); - it('should render and interact to update and detect changes', async() => { + it('should render and interact to update and detect changes', async () => { openBrowser({url: '/', ignoreBrowserSynchronization: true}); create(); const items = element.all(by.css('styling-bindings button')); @@ -38,7 +38,7 @@ describe('styling benchmark spec', () => { expect(await items.first().getAttribute('title')).toBe('baz'); }); - it('should render and run noop change detection', async() => { + it('should render and run noop change detection', async () => { openBrowser({url: '/', ignoreBrowserSynchronization: true}); create(); const items = element.all(by.css('styling-bindings button')); @@ -51,7 +51,7 @@ describe('styling benchmark spec', () => { // Create benchmarks for each possible test scenario. SCENARIOS.forEach(({optionIndex, id}) => { describe(id, () => { - it('should run create benchmark', async() => { + it('should run create benchmark', async () => { await runStylingBenchmark(`styling.${id}.create`, { work: () => create(), prepare: () => { @@ -61,7 +61,7 @@ describe('styling benchmark spec', () => { }); }); - it('should run update benchmark', async() => { + it('should run update benchmark', async () => { await runStylingBenchmark(`styling.${id}.update`, { work: () => update(), prepare: () => { @@ -71,7 +71,7 @@ describe('styling benchmark spec', () => { }); }); - it('should run detect changes benchmark', async() => { + it('should run detect changes benchmark', async () => { await runStylingBenchmark(`styling.${id}.noop_cd`, { work: () => detectChanges(), prepare: () => { diff --git a/modules/benchmarks/src/tree/baseline/tree.ts b/modules/benchmarks/src/tree/baseline/tree.ts index 8350018a600c1..c1a97ba99cfbb 100644 --- a/modules/benchmarks/src/tree/baseline/tree.ts +++ b/modules/benchmarks/src/tree/baseline/tree.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TreeNode, newArray} from '../util'; +import {newArray, TreeNode} from '../util'; export class TreeComponent { private _renderNodes: any[]; diff --git a/modules/benchmarks/src/tree/incremental_dom/tree.ts b/modules/benchmarks/src/tree/incremental_dom/tree.ts index 0a5a23ab6bafe..c241f4ab5b1b1 100644 --- a/modules/benchmarks/src/tree/incremental_dom/tree.ts +++ b/modules/benchmarks/src/tree/incremental_dom/tree.ts @@ -17,7 +17,9 @@ const {patch, elementOpen, elementClose, elementOpenStart, elementOpenEnd, text, export class TreeComponent { constructor(private _rootEl: any) {} - set data(data: TreeNode) { patch(this._rootEl, () => this._render(data)); } + set data(data: TreeNode) { + patch(this._rootEl, () => this._render(data)); + } private _render(data: TreeNode) { elementOpenStart('span', '', null); diff --git a/modules/benchmarks/src/tree/ng1/index.ts b/modules/benchmarks/src/tree/ng1/index.ts index d4dd3c80ae2ae..40348ed4aaebe 100644 --- a/modules/benchmarks/src/tree/ng1/index.ts +++ b/modules/benchmarks/src/tree/ng1/index.ts @@ -15,7 +15,7 @@ declare var angular: any; function init() { let detectChangesRuns = 0; - const numberOfChecksEl = document.getElementById('numberOfChecks') !; + const numberOfChecksEl = document.getElementById('numberOfChecks')!; addTreeToModule(angular.module('app', [])).run([ '$rootScope', @@ -31,11 +31,15 @@ function init() { function noop() {} function destroyDom() { - $rootScope.$apply(() => { $rootScope.initData = emptyTree; }); + $rootScope.$apply(() => { + $rootScope.initData = emptyTree; + }); } function createDom() { - $rootScope.$apply(() => { $rootScope.initData = buildTree(); }); + $rootScope.$apply(() => { + $rootScope.initData = buildTree(); + }); } bindAction('#destroyDom', destroyDom); diff --git a/modules/benchmarks/src/tree/ng1/tree.ts b/modules/benchmarks/src/tree/ng1/tree.ts index d914fa9b7b1bb..a212febb9819e 100644 --- a/modules/benchmarks/src/tree/ng1/tree.ts +++ b/modules/benchmarks/src/tree/ng1/tree.ts @@ -41,7 +41,6 @@ export function addTreeToModule(mod: any): any { } let childElement: any, childScope: any; $scope.$watch($attr.data, function ngIfWatchAction(value: any) { - if (value) { if (!childScope) { childScope = $scope.$new(); @@ -67,6 +66,8 @@ export function addTreeToModule(mod: any): any { ]) .config([ '$compileProvider', - function($compileProvider: any) { $compileProvider.debugInfoEnabled(false); } + function($compileProvider: any) { + $compileProvider.debugInfoEnabled(false); + } ]); } diff --git a/modules/benchmarks/src/tree/ng2/init.ts b/modules/benchmarks/src/tree/ng2/init.ts index 06d5b861283bf..1f45f7cb0f2f6 100644 --- a/modules/benchmarks/src/tree/ng2/init.ts +++ b/modules/benchmarks/src/tree/ng2/init.ts @@ -40,7 +40,7 @@ export function init(moduleRef: NgModuleRef<AppModule>) { const injector = moduleRef.injector; appRef = injector.get(ApplicationRef); - const numberOfChecksEl = document.getElementById('numberOfChecks') !; + const numberOfChecksEl = document.getElementById('numberOfChecks')!; tree = appRef.components[0].instance; diff --git a/modules/benchmarks/src/tree/ng2/tree.ts b/modules/benchmarks/src/tree/ng2/tree.ts index eea897e5cb6a0..199f33355cc4c 100644 --- a/modules/benchmarks/src/tree/ng2/tree.ts +++ b/modules/benchmarks/src/tree/ng2/tree.ts @@ -9,7 +9,7 @@ import {Component, NgModule} from '@angular/core'; import {BrowserModule, DomSanitizer, SafeStyle} from '@angular/platform-browser'; -import {TreeNode, emptyTree} from '../util'; +import {emptyTree, TreeNode} from '../util'; let trustedEmptyColor: SafeStyle; let trustedGreyColor: SafeStyle; @@ -22,7 +22,9 @@ let trustedGreyColor: SafeStyle; }) export class TreeComponent { data: TreeNode = emptyTree; - get bgColor() { return this.data.depth % 2 ? trustedEmptyColor : trustedGreyColor; } + get bgColor() { + return this.data.depth % 2 ? trustedEmptyColor : trustedGreyColor; + } } @NgModule({imports: [BrowserModule], bootstrap: [TreeComponent], declarations: [TreeComponent]}) diff --git a/modules/benchmarks/src/tree/ng2_next/tree.ts b/modules/benchmarks/src/tree/ng2_next/tree.ts index 6ded8e33d64ae..31a28a9e3f82d 100644 --- a/modules/benchmarks/src/tree/ng2_next/tree.ts +++ b/modules/benchmarks/src/tree/ng2_next/tree.ts @@ -7,17 +7,19 @@ */ import {NgIf} from '@angular/common'; -import {ComponentFactory, ComponentFactoryResolver, ComponentRef, ErrorHandler, Injector, NgModuleRef, RendererFactory2, Sanitizer, TemplateRef, ViewContainerRef, ɵArgumentType as ArgumentType, ɵBindingFlags as BindingFlags, ɵNodeFlags as NodeFlags, ɵViewDefinition as ViewDefinition, ɵViewFlags as ViewFlags, ɵand as anchorDef, ɵccf as createComponentFactory, ɵdid as directiveDef, ɵeld as elementDef, ɵinitServicesIfNeeded as initServicesIfNeeded, ɵted as textDef, ɵvid as viewDef} from '@angular/core'; +import {ComponentFactory, ComponentFactoryResolver, ComponentRef, ErrorHandler, Injector, NgModuleRef, RendererFactory2, Sanitizer, TemplateRef, ViewContainerRef, ɵand as anchorDef, ɵArgumentType as ArgumentType, ɵBindingFlags as BindingFlags, ɵccf as createComponentFactory, ɵdid as directiveDef, ɵeld as elementDef, ɵinitServicesIfNeeded as initServicesIfNeeded, ɵNodeFlags as NodeFlags, ɵted as textDef, ɵvid as viewDef, ɵViewDefinition as ViewDefinition, ɵViewFlags as ViewFlags} from '@angular/core'; import {SafeStyle, ɵDomRendererFactory2 as DomRendererFactory2, ɵDomSanitizerImpl as DomSanitizerImpl} from '@angular/platform-browser'; -import {TreeNode, emptyTree} from '../util'; +import {emptyTree, TreeNode} from '../util'; let trustedEmptyColor: SafeStyle; let trustedGreyColor: SafeStyle; export class TreeComponent { data: TreeNode = emptyTree; - get bgColor() { return this.data.depth % 2 ? trustedEmptyColor : trustedGreyColor; } + get bgColor() { + return this.data.depth % 2 ? trustedEmptyColor : trustedGreyColor; + } } let viewFlags = ViewFlags.None; @@ -120,11 +122,19 @@ export class AppModule implements Injector, NgModuleRef<any> { this.componentFactory.create(Injector.NULL, [], this.componentFactory.selector, this); } - tick() { this.componentRef.changeDetectorRef.detectChanges(); } + tick() { + this.componentRef.changeDetectorRef.detectChanges(); + } - get injector() { return this; } - get componentFactoryResolver(): ComponentFactoryResolver { return null; } - get instance() { return this; } + get injector() { + return this; + } + get componentFactoryResolver(): ComponentFactoryResolver { + return null; + } + get instance() { + return this; + } destroy() {} onDestroy(callback: () => void) {} } diff --git a/modules/benchmarks/src/tree/ng2_static/tree.ts b/modules/benchmarks/src/tree/ng2_static/tree.ts index 2ca2adaf1d184..cf0af17d51230 100644 --- a/modules/benchmarks/src/tree/ng2_static/tree.ts +++ b/modules/benchmarks/src/tree/ng2_static/tree.ts @@ -9,24 +9,25 @@ import {Component, Input, NgModule} from '@angular/core'; import {BrowserModule, DomSanitizer, SafeStyle} from '@angular/platform-browser'; -import {TreeNode, emptyTree, getMaxDepth} from '../util'; +import {emptyTree, getMaxDepth, TreeNode} from '../util'; let trustedEmptyColor: SafeStyle; let trustedGreyColor: SafeStyle; function createTreeComponent(level: number, isLeaf: boolean) { - const nextTreeEl = `tree${level+1}`; + const nextTreeEl = `tree${level + 1}`; let template = `<span [style.backgroundColor]="bgColor"> {{data.value}} </span>`; if (!isLeaf) { - template += - `<${nextTreeEl} [data]='data.right'></${nextTreeEl}><${nextTreeEl} [data]='data.left'></${nextTreeEl}>`; + template += `<${nextTreeEl} [data]='data.right'></${nextTreeEl}><${ + nextTreeEl} [data]='data.left'></${nextTreeEl}>`; } @Component({selector: `tree${level}`, template: template}) class TreeComponent { - @Input() - data: TreeNode; - get bgColor() { return this.data.depth % 2 ? trustedEmptyColor : trustedGreyColor; } + @Input() data: TreeNode; + get bgColor() { + return this.data.depth % 2 ? trustedEmptyColor : trustedGreyColor; + } } return TreeComponent; @@ -34,8 +35,7 @@ function createTreeComponent(level: number, isLeaf: boolean) { @Component({selector: 'tree', template: `<tree0 *ngIf="data.left != null" [data]='data'></tree0>`}) export class RootTreeComponent { - @Input() - data: TreeNode = emptyTree; + @Input() data: TreeNode = emptyTree; } function createModule(): any { diff --git a/modules/benchmarks/src/tree/ng2_switch/tree.ts b/modules/benchmarks/src/tree/ng2_switch/tree.ts index 42164490744a9..24ec4c6df9abd 100644 --- a/modules/benchmarks/src/tree/ng2_switch/tree.ts +++ b/modules/benchmarks/src/tree/ng2_switch/tree.ts @@ -9,7 +9,7 @@ import {Component, Input, NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; -import {TreeNode, emptyTree} from '../util'; +import {emptyTree, TreeNode} from '../util'; @Component({ selector: 'tree', @@ -19,8 +19,7 @@ import {TreeNode, emptyTree} from '../util'; <tree *ngIf='data.right != null' [data]='data.right'></tree><tree *ngIf='data.left != null' [data]='data.left'></tree>` }) export class TreeComponent { - @Input() - data: TreeNode = emptyTree; + @Input() data: TreeNode = emptyTree; } @NgModule({ diff --git a/modules/benchmarks/src/tree/render3/index_aot.ts b/modules/benchmarks/src/tree/render3/index_aot.ts index b9235143140d6..797ccc7ff3976 100644 --- a/modules/benchmarks/src/tree/render3/index_aot.ts +++ b/modules/benchmarks/src/tree/render3/index_aot.ts @@ -7,8 +7,10 @@ */ import {ɵrenderComponent as renderComponent} from '@angular/core'; + import {bindAction, profile} from '../../util'; -import {TreeComponent, createDom, destroyDom, detectChanges} from './tree'; + +import {createDom, destroyDom, detectChanges, TreeComponent} from './tree'; function noop() {} diff --git a/modules/benchmarks/src/tree/render3/tree.ts b/modules/benchmarks/src/tree/render3/tree.ts index a6c77591cefd5..8fa337963d8e8 100644 --- a/modules/benchmarks/src/tree/render3/tree.ts +++ b/modules/benchmarks/src/tree/render3/tree.ts @@ -21,7 +21,7 @@ export function createDom(component: TreeComponent) { ɵdetectChanges(component); } -const numberOfChecksEl = document.getElementById('numberOfChecks') !; +const numberOfChecksEl = document.getElementById('numberOfChecks')!; let detectChangesRuns = 0; export function detectChanges(component: TreeComponent) { for (let i = 0; i < 10; i++) { @@ -42,7 +42,9 @@ export function detectChanges(component: TreeComponent) { }) export class TreeComponent { data: any = emptyTree; - get bgColor() { return this.data.depth % 2 ? '' : 'grey'; } + get bgColor() { + return this.data.depth % 2 ? '' : 'grey'; + } } @NgModule({declarations: [TreeComponent], imports: [CommonModule]}) diff --git a/modules/benchmarks/src/tree/render3_function/index.ts b/modules/benchmarks/src/tree/render3_function/index.ts index 32be3be0b8608..8afd56308d3b4 100644 --- a/modules/benchmarks/src/tree/render3_function/index.ts +++ b/modules/benchmarks/src/tree/render3_function/index.ts @@ -6,11 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵRenderFlags, ɵrenderComponent as renderComponent, ɵɵadvance, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineComponent, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵstyleProp, ɵɵtext, ɵɵtextInterpolate1} from '@angular/core'; +import {ɵrenderComponent as renderComponent, ɵRenderFlags, ɵɵadvance, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineComponent, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵstyleProp, ɵɵtext, ɵɵtextInterpolate1} from '@angular/core'; import {bindAction, profile} from '../../util'; import {createDom, destroyDom, detectChanges} from '../render3/tree'; -import {TreeNode, emptyTree} from '../util'; +import {emptyTree, TreeNode} from '../util'; function noop() {} @@ -26,15 +26,16 @@ export class TreeFunction { selectors: [['tree']], decls: 5, vars: 2, - template: function(rf: ɵRenderFlags, ctx: TreeFunction) { - // bit of a hack - TreeTpl(rf, ctx.data); - }, + template: + function(rf: ɵRenderFlags, ctx: TreeFunction) { + // bit of a hack + TreeTpl(rf, ctx.data); + }, inputs: {data: 'data'} }); } -const TreeFunctionCmpDef = TreeFunction.ɵcmp as{decls: number, vars: number}; +const TreeFunctionCmpDef = TreeFunction.ɵcmp as {decls: number, vars: number}; export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) { if (rf & ɵRenderFlags.Create) { ɵɵelementStart(0, 'tree'); diff --git a/modules/benchmarks/src/tree/tree.e2e-spec.ts b/modules/benchmarks/src/tree/tree.e2e-spec.ts index f86a822568f05..8f9ecfb3d5fa9 100644 --- a/modules/benchmarks/src/tree/tree.e2e-spec.ts +++ b/modules/benchmarks/src/tree/tree.e2e-spec.ts @@ -11,7 +11,7 @@ import {$} from 'protractor'; import {openTreeBenchmark} from './test_utils'; describe('tree benchmark', () => { - it('should work for createDestroy', async() => { + it('should work for createDestroy', async () => { openTreeBenchmark(); await $('#createDom').click(); expect($('#root').getText()).toContain('1'); @@ -19,7 +19,7 @@ describe('tree benchmark', () => { expect(await $('#root').getText()).toEqual(''); }); - it('should work for update', async() => { + it('should work for update', async () => { openTreeBenchmark(); await $('#createDom').click(); await $('#createDom').click(); diff --git a/modules/benchmarks/src/tree/tree.perf-spec.ts b/modules/benchmarks/src/tree/tree.perf-spec.ts index 2e52a2fae88f3..dc703a061826b 100644 --- a/modules/benchmarks/src/tree/tree.perf-spec.ts +++ b/modules/benchmarks/src/tree/tree.perf-spec.ts @@ -10,7 +10,7 @@ import {$} from 'protractor'; import {runTreeBenchmark} from './test_utils'; describe('tree benchmark perf', () => { - it('should work for createOnly', async() => { + it('should work for createOnly', async () => { await runTreeBenchmark({ // This cannot be called "createOnly" because the actual destroy benchmark // has the "createOnly" id already. See: https://github.com/angular/angular/pull/21503 @@ -20,7 +20,7 @@ describe('tree benchmark perf', () => { }); }); - it('should work for destroy', async() => { + it('should work for destroy', async () => { await runTreeBenchmark({ // This is actually a benchmark for destroying the dom, but it has been accidentally // named "createOnly". See https://github.com/angular/angular/pull/21503. @@ -30,7 +30,7 @@ describe('tree benchmark perf', () => { }); }); - it('should work for createDestroy', async() => { + it('should work for createDestroy', async () => { await runTreeBenchmark({ id: 'createDestroy', work: () => { @@ -40,7 +40,7 @@ describe('tree benchmark perf', () => { }); }); - it('should work for update', async() => { + it('should work for update', async () => { await runTreeBenchmark({ id: 'update', work: () => $('#createDom').click(), diff --git a/modules/benchmarks/src/tree/tree_detect_changes.e2e-spec.ts b/modules/benchmarks/src/tree/tree_detect_changes.e2e-spec.ts index d75539af1340f..d6f61653e2385 100644 --- a/modules/benchmarks/src/tree/tree_detect_changes.e2e-spec.ts +++ b/modules/benchmarks/src/tree/tree_detect_changes.e2e-spec.ts @@ -11,7 +11,7 @@ import {$} from 'protractor'; import {openTreeBenchmark} from './test_utils'; describe('tree benchmark detect changes', () => { - it('should work for detectChanges', async() => { + it('should work for detectChanges', async () => { openTreeBenchmark(); await $('#detectChanges').click(); expect($('#numberOfChecks').getText()).toContain('10'); diff --git a/modules/benchmarks/src/tree/tree_detect_changes.perf-spec.ts b/modules/benchmarks/src/tree/tree_detect_changes.perf-spec.ts index 822491e6193fe..49a391f476205 100644 --- a/modules/benchmarks/src/tree/tree_detect_changes.perf-spec.ts +++ b/modules/benchmarks/src/tree/tree_detect_changes.perf-spec.ts @@ -10,7 +10,7 @@ import {$} from 'protractor'; import {runTreeBenchmark} from './test_utils'; describe('tree benchmark detect changes perf', () => { - it('should work for detectChanges', async() => { + it('should work for detectChanges', async () => { await runTreeBenchmark({ id: 'detectChanges', work: () => $('#detectChanges').click(), diff --git a/modules/benchmarks/src/tree/util.ts b/modules/benchmarks/src/tree/util.ts index 4c9dc197898fa..519945df508e3 100644 --- a/modules/benchmarks/src/tree/util.ts +++ b/modules/benchmarks/src/tree/util.ts @@ -16,12 +16,14 @@ export class TreeNode { public value: string, public depth: number, public maxDepth: number, public left: TreeNode|null, public right: TreeNode|null) { this.transitiveChildCount = Math.pow(2, (this.maxDepth - this.depth + 1)) - 1; - this.children = this.left ? [this.left, this.right !] : []; + this.children = this.left ? [this.left, this.right!] : []; } // Needed for Polymer as it does not support ternary nor modulo operator // in expressions - get style(): string { return this.depth % 2 === 0 ? 'background-color: grey' : ''; } + get style(): string { + return this.depth % 2 === 0 ? 'background-color: grey' : ''; + } } let treeCreateCount: number; @@ -78,7 +80,7 @@ export function newArray<T>(size: number, value: T): T[]; export function newArray<T>(size: number, value?: T): T[] { const list: T[] = []; for (let i = 0; i < size; i++) { - list.push(value !); + list.push(value!); } return list; } diff --git a/modules/benchmarks/src/util.ts b/modules/benchmarks/src/util.ts index 099be1664c68b..e5937a64a1fb1 100644 --- a/modules/benchmarks/src/util.ts +++ b/modules/benchmarks/src/util.ts @@ -35,7 +35,7 @@ export function getStringParameter(name: string) { } export function bindAction(selector: string, callback: () => void) { - document.querySelector(selector) !.addEventListener('click', callback); + document.querySelector(selector)!.addEventListener('click', callback); } @@ -66,7 +66,8 @@ function reportProfileResults(durations: number[], count: number) { Number.MAX_SAFE_INTEGER) .toFixed(2); window.console.log( - `Iterations: ${count}; cold time: ${durations[0].toFixed(2)} ms; average time: ${avgDuration} ms / iteration; best time: ${minDuration} ms`); + `Iterations: ${count}; cold time: ${durations[0].toFixed(2)} ms; average time: ${ + avgDuration} ms / iteration; best time: ${minDuration} ms`); } // helper script that will read out the url parameters diff --git a/modules/benchmarks/src/views/views-benchmark.ts b/modules/benchmarks/src/views/views-benchmark.ts index 5984d34480b6e..d6d807c1a629b 100644 --- a/modules/benchmarks/src/views/views-benchmark.ts +++ b/modules/benchmarks/src/views/views-benchmark.ts @@ -20,7 +20,9 @@ export class ViewManipulationDirective { } } - clear() { this._vcRef.clear(); } + clear() { + this._vcRef.clear(); + } } @Component({ @@ -44,9 +46,13 @@ export class ViewsBenchmark { constructor(private _cdRef: ChangeDetectorRef) {} - create(vm: ViewManipulationDirective) { vm.create(1000); } + create(vm: ViewManipulationDirective) { + vm.create(1000); + } - destroy(vm: ViewManipulationDirective) { vm.clear(); } + destroy(vm: ViewManipulationDirective) { + vm.clear(); + } check() { for (let i = 0; i < 10000; i++) { diff --git a/modules/playground/e2e_test/hello_world/hello_world_spec.ts b/modules/playground/e2e_test/hello_world/hello_world_spec.ts index 7e20b09b742f3..2d167fec7436b 100644 --- a/modules/playground/e2e_test/hello_world/hello_world_spec.ts +++ b/modules/playground/e2e_test/hello_world/hello_world_spec.ts @@ -11,7 +11,6 @@ import {browser} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('hello world', function() { - afterEach(verifyNoBrowserErrors); describe('hello world app', function() { @@ -30,7 +29,6 @@ describe('hello world', function() { expect(getComponentText('hello-app', '.greeting')).toEqual('howdy world!'); }); }); - }); function getComponentText(selector: string, innerSelector: string) { diff --git a/modules/playground/e2e_test/http/http_spec.ts b/modules/playground/e2e_test/http/http_spec.ts index 58d4ecb0db8e6..f5a5618b3f198 100644 --- a/modules/playground/e2e_test/http/http_spec.ts +++ b/modules/playground/e2e_test/http/http_spec.ts @@ -11,7 +11,6 @@ import {browser} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('http', function() { - afterEach(verifyNoBrowserErrors); describe('fetching', function() { @@ -25,6 +24,6 @@ describe('http', function() { }); function getComponentText(selector: string, innerSelector: string) { - return browser.executeScript( - `return document.querySelector("${selector}").querySelector("${innerSelector}").textContent.trim()`); + return browser.executeScript(`return document.querySelector("${selector}").querySelector("${ + innerSelector}").textContent.trim()`); } diff --git a/modules/playground/e2e_test/jsonp/jsonp_spec.ts b/modules/playground/e2e_test/jsonp/jsonp_spec.ts index 71c6734ea4ba9..72f1003a5fffd 100644 --- a/modules/playground/e2e_test/jsonp/jsonp_spec.ts +++ b/modules/playground/e2e_test/jsonp/jsonp_spec.ts @@ -11,7 +11,6 @@ import {browser} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('jsonp', function() { - afterEach(verifyNoBrowserErrors); describe('fetching', function() { @@ -25,6 +24,6 @@ describe('jsonp', function() { }); function getComponentText(selector: string, innerSelector: string) { - return browser.executeScript( - `return document.querySelector("${selector}").querySelector("${innerSelector}").textContent.trim()`); + return browser.executeScript(`return document.querySelector("${selector}").querySelector("${ + innerSelector}").textContent.trim()`); } diff --git a/modules/playground/e2e_test/key_events/key_events_spec.ts b/modules/playground/e2e_test/key_events/key_events_spec.ts index f98d087df49c1..898950d8c5036 100644 --- a/modules/playground/e2e_test/key_events/key_events_spec.ts +++ b/modules/playground/e2e_test/key_events/key_events_spec.ts @@ -13,11 +13,12 @@ import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; const Key = protractor.Key; describe('key_events', function() { - const URL = '/'; afterEach(verifyNoBrowserErrors); - beforeEach(() => { browser.get(URL); }); + beforeEach(() => { + browser.get(URL); + }); it('should display correct key names', function() { const firstArea = element.all(by.css('.sample-area')).get(0); @@ -78,5 +79,4 @@ describe('key_events', function() { secondArea.sendKeys(Key.CONTROL, Key.SHIFT, Key.ENTER); expect(secondArea.getText()).toEqual(''); }); - }); diff --git a/modules/playground/e2e_test/model_driven_forms/model_driven_forms_spec.ts b/modules/playground/e2e_test/model_driven_forms/model_driven_forms_spec.ts index 8fd35bea15aed..25c96ee92c0af 100644 --- a/modules/playground/e2e_test/model_driven_forms/model_driven_forms_spec.ts +++ b/modules/playground/e2e_test/model_driven_forms/model_driven_forms_spec.ts @@ -11,7 +11,6 @@ import {browser, by, element} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('Model-Driven Forms', function() { - afterEach(verifyNoBrowserErrors); const URL = '/'; diff --git a/modules/playground/e2e_test/relative_assets/assets_spec.ts b/modules/playground/e2e_test/relative_assets/assets_spec.ts index 1b777c6d2d0a1..eb57cfe5c3323 100644 --- a/modules/playground/e2e_test/relative_assets/assets_spec.ts +++ b/modules/playground/e2e_test/relative_assets/assets_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {$, ExpectedConditions, browser, by, element} from 'protractor'; +import {$, browser, by, element, ExpectedConditions} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; @@ -16,7 +16,6 @@ function waitForElement(selector: string) { } describe('relative assets relative-app', () => { - afterEach(verifyNoBrowserErrors); const URL = '/'; diff --git a/modules/playground/e2e_test/routing/routing_spec.ts b/modules/playground/e2e_test/routing/routing_spec.ts index 9e28ba0d41752..f71e7eed8cf5f 100644 --- a/modules/playground/e2e_test/routing/routing_spec.ts +++ b/modules/playground/e2e_test/routing/routing_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {$, ExpectedConditions, browser, by, element} from 'protractor'; +import {$, browser, by, element, ExpectedConditions} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; @@ -16,7 +16,6 @@ function waitForElement(selector: string) { } describe('routing inbox-app', () => { - afterEach(verifyNoBrowserErrors); describe('index view', () => { diff --git a/modules/playground/e2e_test/svg/svg_spec.ts b/modules/playground/e2e_test/svg/svg_spec.ts index 0c58cab353616..7cbeb29b98b50 100644 --- a/modules/playground/e2e_test/svg/svg_spec.ts +++ b/modules/playground/e2e_test/svg/svg_spec.ts @@ -11,15 +11,15 @@ import {browser, by, element} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('SVG', function() { - const URL = '/'; afterEach(verifyNoBrowserErrors); - beforeEach(() => { browser.get(URL); }); + beforeEach(() => { + browser.get(URL); + }); it('should display SVG component contents', function() { const svgText = element.all(by.css('g text')).get(0); expect(svgText.getText()).toEqual('Hello'); }); - }); diff --git a/modules/playground/e2e_test/template_driven_forms/template_driven_forms_spec.ts b/modules/playground/e2e_test/template_driven_forms/template_driven_forms_spec.ts index b822ad49cd2aa..96f0efc869c05 100644 --- a/modules/playground/e2e_test/template_driven_forms/template_driven_forms_spec.ts +++ b/modules/playground/e2e_test/template_driven_forms/template_driven_forms_spec.ts @@ -11,7 +11,6 @@ import {browser, by, element} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('Template-Driven Forms', function() { - afterEach(verifyNoBrowserErrors); const URL = '/'; diff --git a/modules/playground/e2e_test/web_workers/input/input_spec.ts b/modules/playground/e2e_test/web_workers/input/input_spec.ts index 0659ba5adf8d8..bd73b09ccf81f 100644 --- a/modules/playground/e2e_test/web_workers/input/input_spec.ts +++ b/modules/playground/e2e_test/web_workers/input/input_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ExpectedConditions, browser, by, element, protractor} from 'protractor'; +import {browser, by, element, ExpectedConditions, protractor} from 'protractor'; import {verifyNoBrowserErrors} from '../../../../e2e_util/e2e_util'; diff --git a/modules/playground/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts b/modules/playground/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts index a271f82ce5277..ecb474a84f486 100644 --- a/modules/playground/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts +++ b/modules/playground/e2e_test/web_workers/kitchen_sink/kitchen_sink_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ExpectedConditions, browser, by, element, protractor} from 'protractor'; +import {browser, by, element, ExpectedConditions, protractor} from 'protractor'; import {verifyNoBrowserErrors} from '../../../../e2e_util/e2e_util'; @@ -27,7 +27,6 @@ describe('WebWorkers Kitchen Sink', function() { const elem = element(by.css(selector)); browser.wait(ExpectedConditions.textToBePresentInElement(elem, 'hello world!'), 5000); expect(elem.getText()).toEqual('hello world!'); - }); it('should change greeting', () => { diff --git a/modules/playground/e2e_test/web_workers/message_broker/message_broker_spec.ts b/modules/playground/e2e_test/web_workers/message_broker/message_broker_spec.ts index d5c1ec34ca878..23d59484addad 100644 --- a/modules/playground/e2e_test/web_workers/message_broker/message_broker_spec.ts +++ b/modules/playground/e2e_test/web_workers/message_broker/message_broker_spec.ts @@ -6,14 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {ExpectedConditions, browser, by, element, protractor} from 'protractor'; +import {browser, by, element, ExpectedConditions, protractor} from 'protractor'; import {verifyNoBrowserErrors} from '../../../../e2e_util/e2e_util'; const URL = '/'; describe('MessageBroker', function() { - afterEach(() => { verifyNoBrowserErrors(); browser.ignoreSynchronization = false; diff --git a/modules/playground/e2e_test/web_workers/router/router_spec.ts b/modules/playground/e2e_test/web_workers/router/router_spec.ts index 736e01fc54283..752687c20c9dd 100644 --- a/modules/playground/e2e_test/web_workers/router/router_spec.ts +++ b/modules/playground/e2e_test/web_workers/router/router_spec.ts @@ -69,7 +69,9 @@ describe('WebWorker Router', () => { browser.wait(() => { const deferred = protractor.promise.defer(); const elem = element(by.css(contentSelector)); - elem.getText().then((text: string) => { return deferred.fulfill(text === expected); }); + elem.getText().then((text: string) => { + return deferred.fulfill(text === expected); + }); return deferred.promise; }, 5000); } @@ -77,8 +79,9 @@ describe('WebWorker Router', () => { function waitForUrl(regex: RegExp): void { browser.wait(() => { const deferred = protractor.promise.defer(); - browser.getCurrentUrl().then( - (url: string) => { return deferred.fulfill(url.match(regex) !== null); }); + browser.getCurrentUrl().then((url: string) => { + return deferred.fulfill(url.match(regex) !== null); + }); return deferred.promise; }, 5000); } diff --git a/modules/playground/e2e_test/web_workers/todo/todo_spec.ts b/modules/playground/e2e_test/web_workers/todo/todo_spec.ts index f1a54158663db..2af60cf400fe2 100644 --- a/modules/playground/e2e_test/web_workers/todo/todo_spec.ts +++ b/modules/playground/e2e_test/web_workers/todo/todo_spec.ts @@ -26,7 +26,6 @@ describe('WebWorkers Todo', function() { waitForBootstrap(); expect(element(by.css('#todoapp header')).getText()).toEqual('todos'); }); - }); function waitForBootstrap(): void { diff --git a/modules/playground/e2e_test/zippy_component/zippy_spec.ts b/modules/playground/e2e_test/zippy_component/zippy_spec.ts index e3d7f25043564..6e66e4049be7f 100644 --- a/modules/playground/e2e_test/zippy_component/zippy_spec.ts +++ b/modules/playground/e2e_test/zippy_component/zippy_spec.ts @@ -11,13 +11,14 @@ import {browser, by, element} from 'protractor'; import {verifyNoBrowserErrors} from '../../../e2e_util/e2e_util'; describe('Zippy Component', function() { - afterEach(verifyNoBrowserErrors); describe('zippy', function() { const URL = '/'; - beforeEach(function() { browser.get(URL); }); + beforeEach(function() { + browser.get(URL); + }); it('should change the zippy title depending on it\'s state', function() { const zippyTitle = element(by.css('.zippy__title')); diff --git a/modules/playground/src/animate/app/animate-app.ts b/modules/playground/src/animate/app/animate-app.ts index 273fa5441563a..14f55429b46a8 100644 --- a/modules/playground/src/animate/app/animate-app.ts +++ b/modules/playground/src/animate/app/animate-app.ts @@ -87,7 +87,9 @@ export class AnimateApp { alert(`backgroundAnimation has ${phase} from ${data['fromState']} to ${data['toState']}`); } - get state() { return this._state; } + get state() { + return this._state; + } set state(s) { this._state = s; if (s == 'void') { diff --git a/modules/playground/src/async/index.ts b/modules/playground/src/async/index.ts index d3405212e02c8..5036c61f5e0a0 100644 --- a/modules/playground/src/async/index.ts +++ b/modules/playground/src/async/index.ts @@ -43,7 +43,9 @@ class AsyncApplication { multiTimeoutId: any = null; intervalId: any = null; - increment(): void { this.val1++; } + increment(): void { + this.val1++; + } delayedIncrement(): void { this.cancelDelayedIncrement(); diff --git a/modules/playground/src/gestures/index.ts b/modules/playground/src/gestures/index.ts index 2dbc531eb68ce..56f3c1c21b6cd 100644 --- a/modules/playground/src/gestures/index.ts +++ b/modules/playground/src/gestures/index.ts @@ -16,11 +16,17 @@ class GesturesCmp { pinchScale: number = 1; rotateAngle: number = 0; - onSwipe(event: HammerInput): void { this.swipeDirection = event.deltaX > 0 ? 'right' : 'left'; } + onSwipe(event: HammerInput): void { + this.swipeDirection = event.deltaX > 0 ? 'right' : 'left'; + } - onPinch(event: HammerInput): void { this.pinchScale = event.scale; } + onPinch(event: HammerInput): void { + this.pinchScale = event.scale; + } - onRotate(event: HammerInput): void { this.rotateAngle = event.rotation; } + onRotate(event: HammerInput): void { + this.rotateAngle = event.rotation; + } } @NgModule({declarations: [GesturesCmp], bootstrap: [GesturesCmp], imports: [BrowserModule]}) diff --git a/modules/playground/src/hello_world/index.ts b/modules/playground/src/hello_world/index.ts index 5a449f7f3ec09..c0a37bded689c 100644 --- a/modules/playground/src/hello_world/index.ts +++ b/modules/playground/src/hello_world/index.ts @@ -48,9 +48,13 @@ export class RedDec { export class HelloCmp { greeting: string; - constructor(service: GreetingService) { this.greeting = service.greeting; } + constructor(service: GreetingService) { + this.greeting = service.greeting; + } - changeGreeting(): void { this.greeting = 'howdy'; } + changeGreeting(): void { + this.greeting = 'howdy'; + } } @NgModule({declarations: [HelloCmp, RedDec], bootstrap: [HelloCmp], imports: [BrowserModule]}) diff --git a/modules/playground/src/key_events/index.ts b/modules/playground/src/key_events/index.ts index e73aaf437b14e..ed4c34bf67683 100644 --- a/modules/playground/src/key_events/index.ts +++ b/modules/playground/src/key_events/index.ts @@ -36,7 +36,9 @@ export class KeyEventsApp { event.preventDefault(); } - resetShiftEnter(): void { this.shiftEnter = false; } + resetShiftEnter(): void { + this.shiftEnter = false; + } /** * Get a more readable version of current pressed keys. diff --git a/modules/playground/src/model_driven_forms/index.ts b/modules/playground/src/model_driven_forms/index.ts index b71d22cf76bda..94138296a5b59 100644 --- a/modules/playground/src/model_driven_forms/index.ts +++ b/modules/playground/src/model_driven_forms/index.ts @@ -52,7 +52,9 @@ export class ShowError { controlPath: string; errorTypes: string[]; - constructor(@Host() formDir: FormGroupDirective) { this.formDir = formDir; } + constructor(@Host() formDir: FormGroupDirective) { + this.formDir = formDir; + } get errorMessage(): string { const form: FormGroup = this.formDir.form; diff --git a/modules/playground/src/order_management/index.ts b/modules/playground/src/order_management/index.ts index b83b10b9f1fea..10d933702f668 100644 --- a/modules/playground/src/order_management/index.ts +++ b/modules/playground/src/order_management/index.ts @@ -23,7 +23,9 @@ class OrderItem { public orderItemId: number, public orderId: number, public productName: string, public qty: number, public unitPrice: number) {} - get total(): number { return this.qty * this.unitPrice; } + get total(): number { + return this.qty * this.unitPrice; + } } class Order { @@ -31,8 +33,12 @@ class Order { public orderId: number, public customerName: string, public limit: number, private _dataService: DataService) {} - get items(): OrderItem[] { return this._dataService.itemsFor(this); } - get total(): number { return this.items.map(i => i.total).reduce((a, b) => a + b, 0); } + get items(): OrderItem[] { + return this._dataService.itemsFor(this); + } + get total(): number { + return this.items.map(i => i.total).reduce((a, b) => a + b, 0); + } } @@ -69,7 +75,9 @@ export class DataService { this.orderItems.push(new OrderItem(_nextId++, order.orderId, '', 0, 0)); } - deleteItem(item: OrderItem): void { this.orderItems.splice(this.orderItems.indexOf(item), 1); } + deleteItem(item: OrderItem): void { + this.orderItems.splice(this.orderItems.indexOf(item), 1); + } } @@ -107,8 +115,12 @@ export class DataService { export class OrderListComponent { orders: Order[]; - constructor(private _service: DataService) { this.orders = _service.orders; } - select(order: Order): void { this._service.currentOrder = order; } + constructor(private _service: DataService) { + this.orders = _service.orders; + } + select(order: Order): void { + this._service.currentOrder = order; + } } @@ -141,7 +153,9 @@ export class OrderItemComponent { @Input() item: OrderItem; @Output() delete = new EventEmitter(); - onDelete(): void { this.delete.emit(this.item); } + onDelete(): void { + this.delete.emit(this.item); + } } @Component({ @@ -176,11 +190,17 @@ export class OrderItemComponent { export class OrderDetailsComponent { constructor(private _service: DataService) {} - get order(): Order { return this._service.currentOrder; } + get order(): Order { + return this._service.currentOrder; + } - deleteItem(item: OrderItem): void { this._service.deleteItem(item); } + deleteItem(item: OrderItem): void { + this._service.deleteItem(item); + } - addItem(): void { this._service.addItemForOrder(this.order); } + addItem(): void { + this._service.addItemForOrder(this.order); + } } @Component({ diff --git a/modules/playground/src/person_management/index.ts b/modules/playground/src/person_management/index.ts index d981acad14575..e660952e1b294 100644 --- a/modules/playground/src/person_management/index.ts +++ b/modules/playground/src/person_management/index.ts @@ -35,9 +35,15 @@ class Person { this.personId = _nextId++; } - get age(): number { return 2015 - this.yearOfBirth; } - get fullName(): string { return `${this.firstName} ${this.lastName}`; } - get friendNames(): string { return this.friends.map(f => f.fullName).join(', '); } + get age(): number { + return 2015 - this.yearOfBirth; + } + get fullName(): string { + return `${this.firstName} ${this.lastName}`; + } + get friendNames(): string { + return this.friends.map(f => f.fullName).join(', '); + } } @@ -106,7 +112,9 @@ export class DataService { }) export class FullNameComponent { constructor(private _service: DataService) {} - get person(): Person { return this._service.currentPerson; } + get person(): Person { + return this._service.currentPerson; + } } @Component({ @@ -158,7 +166,9 @@ export class FullNameComponent { }) export class PersonsDetailComponent { constructor(private _service: DataService) {} - get person(): Person { return this._service.currentPerson; } + get person(): Person { + return this._service.currentPerson; + } } @Component({ @@ -179,9 +189,13 @@ export class PersonsDetailComponent { export class PersonsComponent { persons: Person[]; - constructor(private _service: DataService) { this.persons = _service.persons; } + constructor(private _service: DataService) { + this.persons = _service.persons; + } - select(person: Person): void { this._service.currentPerson = person; } + select(person: Person): void { + this._service.currentPerson = person; + } } @@ -199,8 +213,12 @@ export class PersonsComponent { export class PersonManagementApplication { mode: string; - switchToEditName(): void { this.mode = 'editName'; } - switchToPersonList(): void { this.mode = 'personList'; } + switchToEditName(): void { + this.mode = 'editName'; + } + switchToPersonList(): void { + this.mode = 'personList'; + } } @NgModule({ diff --git a/modules/playground/src/routing/app/inbox-app.ts b/modules/playground/src/routing/app/inbox-app.ts index 2d87ef3719da6..4e06ab860192f 100644 --- a/modules/playground/src/routing/app/inbox-app.ts +++ b/modules/playground/src/routing/app/inbox-app.ts @@ -29,7 +29,8 @@ export class InboxRecord { email: string, firstName: string, lastName: string, - date: string, draft?: boolean + date: string, + draft?: boolean } = null) { if (data) { this.setData(data); @@ -43,7 +44,8 @@ export class InboxRecord { email: string, firstName: string, lastName: string, - date: string, draft?: boolean + date: string, + draft?: boolean }) { this.id = record.id; this.subject = record.subject; diff --git a/modules/playground/src/routing/app/inbox-detail.ts b/modules/playground/src/routing/app/inbox-detail.ts index d59ccd0e0c1ab..9b95cadcf0a9d 100644 --- a/modules/playground/src/routing/app/inbox-detail.ts +++ b/modules/playground/src/routing/app/inbox-detail.ts @@ -17,8 +17,11 @@ export class InboxDetailCmp { private ready: boolean = false; constructor(db: DbService, route: ActivatedRoute) { - route.paramMap.forEach( - p => { db.email(p.get('id')).then((data) => { this.record.setData(data); }); }); + route.paramMap.forEach(p => { + db.email(p.get('id')).then((data) => { + this.record.setData(data); + }); + }); } } diff --git a/modules/playground/src/sourcemap/index.ts b/modules/playground/src/sourcemap/index.ts index 2a0622683da9d..80e3b844588d4 100644 --- a/modules/playground/src/sourcemap/index.ts +++ b/modules/playground/src/sourcemap/index.ts @@ -16,7 +16,9 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; <button class="errorButton" (click)="createError()">create error</button>` }) export class ErrorComponent { - createError(): void { throw new Error('Sourcemap test'); } + createError(): void { + throw new Error('Sourcemap test'); + } } @NgModule({declarations: [ErrorComponent], bootstrap: [ErrorComponent], imports: [BrowserModule]}) diff --git a/modules/playground/src/template_driven_forms/index.ts b/modules/playground/src/template_driven_forms/index.ts index e5c316370ba66..59f3f25080a91 100644 --- a/modules/playground/src/template_driven_forms/index.ts +++ b/modules/playground/src/template_driven_forms/index.ts @@ -78,7 +78,9 @@ export class ShowError { controlPath: string; errorTypes: string[]; - constructor(@Host() formDir: NgForm) { this.formDir = formDir; } + constructor(@Host() formDir: NgForm) { + this.formDir = formDir; + } get errorMessage(): string { const form: FormGroup = this.formDir.form; diff --git a/modules/playground/src/todo/app/TodoStore.ts b/modules/playground/src/todo/app/TodoStore.ts index e8bde62ae16ae..5938b9674ca2c 100644 --- a/modules/playground/src/todo/app/TodoStore.ts +++ b/modules/playground/src/todo/app/TodoStore.ts @@ -14,14 +14,18 @@ export abstract class KeyModel { } export class Todo extends KeyModel { - constructor(key: number, public title: string, public completed: boolean) { super(key); } + constructor(key: number, public title: string, public completed: boolean) { + super(key); + } } @Injectable() export class TodoFactory { private _uid: number = 0; - nextUid(): number { return ++this._uid; } + nextUid(): number { + return ++this._uid; + } create(title: string, isCompleted: boolean): Todo { return new Todo(this.nextUid(), title, isCompleted); @@ -33,9 +37,13 @@ export class TodoFactory { export class Store<T extends KeyModel> { list: T[] = []; - add(record: T): void { this.list.push(record); } + add(record: T): void { + this.list.push(record); + } - remove(record: T): void { this.removeBy((item) => item === record); } + remove(record: T): void { + this.removeBy((item) => item === record); + } removeBy(callback: (record: T) => boolean): void { this.list = this.list.filter((record) => !callback(record)); diff --git a/modules/playground/src/todo/index.ts b/modules/playground/src/todo/index.ts index 3e6526cf9488a..04a1d55fabf5e 100644 --- a/modules/playground/src/todo/index.ts +++ b/modules/playground/src/todo/index.ts @@ -23,7 +23,9 @@ export class TodoApp { inputElement.value = ''; } - editTodo(todo: Todo): void { this.todoEdit = todo; } + editTodo(todo: Todo): void { + this.todoEdit = todo; + } doneEditing($event: KeyboardEvent, todo: Todo): void { const which = $event.which; @@ -37,18 +39,28 @@ export class TodoApp { } } - addTodo(newTitle: string): void { this.todoStore.add(this.factory.create(newTitle, false)); } + addTodo(newTitle: string): void { + this.todoStore.add(this.factory.create(newTitle, false)); + } - completeMe(todo: Todo): void { todo.completed = !todo.completed; } + completeMe(todo: Todo): void { + todo.completed = !todo.completed; + } - deleteMe(todo: Todo): void { this.todoStore.remove(todo); } + deleteMe(todo: Todo): void { + this.todoStore.remove(todo); + } toggleAll($event: MouseEvent): void { const isComplete = ($event.target as HTMLInputElement).checked; - this.todoStore.list.forEach((todo: Todo) => { todo.completed = isComplete; }); + this.todoStore.list.forEach((todo: Todo) => { + todo.completed = isComplete; + }); } - clearCompleted(): void { this.todoStore.removeBy((todo: Todo) => todo.completed); } + clearCompleted(): void { + this.todoStore.removeBy((todo: Todo) => todo.completed); + } } @NgModule({declarations: [TodoApp], bootstrap: [TodoApp], imports: [BrowserModule]}) diff --git a/modules/playground/src/upgrade/index.ts b/modules/playground/src/upgrade/index.ts index 7b737ebeab7cb..6a336abc9cc18 100644 --- a/modules/playground/src/upgrade/index.ts +++ b/modules/playground/src/upgrade/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, EventEmitter, Input, NgModule, Output, forwardRef} from '@angular/core'; +import {Component, EventEmitter, forwardRef, Input, NgModule, Output} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {UpgradeAdapter} from '@angular/upgrade'; @@ -30,7 +30,9 @@ const styles = [` const adapter = new UpgradeAdapter(forwardRef(() => Ng2AppModule)); const ng1module = angular.module('myExample', []); -ng1module.controller('Index', function($scope: any) { $scope.name = 'World'; }); +ng1module.controller('Index', function($scope: any) { + $scope.name = 'World'; +}); ng1module.directive('ng1User', function() { return { diff --git a/modules/playground/src/web_workers/input/index_common.ts b/modules/playground/src/web_workers/input/index_common.ts index 6ac81ce050a92..f820e28e52fd0 100644 --- a/modules/playground/src/web_workers/input/index_common.ts +++ b/modules/playground/src/web_workers/input/index_common.ts @@ -26,7 +26,11 @@ export class InputCmp { inputVal = ''; textareaVal = ''; - inputChanged(e: Event) { this.inputVal = (e.target as HTMLInputElement).value; } + inputChanged(e: Event) { + this.inputVal = (e.target as HTMLInputElement).value; + } - textAreaChanged(e: Event) { this.textareaVal = (e.target as HTMLTextAreaElement).value; } + textAreaChanged(e: Event) { + this.textareaVal = (e.target as HTMLTextAreaElement).value; + } } diff --git a/modules/playground/src/web_workers/kitchen_sink/index_common.ts b/modules/playground/src/web_workers/kitchen_sink/index_common.ts index 387c95d5995cf..53d3d5a508298 100644 --- a/modules/playground/src/web_workers/kitchen_sink/index_common.ts +++ b/modules/playground/src/web_workers/kitchen_sink/index_common.ts @@ -49,9 +49,15 @@ export class HelloCmp { greeting: string; lastKey: string = '(none)'; - constructor(service: GreetingService) { this.greeting = service.greeting; } + constructor(service: GreetingService) { + this.greeting = service.greeting; + } - changeGreeting(): void { this.greeting = 'howdy'; } + changeGreeting(): void { + this.greeting = 'howdy'; + } - onKeyDown(event: KeyboardEvent): void { this.lastKey = String.fromCharCode(event.keyCode); } + onKeyDown(event: KeyboardEvent): void { + this.lastKey = String.fromCharCode(event.keyCode); + } } diff --git a/modules/playground/src/web_workers/message_broker/index.ts b/modules/playground/src/web_workers/message_broker/index.ts index 0f6979eecea1c..c00828e9e77a5 100644 --- a/modules/playground/src/web_workers/message_broker/index.ts +++ b/modules/playground/src/web_workers/message_broker/index.ts @@ -7,7 +7,7 @@ */ import {PlatformRef} from '@angular/core'; -import {ClientMessageBrokerFactory, FnArg, SerializerTypes, UiArguments, bootstrapWorkerUi} from '@angular/platform-webworker'; +import {bootstrapWorkerUi, ClientMessageBrokerFactory, FnArg, SerializerTypes, UiArguments} from '@angular/platform-webworker'; const ECHO_CHANNEL = 'ECHO'; diff --git a/modules/playground/src/web_workers/message_broker/index_common.ts b/modules/playground/src/web_workers/message_broker/index_common.ts index 181b5811669c6..b451fcc8df41c 100644 --- a/modules/playground/src/web_workers/message_broker/index_common.ts +++ b/modules/playground/src/web_workers/message_broker/index_common.ts @@ -19,5 +19,7 @@ export class App { 'echo', [SerializerTypes.PRIMITIVE], this._echo, SerializerTypes.PRIMITIVE); } - private _echo(val: string) { return new Promise((res) => res(val)); } + private _echo(val: string) { + return new Promise((res) => res(val)); + } } diff --git a/modules/playground/src/web_workers/router/index.ts b/modules/playground/src/web_workers/router/index.ts index 788553247276a..50e4244c3fe6a 100644 --- a/modules/playground/src/web_workers/router/index.ts +++ b/modules/playground/src/web_workers/router/index.ts @@ -6,6 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {WORKER_UI_LOCATION_PROVIDERS, bootstrapWorkerUi} from '@angular/platform-webworker'; +import {bootstrapWorkerUi, WORKER_UI_LOCATION_PROVIDERS} from '@angular/platform-webworker'; bootstrapWorkerUi('loader.js', WORKER_UI_LOCATION_PROVIDERS); diff --git a/modules/playground/src/web_workers/todo/index_common.ts b/modules/playground/src/web_workers/todo/index_common.ts index 584ae61e86ee4..443557f9b8cc7 100644 --- a/modules/playground/src/web_workers/todo/index_common.ts +++ b/modules/playground/src/web_workers/todo/index_common.ts @@ -36,11 +36,17 @@ export class TodoApp { } } - editTodo(todo: Todo): void { this.todoEdit = todo; } + editTodo(todo: Todo): void { + this.todoEdit = todo; + } - addTodo(newTitle: string): void { this.todoStore.add(this.factory.create(newTitle, false)); } + addTodo(newTitle: string): void { + this.todoStore.add(this.factory.create(newTitle, false)); + } - completeMe(todo: Todo): void { todo.completed = !todo.completed; } + completeMe(todo: Todo): void { + todo.completed = !todo.completed; + } toggleCompleted(): void { this.hideActive = !this.hideActive; @@ -57,12 +63,18 @@ export class TodoApp { this.hideActive = false; } - deleteMe(todo: Todo): void { this.todoStore.remove(todo); } + deleteMe(todo: Todo): void { + this.todoStore.remove(todo); + } toggleAll($event: MouseEvent): void { this.isComplete = !this.isComplete; - this.todoStore.list.forEach((todo: Todo) => { todo.completed = this.isComplete; }); + this.todoStore.list.forEach((todo: Todo) => { + todo.completed = this.isComplete; + }); } - clearCompleted(): void { this.todoStore.removeBy((todo: Todo) => todo.completed); } + clearCompleted(): void { + this.todoStore.removeBy((todo: Todo) => todo.completed); + } } diff --git a/modules/playground/src/web_workers/todo/services/TodoStore.ts b/modules/playground/src/web_workers/todo/services/TodoStore.ts index c889788eb83b8..3486fe7fc131d 100644 --- a/modules/playground/src/web_workers/todo/services/TodoStore.ts +++ b/modules/playground/src/web_workers/todo/services/TodoStore.ts @@ -25,7 +25,9 @@ export class Todo extends KeyModel { export class TodoFactory { private _uid: number = 0; - nextUid(): number { return ++this._uid; } + nextUid(): number { + return ++this._uid; + } create(title: string, isCompleted: boolean): Todo { return new Todo(this.nextUid(), title, isCompleted); @@ -37,9 +39,13 @@ export class TodoFactory { export class Store<T extends KeyModel> { list: T[] = []; - add(record: T): void { this.list.push(record); } + add(record: T): void { + this.list.push(record); + } - remove(record: T): void { this.removeBy((item) => item === record); } + remove(record: T): void { + this.removeBy((item) => item === record); + } removeBy(callback: (record: T) => boolean): void { this.list = this.list.filter((record) => !callback(record)); diff --git a/modules/playground/src/zippy_component/index.ts b/modules/playground/src/zippy_component/index.ts index 00ede4cd6cae4..9d1b5706db338 100644 --- a/modules/playground/src/zippy_component/index.ts +++ b/modules/playground/src/zippy_component/index.ts @@ -26,7 +26,9 @@ import {Zippy} from './app/zippy'; export class ZippyApp { logs: string[] = []; - pushLog(log: string) { this.logs.push(log); } + pushLog(log: string) { + this.logs.push(log); + } } @NgModule({declarations: [ZippyApp, Zippy], bootstrap: [ZippyApp], imports: [BrowserModule]}) diff --git a/package.json b/package.json index 0a20382a83dfb..d1224e2bc3581 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "9.1.0-rc.0", + "version": "10.0.0-next.3", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", @@ -9,7 +9,7 @@ "//engines-comment": "Keep this in sync with /aio/package.json and /aio/tools/examples/shared/package.json", "engines": { "node": ">=10.9.0 <13.0.0", - "yarn": ">=1.21.1 <2" + "yarn": ">=1.22.4 <2" }, "repository": { "type": "git", @@ -22,12 +22,10 @@ "preinstall": "node tools/yarn/check-yarn.js", "postinstall": "node scripts/webdriver-manager-update.js && node --preserve-symlinks --preserve-symlinks-main ./tools/postinstall-patches.js", "check-env": "gulp check-env", - "commitmsg": "node ./scripts/git/commit-msg.js", "test-ivy-aot": "bazelisk test --config=ivy --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot", "test-non-ivy": "bazelisk test --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only", "test-fixme-ivy-aot": "bazelisk test --config=ivy --build_tag_filters=-no-ivy-aot --test_tag_filters=-no-ivy-aot", "list-fixme-ivy-targets": "bazelisk query --output=label 'attr(\"tags\", \"\\[.*fixme-ivy.*\\]\", //...) except kind(\"sh_binary\", //...) except kind(\"devmode_js_sources\", //...)' | sort", - "bazel": "bazelisk", "//circleci-win-comment": "See the test-win circleci job for why these are needed. If they are not needed anymore, remove them.", "circleci-win-ve": "bazelisk test --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only,-browser:chromium-local //packages/compiler-cli/... //tools/ts-api-guardian/...", "circleci-win-ivy": "bazelisk test --config=ivy --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local //packages/compiler-cli/... //tools/ts-api-guardian/...", @@ -35,9 +33,10 @@ "tslint": "tsc -p tools/tsconfig.json && tslint -c tslint.json \"+(packages|modules|scripts|tools)/**/*.+(js|ts)\"", "public-api:check": "node goldens/public-api/manage.js test", "public-api:update": "node goldens/public-api/manage.js accept", - "ts-circular-deps": "ts-node dev-infra/ts-circular-dependencies/index.ts", - "ts-circular-deps:check": "yarn -s ts-circular-deps check ./goldens/packages-circular-deps.json", - "ts-circular-deps:approve": "yarn -s ts-circular-deps approve ./goldens/packages-circular-deps.json" + "ts-circular-deps": "ts-node --transpile-only -- dev-infra/ts-circular-dependencies/index.ts --config ./packages/circular-deps-test.conf.js", + "ts-circular-deps:check": "yarn -s ts-circular-deps check", + "ts-circular-deps:approve": "yarn -s ts-circular-deps approve", + "ng-dev": "ts-node --transpile-only -- dev-infra/cli.ts" }, "// 1": "dependencies are used locally and by bazel", "dependencies": { @@ -51,13 +50,13 @@ "@babel/template": "^7.8.6", "@babel/traverse": "^7.8.6", "@babel/types": "^7.8.6", - "@bazel/jasmine": "1.4.1", - "@bazel/karma": "1.4.1", - "@bazel/protractor": "1.4.1", - "@bazel/rollup": "1.4.1", - "@bazel/terser": "1.4.1", - "@bazel/typescript": "1.4.1", - "@microsoft/api-extractor": "^7.3.9", + "@bazel/jasmine": "1.6.0", + "@bazel/karma": "1.6.0", + "@bazel/protractor": "1.6.0", + "@bazel/rollup": "1.6.0", + "@bazel/terser": "1.6.0", + "@bazel/typescript": "1.6.0", + "@microsoft/api-extractor": "~7.6.0", "@schematics/angular": "9.0.3", "@types/angular": "^1.6.47", "@types/babel__core": "^7.1.6", @@ -72,8 +71,8 @@ "@types/fs-extra": "4.0.2", "@types/hammerjs": "2.0.35", "@types/inquirer": "^0.0.44", - "@types/jasmine": "^2.8.8", - "@types/jasminewd2": "^2.0.6", + "@types/jasmine": "3.5.10", + "@types/jasminewd2": "^2.0.8", "@types/minimist": "^1.2.0", "@types/node": "^12.11.1", "@types/selenium-webdriver": "3.0.7", @@ -105,8 +104,8 @@ "hammerjs": "2.0.8", "http-server": "^0.11.1", "incremental-dom": "0.4.1", - "jasmine": "^3.1.0", - "jasmine-core": "^3.1.0", + "jasmine": "^3.5.0", + "jasmine-core": "^3.5.0", "jquery": "3.0.0", "karma": "~4.1.0", "karma-chrome-launcher": "^2.2.0", @@ -114,7 +113,7 @@ "karma-jasmine": "^2.0.1", "karma-requirejs": "^1.1.0", "karma-sourcemap-loader": "^0.3.7", - "magic-string": "^0.25.0", + "magic-string": "0.25.4", "materialize-css": "1.0.0", "minimatch": "^3.0.4", "minimist": "1.2.0", @@ -140,14 +139,14 @@ "tslib": "^1.10.0", "tslint": "6.0.0", "typescript": "~3.8.3", - "xhr2": "0.1.4", + "xhr2": "0.2.0", "yaml": "^1.7.2", "yargs": "15.3.0" }, "// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.", "devDependencies": { "@angular/cli": "9.0.3", - "@bazel/bazelisk": "^1.3.0", + "@bazel/bazelisk": "^1.4.0", "@bazel/buildifier": "^0.29.0", "@bazel/ibazel": "^0.12.3", "@octokit/graphql": "^4.3.1", @@ -156,7 +155,7 @@ "@yarnpkg/lockfile": "^1.1.0", "browserstacktunnel-wrapper": "2.0.1", "check-side-effects": "0.0.21", - "clang-format": "1.0.41", + "clang-format": "^1.4.0", "cldr": "4.10.0", "cldr-data": "36.0.0", "cldrjs": "0.5.0", @@ -166,12 +165,12 @@ "firefox-profile": "1.0.3", "glob": "7.1.2", "gulp": "3.9.1", - "gulp-clang-format": "1.0.23", + "gulp-clang-format": "^1.0.27", "gulp-conventional-changelog": "^2.0.3", "gulp-filter": "^5.1.0", "gulp-git": "^2.7.0", "gulp-tslint": "8.1.2", - "husky": "^0.14.3", + "husky": "^4.2.3", "jpm": "1.3.1", "json5": "^2.1.2", "karma-browserstack-launcher": "^1.3.0", @@ -196,5 +195,10 @@ "**/graceful-fs": "4.2.2", "**/webdriver-manager": "12.1.7" }, - "cldr-data-coverage": "full" + "cldr-data-coverage": "full", + "husky": { + "hooks": { + "commit-msg": "yarn -s ng-dev commit-message pre-commit-validate --file-env-variable HUSKY_GIT_PARAMS" + } + } } diff --git a/packages/animations/browser/src/dsl/animation.ts b/packages/animations/browser/src/dsl/animation.ts index e21494a980a86..a9ba429ca0048 100644 --- a/packages/animations/browser/src/dsl/animation.ts +++ b/packages/animations/browser/src/dsl/animation.ts @@ -22,7 +22,7 @@ export class Animation { const errors: any[] = []; const ast = buildAnimationAst(_driver, input, errors); if (errors.length) { - const errorMessage = `animation validation failed:\n${errors.join("\n")}`; + const errorMessage = `animation validation failed:\n${errors.join('\n')}`; throw new Error(errorMessage); } this._animationAst = ast; @@ -42,7 +42,7 @@ export class Animation { this._driver, element, this._animationAst, ENTER_CLASSNAME, LEAVE_CLASSNAME, start, dest, options, subInstructions, errors); if (errors.length) { - const errorMessage = `animation building failed:\n${errors.join("\n")}`; + const errorMessage = `animation building failed:\n${errors.join('\n')}`; throw new Error(errorMessage); } return result; diff --git a/packages/animations/browser/src/dsl/animation_ast.ts b/packages/animations/browser/src/dsl/animation_ast.ts index dd5a46363becc..8d57c09f0a5e3 100644 --- a/packages/animations/browser/src/dsl/animation_ast.ts +++ b/packages/animations/browser/src/dsl/animation_ast.ts @@ -74,7 +74,9 @@ export interface StyleAst extends Ast<AnimationMetadataType.Style> { isEmptyStep?: boolean; } -export interface KeyframesAst extends Ast<AnimationMetadataType.Keyframes> { styles: StyleAst[]; } +export interface KeyframesAst extends Ast<AnimationMetadataType.Keyframes> { + styles: StyleAst[]; +} export interface ReferenceAst extends Ast<AnimationMetadataType.Reference> { animation: Ast<AnimationMetadataType>; diff --git a/packages/animations/browser/src/dsl/animation_ast_builder.ts b/packages/animations/browser/src/dsl/animation_ast_builder.ts index e883eb742e065..5184e74589d5d 100644 --- a/packages/animations/browser/src/dsl/animation_ast_builder.ts +++ b/packages/animations/browser/src/dsl/animation_ast_builder.ts @@ -5,11 +5,11 @@ * 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 {AUTO_STYLE, AnimateTimings, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, style, ɵStyleData} from '@angular/animations'; +import {AnimateTimings, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, AUTO_STYLE, style, ɵStyleData} from '@angular/animations'; import {AnimationDriver} from '../render/animation_driver'; import {getOrSetAsInMap} from '../render/shared'; -import {ENTER_SELECTOR, LEAVE_SELECTOR, NG_ANIMATING_SELECTOR, NG_TRIGGER_SELECTOR, SUBSTITUTION_EXPR_START, copyObj, extractStyleParams, iteratorToArray, normalizeAnimationEntry, resolveTiming, validateStyleParams, visitDslNode} from '../util'; +import {copyObj, ENTER_SELECTOR, extractStyleParams, iteratorToArray, LEAVE_SELECTOR, NG_ANIMATING_SELECTOR, NG_TRIGGER_SELECTOR, normalizeAnimationEntry, resolveTiming, SUBSTITUTION_EXPR_START, validateStyleParams, visitDslNode} from '../util'; import {AnimateAst, AnimateChildAst, AnimateRefAst, Ast, DynamicTimingAst, GroupAst, KeyframesAst, QueryAst, ReferenceAst, SequenceAst, StaggerAst, StateAst, StyleAst, TimingAst, TransitionAst, TriggerAst} from './animation_ast'; import {AnimationDslVisitor} from './animation_dsl_visitor'; @@ -55,7 +55,7 @@ const SELF_TOKEN_REGEX = new RegExp(`\s*${SELF_TOKEN}\s*,?`, 'g'); * Otherwise an error will be thrown. */ export function buildAnimationAst( - driver: AnimationDriver, metadata: AnimationMetadata | AnimationMetadata[], + driver: AnimationDriver, metadata: AnimationMetadata|AnimationMetadata[], errors: any[]): Ast<AnimationMetadataType> { return new AnimationAstBuilderVisitor(driver).build(metadata, errors); } @@ -114,7 +114,11 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { return { type: AnimationMetadataType.Trigger, - name: metadata.name, states, transitions, queryCount, depCount, + name: metadata.name, + states, + transitions, + queryCount, + depCount, options: null }; } @@ -139,8 +143,10 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { }); if (missingSubs.size) { const missingSubsArr = iteratorToArray(missingSubs.values()); - context.errors.push( - `state("${metadata.name}", ...) must define default values for all the following style substitutions: ${missingSubsArr.join(', ')}`); + context.errors.push(`state("${ + metadata + .name}", ...) must define default values for all the following style substitutions: ${ + missingSubsArr.join(', ')}`); } } @@ -210,7 +216,7 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { let isEmpty = false; if (!styleMetadata) { isEmpty = true; - const newStyleData: {[prop: string]: string | number} = {}; + const newStyleData: {[prop: string]: string|number} = {}; if (timingAst.easing) { newStyleData['easing'] = timingAst.easing; } @@ -239,9 +245,9 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { private _makeStyleAst(metadata: AnimationStyleMetadata, context: AnimationAstBuilderContext): StyleAst { - const styles: (ɵStyleData | string)[] = []; + const styles: (ɵStyleData|string)[] = []; if (Array.isArray(metadata.styles)) { - (metadata.styles as(ɵStyleData | string)[]).forEach(styleTuple => { + (metadata.styles as (ɵStyleData | string)[]).forEach(styleTuple => { if (typeof styleTuple == 'string') { if (styleTuple == AUTO_STYLE) { styles.push(styleTuple); @@ -282,7 +288,8 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { type: AnimationMetadataType.Style, styles, easing: collectedEasing, - offset: metadata.offset, containsDynamicStyles, + offset: metadata.offset, + containsDynamicStyles, options: null }; } @@ -300,19 +307,22 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { Object.keys(tuple).forEach(prop => { if (!this._driver.validateStyleProperty(prop)) { - context.errors.push( - `The provided animation property "${prop}" is not a supported CSS property for animations`); + context.errors.push(`The provided animation property "${ + prop}" is not a supported CSS property for animations`); return; } - const collectedStyles = context.collectedStyles[context.currentQuerySelector !]; + const collectedStyles = context.collectedStyles[context.currentQuerySelector!]; const collectedEntry = collectedStyles[prop]; let updateCollectedStyle = true; if (collectedEntry) { if (startTime != endTime && startTime >= collectedEntry.startTime && endTime <= collectedEntry.endTime) { - context.errors.push( - `The CSS property "${prop}" that exists between the times of "${collectedEntry.startTime}ms" and "${collectedEntry.endTime}ms" is also being animated in a parallel animation between the times of "${startTime}ms" and "${endTime}ms"`); + context.errors.push(`The CSS property "${prop}" that exists between the times of "${ + collectedEntry.startTime}ms" and "${ + collectedEntry + .endTime}ms" is also being animated in a parallel animation between the times of "${ + startTime}ms" and "${endTime}ms"`); updateCollectedStyle = false; } @@ -383,7 +393,7 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { const limit = length - 1; const currentTime = context.currentTime; - const currentAnimateTimings = context.currentAnimateTimings !; + const currentAnimateTimings = context.currentAnimateTimings!; const animateDuration = currentAnimateTimings.duration; keyframes.forEach((kf, i) => { const offset = generatedOffset > 0 ? (i == limit ? 1 : (generatedOffset * i)) : offsets[i]; @@ -427,7 +437,7 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { } visitQuery(metadata: AnimationQueryMetadata, context: AnimationAstBuilderContext): QueryAst { - const parentSelector = context.currentQuerySelector !; + const parentSelector = context.currentQuerySelector!; const options = (metadata.options || {}) as AnimationQueryOptions; context.queryCount++; @@ -445,7 +455,9 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { type: AnimationMetadataType.Query, selector, limit: options.limit || 0, - optional: !!options.optional, includeSelf, animation, + optional: !!options.optional, + includeSelf, + animation, originalSelector: metadata.selector, options: normalizeAnimationOptions(metadata.options) }; @@ -462,7 +474,8 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { return { type: AnimationMetadataType.Stagger, - animation: visitDslNode(this, normalizeAnimationEntry(metadata.animation), context), timings, + animation: visitDslNode(this, normalizeAnimationEntry(metadata.animation), context), + timings, options: null }; } @@ -483,7 +496,7 @@ function normalizeSelector(selector: string): [string, boolean] { } -function normalizeParams(obj: {[key: string]: any} | any): {[key: string]: any}|null { +function normalizeParams(obj: {[key: string]: any}|any): {[key: string]: any}|null { return obj ? copyObj(obj) : null; } @@ -504,7 +517,7 @@ export class AnimationAstBuilderContext { constructor(public errors: any[]) {} } -function consumeOffset(styles: ɵStyleData | string | (ɵStyleData | string)[]): number|null { +function consumeOffset(styles: ɵStyleData|string|(ɵStyleData | string)[]): number|null { if (typeof styles == 'string') return null; let offset: number|null = null; @@ -529,7 +542,7 @@ function isObject(value: any): boolean { return !Array.isArray(value) && typeof value == 'object'; } -function constructTimingAst(value: string | number | AnimateTimings, errors: any[]) { +function constructTimingAst(value: string|number|AnimateTimings, errors: any[]) { let timings: AnimateTimings|null = null; if (value.hasOwnProperty('duration')) { timings = value as AnimateTimings; @@ -551,11 +564,11 @@ function constructTimingAst(value: string | number | AnimateTimings, errors: any return makeTimingAst(timings.duration, timings.delay, timings.easing); } -function normalizeAnimationOptions(options: AnimationOptions | null): AnimationOptions { +function normalizeAnimationOptions(options: AnimationOptions|null): AnimationOptions { if (options) { options = copyObj(options); if (options['params']) { - options['params'] = normalizeParams(options['params']) !; + options['params'] = normalizeParams(options['params'])!; } } else { options = {}; @@ -563,6 +576,6 @@ function normalizeAnimationOptions(options: AnimationOptions | null): AnimationO return options; } -function makeTimingAst(duration: number, delay: number, easing: string | null): TimingAst { +function makeTimingAst(duration: number, delay: number, easing: string|null): TimingAst { return {duration, delay, easing}; } diff --git a/packages/animations/browser/src/dsl/animation_timeline_builder.ts b/packages/animations/browser/src/dsl/animation_timeline_builder.ts index b9bc5b09202df..7995610117a4a 100644 --- a/packages/animations/browser/src/dsl/animation_timeline_builder.ts +++ b/packages/animations/browser/src/dsl/animation_timeline_builder.ts @@ -5,7 +5,7 @@ * 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 {AUTO_STYLE, AnimateChildOptions, AnimateTimings, AnimationMetadataType, AnimationOptions, AnimationQueryOptions, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; +import {AnimateChildOptions, AnimateTimings, AnimationMetadataType, AnimationOptions, AnimationQueryOptions, AUTO_STYLE, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; import {AnimationDriver} from '../render/animation_driver'; import {copyObj, copyStyles, interpolateParams, iteratorToArray, resolveTiming, resolveTimingValue, visitDslNode} from '../util'; @@ -301,7 +301,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor { visitStyle(ast: StyleAst, context: AnimationTimelineContext) { const timeline = context.currentTimeline; - const timings = context.currentAnimateTimings !; + const timings = context.currentAnimateTimings!; // this is a special case for when a style() call // directly follows an animate() call (but not inside of an animate() call) @@ -320,8 +320,8 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor { } visitKeyframes(ast: KeyframesAst, context: AnimationTimelineContext) { - const currentAnimateTimings = context.currentAnimateTimings !; - const startTime = (context.currentTimeline !).duration; + const currentAnimateTimings = context.currentAnimateTimings!; + const startTime = (context.currentTimeline!).duration; const duration = currentAnimateTimings.duration; const innerContext = context.createSubContext(); const innerTimeline = innerContext.currentTimeline; @@ -351,8 +351,9 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor { const options = (ast.options || {}) as AnimationQueryOptions; const delay = options.delay ? resolveTimingValue(options.delay) : 0; - if (delay && (context.previousNode.type === AnimationMetadataType.Style || - (startTime == 0 && context.currentTimeline.getCurrentStyleProperties().length))) { + if (delay && + (context.previousNode.type === AnimationMetadataType.Style || + (startTime == 0 && context.currentTimeline.getCurrentStyleProperties().length))) { context.currentTimeline.snapshotCurrentStyles(); context.previousNode = DEFAULT_NOOP_PREVIOUS_NODE; } @@ -365,7 +366,6 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor { context.currentQueryTotal = elms.length; let sameElementTimeline: TimelineBuilder|null = null; elms.forEach((element, i) => { - context.currentQueryIndex = i; const innerContext = context.createSubContext(ast.options, element); if (delay) { @@ -400,7 +400,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor { } visitStagger(ast: StaggerAst, context: AnimationTimelineContext) { - const parentContext = context.parentContext !; + const parentContext = context.parentContext!; const tl = context.currentTimeline; const timings = ast.timings; const duration = Math.abs(timings.duration); @@ -460,7 +460,9 @@ export class AnimationTimelineContext { timelines.push(this.currentTimeline); } - get params() { return this.options.params; } + get params() { + return this.options.params; + } updateOptions(options: AnimationOptions|null, skipIfExists?: boolean) { if (!options) return; @@ -479,7 +481,7 @@ export class AnimationTimelineContext { const newParams = newOptions.params; if (newParams) { - let paramsToUpdate: {[name: string]: any} = optionsToUpdate.params !; + let paramsToUpdate: {[name: string]: any} = optionsToUpdate.params!; if (!paramsToUpdate) { paramsToUpdate = this.options.params = {}; } @@ -498,7 +500,9 @@ export class AnimationTimelineContext { const oldParams = this.options.params; if (oldParams) { const params: {[name: string]: any} = options['params'] = {}; - Object.keys(oldParams).forEach(name => { params[name] = oldParams[name]; }); + Object.keys(oldParams).forEach(name => { + params[name] = oldParams[name]; + }); } } return options; @@ -576,8 +580,8 @@ export class AnimationTimelineContext { } if (!optional && results.length == 0) { - errors.push( - `\`query("${originalSelector}")\` returned zero elements. (Use \`query("${originalSelector}", { optional: true })\` if you wish to allow this.)`); + errors.push(`\`query("${originalSelector}")\` returned zero elements. (Use \`query("${ + originalSelector}", { optional: true })\` if you wish to allow this.)`); } return results; } @@ -587,7 +591,7 @@ export class AnimationTimelineContext { export class TimelineBuilder { public duration: number = 0; // TODO(issue/24571): remove '!'. - public easing !: string | null; + public easing!: string|null; private _previousKeyframe: ɵStyleData = {}; private _currentKeyframe: ɵStyleData = {}; private _keyframes = new Map<number, ɵStyleData>(); @@ -606,7 +610,7 @@ export class TimelineBuilder { } this._localTimelineStyles = Object.create(this._backFill, {}); - this._globalTimelineStyles = this._elementTimelineStylesLookup.get(element) !; + this._globalTimelineStyles = this._elementTimelineStylesLookup.get(element)!; if (!this._globalTimelineStyles) { this._globalTimelineStyles = this._localTimelineStyles; this._elementTimelineStylesLookup.set(element, this._localTimelineStyles); @@ -625,9 +629,13 @@ export class TimelineBuilder { } } - getCurrentStyleProperties(): string[] { return Object.keys(this._currentKeyframe); } + getCurrentStyleProperties(): string[] { + return Object.keys(this._currentKeyframe); + } - get currentTime() { return this.startTime + this.duration; } + get currentTime() { + return this.startTime + this.duration; + } delayNextStep(delay: number) { // in the event that a style() step is placed right before a stagger() @@ -656,7 +664,7 @@ export class TimelineBuilder { if (this._currentKeyframe) { this._previousKeyframe = this._currentKeyframe; } - this._currentKeyframe = this._keyframes.get(this.duration) !; + this._currentKeyframe = this._keyframes.get(this.duration)!; if (!this._currentKeyframe) { this._currentKeyframe = Object.create(this._backFill, {}); this._keyframes.set(this.duration, this._currentKeyframe); @@ -680,7 +688,9 @@ export class TimelineBuilder { this._styleSummary[prop] = {time: this.currentTime, value}; } - allowOnlyTimelineStyles() { return this._currentEmptyStepKeyframe !== this._currentKeyframe; } + allowOnlyTimelineStyles() { + return this._currentEmptyStepKeyframe !== this._currentKeyframe; + } applyEmptyStep(easing: string|null) { if (easing) { @@ -748,7 +758,9 @@ export class TimelineBuilder { }); } - getFinalKeyframe() { return this._keyframes.get(this.duration); } + getFinalKeyframe() { + return this._keyframes.get(this.duration); + } get properties() { const properties: string[] = []; @@ -820,7 +832,9 @@ class SubTimelineBuilder extends TimelineBuilder { this.timings = {duration: timings.duration, delay: timings.delay, easing: timings.easing}; } - containsAnimation(): boolean { return this.keyframes.length > 1; } + containsAnimation(): boolean { + return this.keyframes.length > 1; + } buildKeyframes(): AnimationTimelineInstruction { let keyframes = this.keyframes; @@ -883,13 +897,15 @@ function roundOffset(offset: number, decimalPoints = 3): number { return Math.round(offset * mult) / mult; } -function flattenStyles(input: (ɵStyleData | string)[], allStyles: ɵStyleData) { +function flattenStyles(input: (ɵStyleData|string)[], allStyles: ɵStyleData) { const styles: ɵStyleData = {}; let allProperties: string[]; input.forEach(token => { if (token === '*') { allProperties = allProperties || Object.keys(allStyles); - allProperties.forEach(prop => { styles[prop] = AUTO_STYLE; }); + allProperties.forEach(prop => { + styles[prop] = AUTO_STYLE; + }); } else { copyStyles(token as ɵStyleData, false, styles); } diff --git a/packages/animations/browser/src/dsl/animation_timeline_instruction.ts b/packages/animations/browser/src/dsl/animation_timeline_instruction.ts index 4c10d69a3dfd9..13e4cc4d01c4f 100644 --- a/packages/animations/browser/src/dsl/animation_timeline_instruction.ts +++ b/packages/animations/browser/src/dsl/animation_timeline_instruction.ts @@ -23,7 +23,7 @@ export interface AnimationTimelineInstruction extends AnimationEngineInstruction export function createTimelineInstruction( element: any, keyframes: ɵStyleData[], preStyleProps: string[], postStyleProps: string[], - duration: number, delay: number, easing: string | null = null, + duration: number, delay: number, easing: string|null = null, subTimeline: boolean = false): AnimationTimelineInstruction { return { type: AnimationTransitionInstructionType.TimelineAnimation, @@ -33,6 +33,8 @@ export function createTimelineInstruction( postStyleProps, duration, delay, - totalTime: duration + delay, easing, subTimeline + totalTime: duration + delay, + easing, + subTimeline }; } diff --git a/packages/animations/browser/src/dsl/animation_transition_expr.ts b/packages/animations/browser/src/dsl/animation_transition_expr.ts index 120e97bd2b1cd..9fe58d271a3a6 100644 --- a/packages/animations/browser/src/dsl/animation_transition_expr.ts +++ b/packages/animations/browser/src/dsl/animation_transition_expr.ts @@ -10,7 +10,7 @@ export declare type TransitionMatcherFn = (fromState: any, toState: any, element: any, params: {[key: string]: any}) => boolean; export function parseTransitionExpr( - transitionValue: string | TransitionMatcherFn, errors: string[]): TransitionMatcherFn[] { + transitionValue: string|TransitionMatcherFn, errors: string[]): TransitionMatcherFn[] { const expressions: TransitionMatcherFn[] = []; if (typeof transitionValue == 'string') { transitionValue.split(/\s*,\s*/).forEach( diff --git a/packages/animations/browser/src/dsl/animation_transition_factory.ts b/packages/animations/browser/src/dsl/animation_transition_factory.ts index f1499e862b6a8..ab7729fe6b6d6 100644 --- a/packages/animations/browser/src/dsl/animation_transition_factory.ts +++ b/packages/animations/browser/src/dsl/animation_transition_factory.ts @@ -55,13 +55,16 @@ export class AnimationTransitionFactory { const animationOptions = {params: {...transitionAnimationParams, ...nextAnimationParams}}; - const timelines = skipAstBuild ? [] : buildAnimationTimelines( - driver, element, this.ast.animation, enterClassName, - leaveClassName, currentStateStyles, nextStateStyles, - animationOptions, subInstructions, errors); + const timelines = skipAstBuild ? + [] : + buildAnimationTimelines( + driver, element, this.ast.animation, enterClassName, leaveClassName, currentStateStyles, + nextStateStyles, animationOptions, subInstructions, errors); let totalTime = 0; - timelines.forEach(tl => { totalTime = Math.max(tl.duration + tl.delay, totalTime); }); + timelines.forEach(tl => { + totalTime = Math.max(tl.duration + tl.delay, totalTime); + }); if (errors.length) { return createTransitionInstruction( diff --git a/packages/animations/browser/src/dsl/animation_trigger.ts b/packages/animations/browser/src/dsl/animation_trigger.ts index 79d948af9c06b..5e307adc88776 100644 --- a/packages/animations/browser/src/dsl/animation_trigger.ts +++ b/packages/animations/browser/src/dsl/animation_trigger.ts @@ -22,8 +22,8 @@ export function buildTrigger(name: string, ast: TriggerAst): AnimationTrigger { } /** -* @publicApi -*/ + * @publicApi + */ export class AnimationTrigger { public transitionFactories: AnimationTransitionFactory[] = []; public fallbackTransition: AnimationTransitionFactory; @@ -45,7 +45,9 @@ export class AnimationTrigger { this.fallbackTransition = createFallbackTransition(name, this.states); } - get containsQueries() { return this.ast.queryCount > 0; } + get containsQueries() { + return this.ast.queryCount > 0; + } matchTransition(currentState: any, nextState: any, element: any, params: {[key: string]: any}): AnimationTransitionFactory|null { diff --git a/packages/animations/browser/src/dsl/element_instruction_map.ts b/packages/animations/browser/src/dsl/element_instruction_map.ts index ad61a5f35ee39..7d5ca8baac211 100644 --- a/packages/animations/browser/src/dsl/element_instruction_map.ts +++ b/packages/animations/browser/src/dsl/element_instruction_map.ts @@ -28,7 +28,11 @@ export class ElementInstructionMap { existingInstructions.push(...instructions); } - has(element: any): boolean { return this._map.has(element); } + has(element: any): boolean { + return this._map.has(element); + } - clear() { this._map.clear(); } + clear() { + this._map.clear(); + } } diff --git a/packages/animations/browser/src/dsl/style_normalization/animation_style_normalizer.ts b/packages/animations/browser/src/dsl/style_normalization/animation_style_normalizer.ts index ea0161b90810b..3ab5f062575e8 100644 --- a/packages/animations/browser/src/dsl/style_normalization/animation_style_normalizer.ts +++ b/packages/animations/browser/src/dsl/style_normalization/animation_style_normalizer.ts @@ -20,7 +20,9 @@ export abstract class AnimationStyleNormalizer { * @publicApi */ export class NoopAnimationStyleNormalizer { - normalizePropertyName(propertyName: string, errors: string[]): string { return propertyName; } + normalizePropertyName(propertyName: string, errors: string[]): string { + return propertyName; + } normalizeStyleValue( userProvidedProperty: string, normalizedProperty: string, value: string|number, diff --git a/packages/animations/browser/src/private_export.ts b/packages/animations/browser/src/private_export.ts index bcce822afca9d..3773ef5348807 100644 --- a/packages/animations/browser/src/private_export.ts +++ b/packages/animations/browser/src/private_export.ts @@ -13,6 +13,6 @@ export {AnimationEngine as ɵAnimationEngine} from './render/animation_engine_ne export {CssKeyframesDriver as ɵCssKeyframesDriver} from './render/css_keyframes/css_keyframes_driver'; export {CssKeyframesPlayer as ɵCssKeyframesPlayer} from './render/css_keyframes/css_keyframes_player'; export {containsElement as ɵcontainsElement, invokeQuery as ɵinvokeQuery, matchesElement as ɵmatchesElement, validateStyleProperty as ɵvalidateStyleProperty} from './render/shared'; -export {WebAnimationsDriver as ɵWebAnimationsDriver, supportsWebAnimations as ɵsupportsWebAnimations} from './render/web_animations/web_animations_driver'; +export {supportsWebAnimations as ɵsupportsWebAnimations, WebAnimationsDriver as ɵWebAnimationsDriver} from './render/web_animations/web_animations_driver'; export {WebAnimationsPlayer as ɵWebAnimationsPlayer} from './render/web_animations/web_animations_player'; export {allowPreviousPlayerStylesMerge as ɵallowPreviousPlayerStylesMerge} from './util'; diff --git a/packages/animations/browser/src/render/animation_driver.ts b/packages/animations/browser/src/render/animation_driver.ts index 2302b4f40349d..84085e18656b7 100644 --- a/packages/animations/browser/src/render/animation_driver.ts +++ b/packages/animations/browser/src/render/animation_driver.ts @@ -15,13 +15,17 @@ import {containsElement, invokeQuery, matchesElement, validateStyleProperty} fro */ @Injectable() export class NoopAnimationDriver implements AnimationDriver { - validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); } + validateStyleProperty(prop: string): boolean { + return validateStyleProperty(prop); + } matchesElement(element: any, selector: string): boolean { return matchesElement(element, selector); } - containsElement(elm1: any, elm2: any): boolean { return containsElement(elm1, elm2); } + containsElement(elm1: any, elm2: any): boolean { + return containsElement(elm1, elm2); + } query(element: any, selector: string, multi: boolean): any[] { return invokeQuery(element, selector, multi); @@ -32,7 +36,7 @@ export class NoopAnimationDriver implements AnimationDriver { } animate( - element: any, keyframes: {[key: string]: string | number}[], duration: number, delay: number, + element: any, keyframes: {[key: string]: string|number}[], duration: number, delay: number, easing: string, previousPlayers: any[] = [], scrubberAccessRequested?: boolean): AnimationPlayer { return new NoopAnimationPlayer(duration, delay); @@ -56,6 +60,6 @@ export abstract class AnimationDriver { abstract computeStyle(element: any, prop: string, defaultValue?: string): string; abstract animate( - element: any, keyframes: {[key: string]: string | number}[], duration: number, delay: number, + element: any, keyframes: {[key: string]: string|number}[], duration: number, delay: number, easing?: string|null, previousPlayers?: any[], scrubberAccessRequested?: boolean): any; } diff --git a/packages/animations/browser/src/render/animation_engine_instruction.ts b/packages/animations/browser/src/render/animation_engine_instruction.ts index 43c9fcd773ef2..d263df4ffde83 100644 --- a/packages/animations/browser/src/render/animation_engine_instruction.ts +++ b/packages/animations/browser/src/render/animation_engine_instruction.ts @@ -5,6 +5,11 @@ * 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 */ -export const enum AnimationTransitionInstructionType {TransitionAnimation, TimelineAnimation} +export const enum AnimationTransitionInstructionType { + TransitionAnimation, + TimelineAnimation +} -export interface AnimationEngineInstruction { type: AnimationTransitionInstructionType; } +export interface AnimationEngineInstruction { + type: AnimationTransitionInstructionType; +} diff --git a/packages/animations/browser/src/render/animation_engine_next.ts b/packages/animations/browser/src/render/animation_engine_next.ts index fe458b7611772..fa5ec2b5b966a 100644 --- a/packages/animations/browser/src/render/animation_engine_next.ts +++ b/packages/animations/browser/src/render/animation_engine_next.ts @@ -45,8 +45,8 @@ export class AnimationEngine { const ast = buildAnimationAst(this._driver, metadata as AnimationMetadata, errors) as TriggerAst; if (errors.length) { - throw new Error( - `The animation trigger "${name}" has failed to build due to the following errors:\n - ${errors.join("\n - ")}`); + throw new Error(`The animation trigger "${ + name}" has failed to build due to the following errors:\n - ${errors.join('\n - ')}`); } trigger = buildTrigger(name, ast); this._triggerCache[cacheKey] = trigger; @@ -95,12 +95,16 @@ export class AnimationEngine { return this._transitionEngine.listen(namespaceId, element, eventName, eventPhase, callback); } - flush(microtaskId: number = -1): void { this._transitionEngine.flush(microtaskId); } + flush(microtaskId: number = -1): void { + this._transitionEngine.flush(microtaskId); + } get players(): AnimationPlayer[] { return (this._transitionEngine.players as AnimationPlayer[]) .concat(this._timelineEngine.players as AnimationPlayer[]); } - whenRenderingDone(): Promise<any> { return this._transitionEngine.whenRenderingDone(); } + whenRenderingDone(): Promise<any> { + return this._transitionEngine.whenRenderingDone(); + } } diff --git a/packages/animations/browser/src/render/css_keyframes/css_keyframes_driver.ts b/packages/animations/browser/src/render/css_keyframes/css_keyframes_driver.ts index c69ee499c922c..a849111dfb6e5 100644 --- a/packages/animations/browser/src/render/css_keyframes/css_keyframes_driver.ts +++ b/packages/animations/browser/src/render/css_keyframes/css_keyframes_driver.ts @@ -23,13 +23,17 @@ export class CssKeyframesDriver implements AnimationDriver { private readonly _head: any = document.querySelector('head'); private _warningIssued = false; - validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); } + validateStyleProperty(prop: string): boolean { + return validateStyleProperty(prop); + } matchesElement(element: any, selector: string): boolean { return matchesElement(element, selector); } - containsElement(elm1: any, elm2: any): boolean { return containsElement(elm1, elm2); } + containsElement(elm1: any, elm2: any): boolean { + return containsElement(elm1, elm2); + } query(element: any, selector: string, multi: boolean): any[] { return invokeQuery(element, selector, multi); @@ -104,7 +108,7 @@ export class CssKeyframesDriver implements AnimationDriver { const animationName = `${KEYFRAMES_NAME_PREFIX}${this._count++}`; const kfElm = this.buildKeyframeElement(element, animationName, keyframes); - document.querySelector('head') !.appendChild(kfElm); + document.querySelector('head')!.appendChild(kfElm); const specialStyles = packageNonAnimatableStyles(element, keyframes); const player = new CssKeyframesPlayer( @@ -124,8 +128,8 @@ export class CssKeyframesDriver implements AnimationDriver { } } -function flattenKeyframesIntoStyles( - keyframes: null | {[key: string]: any} | {[key: string]: any}[]): {[key: string]: any} { +function flattenKeyframesIntoStyles(keyframes: null|{[key: string]: any}| + {[key: string]: any}[]): {[key: string]: any} { let flatKeyframes: {[key: string]: any} = {}; if (keyframes) { const kfs = Array.isArray(keyframes) ? keyframes : [keyframes]; diff --git a/packages/animations/browser/src/render/css_keyframes/css_keyframes_player.ts b/packages/animations/browser/src/render/css_keyframes/css_keyframes_player.ts index 1a6f302b043f3..114929a8e3038 100644 --- a/packages/animations/browser/src/render/css_keyframes/css_keyframes_player.ts +++ b/packages/animations/browser/src/render/css_keyframes/css_keyframes_player.ts @@ -14,7 +14,12 @@ import {ElementAnimationStyleHandler} from './element_animation_style_handler'; const DEFAULT_FILL_MODE = 'forwards'; const DEFAULT_EASING = 'linear'; -export const enum AnimatorControlState {INITIALIZED = 1, STARTED = 2, FINISHED = 3, DESTROYED = 4} +export const enum AnimatorControlState { + INITIALIZED = 1, + STARTED = 2, + FINISHED = 3, + DESTROYED = 4 +} export class CssKeyframesPlayer implements AnimationPlayer { private _onDoneFns: Function[] = []; @@ -23,10 +28,10 @@ export class CssKeyframesPlayer implements AnimationPlayer { private _started = false; // TODO(issue/24571): remove '!'. - private _styler !: ElementAnimationStyleHandler; + private _styler!: ElementAnimationStyleHandler; // TODO(issue/24571): remove '!'. - public parentPlayer !: AnimationPlayer; + public parentPlayer!: AnimationPlayer; public readonly totalTime: number; public readonly easing: string; public currentSnapshot: {[key: string]: string} = {}; @@ -34,7 +39,7 @@ export class CssKeyframesPlayer implements AnimationPlayer { private _state: AnimatorControlState = 0; constructor( - public readonly element: any, public readonly keyframes: {[key: string]: string | number}[], + public readonly element: any, public readonly keyframes: {[key: string]: string|number}[], public readonly animationName: string, private readonly _duration: number, private readonly _delay: number, easing: string, private readonly _finalStyles: {[key: string]: any}, @@ -44,11 +49,17 @@ export class CssKeyframesPlayer implements AnimationPlayer { this._buildStyler(); } - onStart(fn: () => void): void { this._onStartFns.push(fn); } + onStart(fn: () => void): void { + this._onStartFns.push(fn); + } - onDone(fn: () => void): void { this._onDoneFns.push(fn); } + onDone(fn: () => void): void { + this._onDoneFns.push(fn); + } - onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); } + onDestroy(fn: () => void): void { + this._onDestroyFns.push(fn); + } destroy() { this.init(); @@ -86,11 +97,17 @@ export class CssKeyframesPlayer implements AnimationPlayer { this._flushDoneFns(); } - setPosition(value: number) { this._styler.setPosition(value); } + setPosition(value: number) { + this._styler.setPosition(value); + } - getPosition(): number { return this._styler.getPosition(); } + getPosition(): number { + return this._styler.getPosition(); + } - hasStarted(): boolean { return this._state >= AnimatorControlState.STARTED; } + hasStarted(): boolean { + return this._state >= AnimatorControlState.STARTED; + } init(): void { if (this._state >= AnimatorControlState.INITIALIZED) return; this._state = AnimatorControlState.INITIALIZED; diff --git a/packages/animations/browser/src/render/css_keyframes/direct_style_player.ts b/packages/animations/browser/src/render/css_keyframes/direct_style_player.ts index f4c092d27eb88..6a7ff3a453eec 100644 --- a/packages/animations/browser/src/render/css_keyframes/direct_style_player.ts +++ b/packages/animations/browser/src/render/css_keyframes/direct_style_player.ts @@ -22,7 +22,7 @@ export class DirectStylePlayer extends NoopAnimationPlayer { if (this.__initialized || !this._startingStyles) return; this.__initialized = true; Object.keys(this._styles).forEach(prop => { - this._startingStyles ![prop] = this.element.style[prop]; + this._startingStyles![prop] = this.element.style[prop]; }); super.init(); } @@ -38,7 +38,7 @@ export class DirectStylePlayer extends NoopAnimationPlayer { destroy() { if (!this._startingStyles) return; Object.keys(this._startingStyles).forEach(prop => { - const value = this._startingStyles ![prop]; + const value = this._startingStyles![prop]; if (value) { this.element.style.setProperty(prop, value); } else { diff --git a/packages/animations/browser/src/render/css_keyframes/element_animation_style_handler.ts b/packages/animations/browser/src/render/css_keyframes/element_animation_style_handler.ts index 03e06889d30b2..095c726a38f84 100644 --- a/packages/animations/browser/src/render/css_keyframes/element_animation_style_handler.ts +++ b/packages/animations/browser/src/render/css_keyframes/element_animation_style_handler.ts @@ -28,14 +28,19 @@ export class ElementAnimationStyleHandler { apply() { applyKeyframeAnimation( this._element, - `${this._duration}ms ${this._easing} ${this._delay}ms 1 normal ${this._fillMode} ${this._name}`); + `${this._duration}ms ${this._easing} ${this._delay}ms 1 normal ${this._fillMode} ${ + this._name}`); addRemoveAnimationEvent(this._element, this._eventFn, false); this._startTime = Date.now(); } - pause() { playPauseAnimation(this._element, this._name, 'paused'); } + pause() { + playPauseAnimation(this._element, this._name, 'paused'); + } - resume() { playPauseAnimation(this._element, this._name, 'running'); } + resume() { + playPauseAnimation(this._element, this._name, 'running'); + } setPosition(position: number) { const index = findIndexForAnimation(this._element, this._name); @@ -43,7 +48,9 @@ export class ElementAnimationStyleHandler { setAnimationStyle(this._element, 'Delay', `-${this._position}ms`, index); } - getPosition() { return this._position; } + getPosition() { + return this._position; + } private _handleCallback(event: any) { const timestamp = event._ngTestManualTimestamp || Date.now(); @@ -70,7 +77,7 @@ export class ElementAnimationStyleHandler { } } -function playPauseAnimation(element: any, name: string, status: 'running' | 'paused') { +function playPauseAnimation(element: any, name: string, status: 'running'|'paused') { const index = findIndexForAnimation(element, name); setAnimationStyle(element, 'PlayState', status, index); } diff --git a/packages/animations/browser/src/render/shared.ts b/packages/animations/browser/src/render/shared.ts index e3d6119fad1c0..c8cbe83ddab71 100644 --- a/packages/animations/browser/src/render/shared.ts +++ b/packages/animations/browser/src/render/shared.ts @@ -5,7 +5,7 @@ * 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 {AUTO_STYLE, AnimationEvent, AnimationPlayer, NoopAnimationPlayer, ɵAnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; +import {AnimationEvent, AnimationPlayer, AUTO_STYLE, NoopAnimationPlayer, ɵAnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; import {AnimationStyleNormalizer} from '../../src/dsl/style_normalization/animation_style_normalizer'; import {AnimationDriver} from '../../src/render/animation_driver'; @@ -89,7 +89,7 @@ export function normalizeKeyframes( } export function listenOnPlayer( - player: AnimationPlayer, eventName: string, event: AnimationEvent | undefined, + player: AnimationPlayer, eventName: string, event: AnimationEvent|undefined, callback: (event: any) => any) { switch (eventName) { case 'start': @@ -125,7 +125,7 @@ export function makeAnimationEvent( } export function getOrSetAsInMap( - map: Map<any, any>| {[key: string]: any}, key: any, defaultValue: any) { + map: Map<any, any>|{[key: string]: any}, key: any, defaultValue: any) { let value: any; if (map instanceof Map) { value = map.get(key); @@ -161,7 +161,9 @@ let _query: (element: any, selector: string, multi: boolean) => any[] = const _isNode = isNode(); if (_isNode || typeof Element !== 'undefined') { // this is well supported in all browsers - _contains = (elm1: any, elm2: any) => { return elm1.contains(elm2) as boolean; }; + _contains = (elm1: any, elm2: any) => { + return elm1.contains(elm2) as boolean; + }; _matches = (() => { if (_isNode || Element.prototype.matches) { @@ -203,15 +205,15 @@ let _IS_WEBKIT = false; export function validateStyleProperty(prop: string): boolean { if (!_CACHED_BODY) { _CACHED_BODY = getBodyNode() || {}; - _IS_WEBKIT = _CACHED_BODY !.style ? ('WebkitAppearance' in _CACHED_BODY !.style) : false; + _IS_WEBKIT = _CACHED_BODY!.style ? ('WebkitAppearance' in _CACHED_BODY!.style) : false; } let result = true; - if (_CACHED_BODY !.style && !containsVendorPrefix(prop)) { - result = prop in _CACHED_BODY !.style; + if (_CACHED_BODY!.style && !containsVendorPrefix(prop)) { + result = prop in _CACHED_BODY!.style; if (!result && _IS_WEBKIT) { const camelProp = 'Webkit' + prop.charAt(0).toUpperCase() + prop.substr(1); - result = camelProp in _CACHED_BODY !.style; + result = camelProp in _CACHED_BODY!.style; } } diff --git a/packages/animations/browser/src/render/special_cased_styles.ts b/packages/animations/browser/src/render/special_cased_styles.ts index 546c3df5e8943..e97aadbc855b4 100644 --- a/packages/animations/browser/src/render/special_cased_styles.ts +++ b/packages/animations/browser/src/render/special_cased_styles.ts @@ -19,7 +19,7 @@ import {eraseStyles, setStyles} from '../util'; * @returns an instance of `SpecialCasedStyles` if any special styles are detected otherwise `null` */ export function packageNonAnimatableStyles( - element: any, styles: {[key: string]: any} | {[key: string]: any}[]): SpecialCasedStyles|null { + element: any, styles: {[key: string]: any}|{[key: string]: any}[]): SpecialCasedStyles|null { let startStyles: {[key: string]: any}|null = null; let endStyles: {[key: string]: any}|null = null; if (Array.isArray(styles) && styles.length) { @@ -47,7 +47,7 @@ export class SpecialCasedStyles { static initialStylesByElement = new WeakMap<any, {[key: string]: any}>(); private _state = SpecialCasedStylesState.Pending; - private _initialStyles !: {[key: string]: any}; + private _initialStyles!: {[key: string]: any}; constructor( private _element: any, private _startStyles: {[key: string]: any}|null, diff --git a/packages/animations/browser/src/render/timeline_animation_engine.ts b/packages/animations/browser/src/render/timeline_animation_engine.ts index d83ad50223207..df26094c0a74c 100644 --- a/packages/animations/browser/src/render/timeline_animation_engine.ts +++ b/packages/animations/browser/src/render/timeline_animation_engine.ts @@ -5,7 +5,7 @@ * 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 {AUTO_STYLE, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationPlayer, ɵStyleData} from '@angular/animations'; +import {AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationPlayer, AUTO_STYLE, ɵStyleData} from '@angular/animations'; import {Ast} from '../dsl/animation_ast'; import {buildAnimationAst} from '../dsl/animation_ast_builder'; @@ -34,7 +34,7 @@ export class TimelineAnimationEngine { const ast = buildAnimationAst(this._driver, metadata, errors); if (errors.length) { throw new Error( - `Unable to build the animation due to the following errors: ${errors.join("\n")}`); + `Unable to build the animation due to the following errors: ${errors.join('\n')}`); } else { this._animations[id] = ast; } @@ -71,12 +71,13 @@ export class TimelineAnimationEngine { if (errors.length) { throw new Error( - `Unable to create the animation due to the following errors: ${errors.join("\n")}`); + `Unable to create the animation due to the following errors: ${errors.join('\n')}`); } autoStylesMap.forEach((styles, element) => { - Object.keys(styles).forEach( - prop => { styles[prop] = this._driver.computeStyle(element, prop, AUTO_STYLE); }); + Object.keys(styles).forEach(prop => { + styles[prop] = this._driver.computeStyle(element, prop, AUTO_STYLE); + }); }); const players = instructions.map(i => { diff --git a/packages/animations/browser/src/render/transition_animation_engine.ts b/packages/animations/browser/src/render/transition_animation_engine.ts index f3ef0f80bc098..236f1e31808eb 100644 --- a/packages/animations/browser/src/render/transition_animation_engine.ts +++ b/packages/animations/browser/src/render/transition_animation_engine.ts @@ -5,7 +5,7 @@ * 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 {AUTO_STYLE, AnimationOptions, AnimationPlayer, NoopAnimationPlayer, ɵAnimationGroupPlayer as AnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; +import {AnimationOptions, AnimationPlayer, AUTO_STYLE, NoopAnimationPlayer, ɵAnimationGroupPlayer as AnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; import {AnimationTimelineInstruction} from '../dsl/animation_timeline_instruction'; import {AnimationTransitionFactory} from '../dsl/animation_transition_factory'; @@ -13,7 +13,7 @@ import {AnimationTransitionInstruction} from '../dsl/animation_transition_instru import {AnimationTrigger} from '../dsl/animation_trigger'; import {ElementInstructionMap} from '../dsl/element_instruction_map'; import {AnimationStyleNormalizer} from '../dsl/style_normalization/animation_style_normalizer'; -import {ENTER_CLASSNAME, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_SELECTOR, NG_TRIGGER_CLASSNAME, NG_TRIGGER_SELECTOR, copyObj, eraseStyles, iteratorToArray, setStyles} from '../util'; +import {copyObj, ENTER_CLASSNAME, eraseStyles, iteratorToArray, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_SELECTOR, NG_TRIGGER_CLASSNAME, NG_TRIGGER_SELECTOR, setStyles} from '../util'; import {AnimationDriver} from './animation_driver'; import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared'; @@ -71,7 +71,9 @@ export class StateValue { public value: string; public options: AnimationOptions; - get params(): {[key: string]: any} { return this.options.params as{[key: string]: any}; } + get params(): {[key: string]: any} { + return this.options.params as {[key: string]: any}; + } constructor(input: any, public namespaceId: string = '') { const isObj = input && input.hasOwnProperty('value'); @@ -92,7 +94,7 @@ export class StateValue { absorbOptions(options: AnimationOptions) { const newParams = options.params; if (newParams) { - const oldParams = this.options.params !; + const oldParams = this.options.params!; Object.keys(newParams).forEach(prop => { if (oldParams[prop] == null) { oldParams[prop] = newParams[prop]; @@ -263,7 +265,9 @@ export class AnimationTransitionNamespace { if (!isFallbackTransition) { addClass(element, QUEUED_CLASSNAME); - player.onStart(() => { removeClass(element, QUEUED_CLASSNAME); }); + player.onStart(() => { + removeClass(element, QUEUED_CLASSNAME); + }); } player.onDone(() => { @@ -290,11 +294,14 @@ export class AnimationTransitionNamespace { deregister(name: string) { delete this._triggers[name]; - this._engine.statesByElement.forEach((stateMap, element) => { delete stateMap[name]; }); + this._engine.statesByElement.forEach((stateMap, element) => { + delete stateMap[name]; + }); this._elementListeners.forEach((listeners, element) => { - this._elementListeners.set( - element, listeners.filter(entry => { return entry.name != name; })); + this._elementListeners.set(element, listeners.filter(entry => { + return entry.name != name; + })); }); } @@ -372,7 +379,7 @@ export class AnimationTransitionNamespace { const trigger = this._triggers[triggerName]; const transition = trigger.fallbackTransition; - const elementStates = this._engine.statesByElement.get(element) !; + const elementStates = this._engine.statesByElement.get(element)!; const fromState = elementStates[triggerName] || DEFAULT_STATE_VALUE; const toState = new StateValue(VOID_VALUE); const player = new TransitionAnimationPlayer(this.id, triggerName, element); @@ -448,7 +455,9 @@ export class AnimationTransitionNamespace { } } - insertNode(element: any, parent: any): void { addClass(element, this._hostClassName); } + insertNode(element: any, parent: any): void { + addClass(element, this._hostClassName); + } drainQueuedTransitions(microtaskId: number): QueueInstruction[] { const instructions: QueueInstruction[] = []; @@ -538,7 +547,9 @@ export class TransitionAnimationEngine { public onRemovalComplete = (element: any, context: any) => {}; /** @internal */ - _onRemovalComplete(element: any, context: any) { this.onRemovalComplete(element, context); } + _onRemovalComplete(element: any, context: any) { + this.onRemovalComplete(element, context); + } constructor( public bodyNode: any, public driver: AnimationDriver, @@ -631,7 +642,9 @@ export class TransitionAnimationEngine { this.afterFlushAnimationsDone(() => ns.destroy(context)); } - private _fetchNamespace(id: string) { return this._namespaceLookup[id]; } + private _fetchNamespace(id: string) { + return this._namespaceLookup[id]; + } fetchNamespacesByElement(element: any): Set<AnimationTransitionNamespace> { // normally there should only be one namespace per element, however @@ -704,7 +717,9 @@ export class TransitionAnimationEngine { } } - collectEnterElement(element: any) { this.collectedEnterElements.push(element); } + collectEnterElement(element: any) { + this.collectedEnterElements.push(element); + } markElementAsDisabled(element: any, value: boolean) { if (value) { @@ -740,11 +755,8 @@ export class TransitionAnimationEngine { markElementAsRemoved(namespaceId: string, element: any, hasAnimation?: boolean, context?: any) { this.collectedLeaveElements.push(element); - element[REMOVAL_FLAG] = { - namespaceId, - setForRemoval: context, hasAnimation, - removedBeforeQueried: false - }; + element[REMOVAL_FLAG] = + {namespaceId, setForRemoval: context, hasAnimation, removedBeforeQueried: false}; } listen( @@ -876,7 +888,9 @@ export class TransitionAnimationEngine { this._whenQuietFns = []; if (players.length) { - optimizeGroupPlayer(players).onDone(() => { quietFns.forEach(fn => fn()); }); + optimizeGroupPlayer(players).onDone(() => { + quietFns.forEach(fn => fn()); + }); } else { quietFns.forEach(fn => fn()); } @@ -950,16 +964,18 @@ export class TransitionAnimationEngine { cleanupFns.push(() => { enterNodeMap.forEach((nodes, root) => { - const className = enterNodeMapIds.get(root) !; + const className = enterNodeMapIds.get(root)!; nodes.forEach(node => removeClass(node, className)); }); leaveNodeMap.forEach((nodes, root) => { - const className = leaveNodeMapIds.get(root) !; + const className = leaveNodeMapIds.get(root)!; nodes.forEach(node => removeClass(node, className)); }); - allLeaveNodes.forEach(element => { this.processLeaveNode(element); }); + allLeaveNodes.forEach(element => { + this.processLeaveNode(element); + }); }); const allPlayers: TransitionAnimationPlayer[] = []; @@ -981,10 +997,10 @@ export class TransitionAnimationEngine { } const nodeIsOrphaned = !bodyNode || !this.driver.containsElement(bodyNode, element); - const leaveClassName = leaveNodeMapIds.get(element) !; - const enterClassName = enterNodeMapIds.get(element) !; + const leaveClassName = leaveNodeMapIds.get(element)!; + const enterClassName = enterNodeMapIds.get(element)!; const instruction = this._buildInstruction( - entry, subTimelines, enterClassName, leaveClassName, nodeIsOrphaned) !; + entry, subTimelines, enterClassName, leaveClassName, nodeIsOrphaned)!; if (instruction.errors && instruction.errors.length) { erroneousTransitions.push(instruction); return; @@ -1029,7 +1045,7 @@ export class TransitionAnimationEngine { instruction.preStyleProps.forEach((stringMap, element) => { const props = Object.keys(stringMap); if (props.length) { - let setVal: Set<string> = allPreStyleElements.get(element) !; + let setVal: Set<string> = allPreStyleElements.get(element)!; if (!setVal) { allPreStyleElements.set(element, setVal = new Set<string>()); } @@ -1039,7 +1055,7 @@ export class TransitionAnimationEngine { instruction.postStyleProps.forEach((stringMap, element) => { const props = Object.keys(stringMap); - let setVal: Set<string> = allPostStyleElements.get(element) !; + let setVal: Set<string> = allPostStyleElements.get(element)!; if (!setVal) { allPostStyleElements.set(element, setVal = new Set<string>()); } @@ -1052,7 +1068,7 @@ export class TransitionAnimationEngine { const errors: string[] = []; erroneousTransitions.forEach(instruction => { errors.push(`@${instruction.triggerName} has failed due to:\n`); - instruction.errors !.forEach(error => errors.push(`- ${error}\n`)); + instruction.errors!.forEach(error => errors.push(`- ${error}\n`)); }); allPlayers.forEach(player => player.destroy()); @@ -1116,7 +1132,7 @@ export class TransitionAnimationEngine { replaceNodes.forEach(node => { const post = postStylesMap.get(node); const pre = preStylesMap.get(node); - postStylesMap.set(node, { ...post, ...pre } as any); + postStylesMap.set(node, {...post, ...pre} as any); }); const rootPlayers: TransitionAnimationPlayer[] = []; @@ -1274,9 +1290,13 @@ export class TransitionAnimationEngine { return this._fetchNamespace(namespaceId).elementContainsData(element) || containsData; } - afterFlush(callback: () => any) { this._flushFns.push(callback); } + afterFlush(callback: () => any) { + this._flushFns.push(callback); + } - afterFlushAnimationsDone(callback: () => any) { this._whenQuietFns.push(callback); } + afterFlushAnimationsDone(callback: () => any) { + this._whenQuietFns.push(callback); + } private _getPreviousPlayers( element: string, isQueriedElement: boolean, namespaceId?: string, triggerName?: string, @@ -1413,8 +1433,9 @@ export class TransitionAnimationEngine { // this basically makes all of the callbacks for sub element animations // be dependent on the upper players for when they finish - allSubElements.forEach( - element => { getOrSetAsInMap(skippedPlayersMap, element, []).push(player); }); + allSubElements.forEach(element => { + getOrSetAsInMap(skippedPlayersMap, element, []).push(player); + }); return player; } @@ -1441,7 +1462,7 @@ export class TransitionAnimationPlayer implements AnimationPlayer { private _queuedCallbacks: {[name: string]: (() => any)[]} = {}; public readonly destroyed = false; // TODO(issue/24571): remove '!'. - public parentPlayer !: AnimationPlayer; + public parentPlayer!: AnimationPlayer; public markedForDestroy: boolean = false; public disabled = false; @@ -1462,17 +1483,21 @@ export class TransitionAnimationPlayer implements AnimationPlayer { this._queuedCallbacks = {}; this._containsRealPlayer = true; this.overrideTotalTime(player.totalTime); - (this as{queued: boolean}).queued = false; + (this as {queued: boolean}).queued = false; } - getRealPlayer() { return this._player; } + getRealPlayer() { + return this._player; + } - overrideTotalTime(totalTime: number) { (this as any).totalTime = totalTime; } + overrideTotalTime(totalTime: number) { + (this as any).totalTime = totalTime; + } syncPlayerEvents(player: AnimationPlayer) { const p = this._player as any; if (p.triggerCallback) { - player.onStart(() => p.triggerCallback !('start')); + player.onStart(() => p.triggerCallback!('start')); } player.onDone(() => this.finish()); player.onDestroy(() => this.destroy()); @@ -1503,24 +1528,38 @@ export class TransitionAnimationPlayer implements AnimationPlayer { this._player.onDestroy(fn); } - init(): void { this._player.init(); } + init(): void { + this._player.init(); + } - hasStarted(): boolean { return this.queued ? false : this._player.hasStarted(); } + hasStarted(): boolean { + return this.queued ? false : this._player.hasStarted(); + } - play(): void { !this.queued && this._player.play(); } + play(): void { + !this.queued && this._player.play(); + } - pause(): void { !this.queued && this._player.pause(); } + pause(): void { + !this.queued && this._player.pause(); + } - restart(): void { !this.queued && this._player.restart(); } + restart(): void { + !this.queued && this._player.restart(); + } - finish(): void { this._player.finish(); } + finish(): void { + this._player.finish(); + } destroy(): void { - (this as{destroyed: boolean}).destroyed = true; + (this as {destroyed: boolean}).destroyed = true; this._player.destroy(); } - reset(): void { !this.queued && this._player.reset(); } + reset(): void { + !this.queued && this._player.reset(); + } setPosition(p: any): void { if (!this.queued) { @@ -1528,7 +1567,9 @@ export class TransitionAnimationPlayer implements AnimationPlayer { } } - getPosition(): number { return this.queued ? 0 : this._player.getPosition(); } + getPosition(): number { + return this.queued ? 0 : this._player.getPosition(); + } /** @internal */ triggerCallback(phaseName: string): void { @@ -1539,7 +1580,7 @@ export class TransitionAnimationPlayer implements AnimationPlayer { } } -function deleteOrUnsetInMap(map: Map<any, any[]>| {[key: string]: any}, key: any, value: any) { +function deleteOrUnsetInMap(map: Map<any, any[]>|{[key: string]: any}, key: any, value: any) { let currentValues: any[]|null|undefined; if (map instanceof Map) { currentValues = map.get(key); @@ -1661,7 +1702,7 @@ function buildRootMap(roots: any[], nodes: any[]): Map<any, any[]> { nodes.forEach(node => { const root = getRoot(node); if (root !== NULL_NODE) { - rootMap.get(root) !.push(node); + rootMap.get(root)!.push(node); } }); @@ -1742,7 +1783,7 @@ function replacePostStylesAsPre( let preEntry = allPreStyleElements.get(element); if (preEntry) { - postEntry.forEach(data => preEntry !.add(data)); + postEntry.forEach(data => preEntry!.add(data)); } else { allPreStyleElements.set(element, postEntry); } diff --git a/packages/animations/browser/src/render/web_animations/web_animations_driver.ts b/packages/animations/browser/src/render/web_animations/web_animations_driver.ts index 29cd26e376ea2..00657d309abda 100644 --- a/packages/animations/browser/src/render/web_animations/web_animations_driver.ts +++ b/packages/animations/browser/src/render/web_animations/web_animations_driver.ts @@ -19,13 +19,17 @@ export class WebAnimationsDriver implements AnimationDriver { private _isNativeImpl = /\{\s*\[native\s+code\]\s*\}/.test(getElementAnimateFn().toString()); private _cssKeyframesDriver = new CssKeyframesDriver(); - validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); } + validateStyleProperty(prop: string): boolean { + return validateStyleProperty(prop); + } matchesElement(element: any, selector: string): boolean { return matchesElement(element, selector); } - containsElement(elm1: any, elm2: any): boolean { return containsElement(elm1, elm2); } + containsElement(elm1: any, elm2: any): boolean { + return containsElement(elm1, elm2); + } query(element: any, selector: string, multi: boolean): any[] { return invokeQuery(element, selector, multi); @@ -35,7 +39,9 @@ export class WebAnimationsDriver implements AnimationDriver { return (window.getComputedStyle(element) as any)[prop] as string; } - overrideWebAnimationsSupport(supported: boolean) { this._isNativeImpl = supported; } + overrideWebAnimationsSupport(supported: boolean) { + this._isNativeImpl = supported; + } animate( element: any, keyframes: ɵStyleData[], duration: number, delay: number, easing: string, @@ -47,7 +53,7 @@ export class WebAnimationsDriver implements AnimationDriver { } const fill = delay == 0 ? 'both' : 'forwards'; - const playerOptions: {[key: string]: string | number} = {duration, delay, fill}; + const playerOptions: {[key: string]: string|number} = {duration, delay, fill}; // we check for this to avoid having a null|undefined value be present // for the easing (which results in an error for certain browsers #9752) if (easing) { diff --git a/packages/animations/browser/src/render/web_animations/web_animations_player.ts b/packages/animations/browser/src/render/web_animations/web_animations_player.ts index 76c2cd44bed34..025fff00c490e 100644 --- a/packages/animations/browser/src/render/web_animations/web_animations_player.ts +++ b/packages/animations/browser/src/render/web_animations/web_animations_player.ts @@ -23,18 +23,18 @@ export class WebAnimationsPlayer implements AnimationPlayer { private _started = false; private _destroyed = false; // TODO(issue/24571): remove '!'. - private _finalKeyframe !: {[key: string]: string | number}; + private _finalKeyframe!: {[key: string]: string|number}; // TODO(issue/24571): remove '!'. - public readonly domPlayer !: DOMAnimation; + public readonly domPlayer!: DOMAnimation; public time = 0; public parentPlayer: AnimationPlayer|null = null; - public currentSnapshot: {[styleName: string]: string | number} = {}; + public currentSnapshot: {[styleName: string]: string|number} = {}; constructor( - public element: any, public keyframes: {[key: string]: string | number}[], - public options: {[key: string]: string | number}, + public element: any, public keyframes: {[key: string]: string|number}[], + public options: {[key: string]: string|number}, private _specialStyles?: SpecialCasedStyles|null) { this._duration = <number>options['duration']; this._delay = <number>options['delay'] || 0; @@ -59,7 +59,7 @@ export class WebAnimationsPlayer implements AnimationPlayer { this._initialized = true; const keyframes = this.keyframes; - (this as{domPlayer: DOMAnimation}).domPlayer = + (this as {domPlayer: DOMAnimation}).domPlayer = this._triggerWebAnimation(this.element, keyframes, this.options); this._finalKeyframe = keyframes.length ? keyframes[keyframes.length - 1] : {}; this.domPlayer.addEventListener('finish', () => this._onFinish()); @@ -81,11 +81,17 @@ export class WebAnimationsPlayer implements AnimationPlayer { return element['animate'](keyframes, options) as DOMAnimation; } - onStart(fn: () => void): void { this._onStartFns.push(fn); } + onStart(fn: () => void): void { + this._onStartFns.push(fn); + } - onDone(fn: () => void): void { this._onDoneFns.push(fn); } + onDone(fn: () => void): void { + this._onDoneFns.push(fn); + } - onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); } + onDestroy(fn: () => void): void { + this._onDestroyFns.push(fn); + } play(): void { this._buildPlayer(); @@ -132,7 +138,9 @@ export class WebAnimationsPlayer implements AnimationPlayer { this.play(); } - hasStarted(): boolean { return this._started; } + hasStarted(): boolean { + return this._started; + } destroy(): void { if (!this._destroyed) { @@ -147,14 +155,20 @@ export class WebAnimationsPlayer implements AnimationPlayer { } } - setPosition(p: number): void { this.domPlayer.currentTime = p * this.time; } + setPosition(p: number): void { + this.domPlayer.currentTime = p * this.time; + } - getPosition(): number { return this.domPlayer.currentTime / this.time; } + getPosition(): number { + return this.domPlayer.currentTime / this.time; + } - get totalTime(): number { return this._delay + this._duration; } + get totalTime(): number { + return this._delay + this._duration; + } beforeDestroy() { - const styles: {[key: string]: string | number} = {}; + const styles: {[key: string]: string|number} = {}; if (this.hasStarted()) { Object.keys(this._finalKeyframe).forEach(prop => { if (prop != 'offset') { diff --git a/packages/animations/browser/src/util.ts b/packages/animations/browser/src/util.ts index ffcc2a7567d1d..aad7d53ae104b 100644 --- a/packages/animations/browser/src/util.ts +++ b/packages/animations/browser/src/util.ts @@ -23,7 +23,7 @@ export const NG_TRIGGER_SELECTOR = '.ng-trigger'; export const NG_ANIMATING_CLASSNAME = 'ng-animating'; export const NG_ANIMATING_SELECTOR = '.ng-animating'; -export function resolveTimingValue(value: string | number) { +export function resolveTimingValue(value: string|number) { if (typeof value == 'number') return value; const matches = value.match(/^(-?[\.\d]+)(m?s)/); @@ -42,14 +42,14 @@ function _convertTimeValueToMS(value: number, unit: string): number { } export function resolveTiming( - timings: string | number | AnimateTimings, errors: any[], allowNegativeValues?: boolean) { + timings: string|number|AnimateTimings, errors: any[], allowNegativeValues?: boolean) { return timings.hasOwnProperty('duration') ? <AnimateTimings>timings : parseTimeExpression(<string|number>timings, errors, allowNegativeValues); } function parseTimeExpression( - exp: string | number, errors: string[], allowNegativeValues?: boolean): AnimateTimings { + exp: string|number, errors: string[], allowNegativeValues?: boolean): AnimateTimings { const regex = /^(-?[\.\d]+)(m?s)(?:\s+(-?[\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?$/i; let duration: number; let delay: number = 0; @@ -97,11 +97,13 @@ function parseTimeExpression( export function copyObj( obj: {[key: string]: any}, destination: {[key: string]: any} = {}): {[key: string]: any} { - Object.keys(obj).forEach(prop => { destination[prop] = obj[prop]; }); + Object.keys(obj).forEach(prop => { + destination[prop] = obj[prop]; + }); return destination; } -export function normalizeStyles(styles: ɵStyleData | ɵStyleData[]): ɵStyleData { +export function normalizeStyles(styles: ɵStyleData|ɵStyleData[]): ɵStyleData { const normalizedStyles: ɵStyleData = {}; if (Array.isArray(styles)) { styles.forEach(data => copyStyles(data, false, normalizedStyles)); @@ -186,8 +188,8 @@ export function eraseStyles(element: any, styles: ɵStyleData) { } } -export function normalizeAnimationEntry(steps: AnimationMetadata | AnimationMetadata[]): - AnimationMetadata { +export function normalizeAnimationEntry(steps: AnimationMetadata| + AnimationMetadata[]): AnimationMetadata { if (Array.isArray(steps)) { if (steps.length == 1) return steps[0]; return sequence(steps); @@ -196,7 +198,7 @@ export function normalizeAnimationEntry(steps: AnimationMetadata | AnimationMeta } export function validateStyleParams( - value: string | number, options: AnimationOptions, errors: any[]) { + value: string|number, options: AnimationOptions, errors: any[]) { const params = options.params || {}; const matches = extractStyleParams(value); if (matches.length) { @@ -211,7 +213,7 @@ export function validateStyleParams( const PARAM_REGEX = new RegExp(`${SUBSTITUTION_EXPR_START}\\s*(.+?)\\s*${SUBSTITUTION_EXPR_END}`, 'g'); -export function extractStyleParams(value: string | number): string[] { +export function extractStyleParams(value: string|number): string[] { let params: string[] = []; if (typeof value === 'string') { let match: any; @@ -224,7 +226,7 @@ export function extractStyleParams(value: string | number): string[] { } export function interpolateParams( - value: string | number, params: {[name: string]: any}, errors: any[]): string|number { + value: string|number, params: {[name: string]: any}, errors: any[]): string|number { const original = value.toString(); const str = original.replace(PARAM_REGEX, (_, varName) => { let localVal = params[varName]; @@ -297,7 +299,9 @@ export function balancePreviousStylesIntoKeyframes( // tslint:disable-next-line for (var i = 1; i < keyframes.length; i++) { let kf = keyframes[i]; - missingStyleProps.forEach(function(prop) { kf[prop] = computeStyle(element, prop); }); + missingStyleProps.forEach(function(prop) { + kf[prop] = computeStyle(element, prop); + }); } } } diff --git a/packages/animations/browser/test/dsl/animation_spec.ts b/packages/animations/browser/test/dsl/animation_spec.ts index 4fe716c808717..5aacf1c78b734 100644 --- a/packages/animations/browser/test/dsl/animation_spec.ts +++ b/packages/animations/browser/test/dsl/animation_spec.ts @@ -5,7 +5,7 @@ * 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 {AUTO_STYLE, AnimationMetadata, AnimationMetadataType, AnimationOptions, animate, animation, group, keyframes, query, sequence, state, style, transition, trigger, useAnimation, ɵStyleData} from '@angular/animations'; +import {animate, animation, AnimationMetadata, AnimationMetadataType, AnimationOptions, AUTO_STYLE, group, keyframes, query, sequence, state, style, transition, trigger, useAnimation, ɵStyleData} from '@angular/animations'; import {Animation} from '../../src/dsl/animation'; import {buildAnimationAst} from '../../src/dsl/animation_ast_builder'; @@ -35,7 +35,9 @@ function createDiv() { rootElement.appendChild(subElement2); }); - afterEach(() => { document.body.removeChild(rootElement); }); + afterEach(() => { + document.body.removeChild(rootElement); + }); describe('validation', () => { it('should throw an error if one or more but not all keyframes() styles contain offsets', @@ -45,7 +47,9 @@ function createDiv() { style({opacity: 1, offset: 1}), ])); - expect(() => { validateAndThrowAnimationSequence(steps); }) + expect(() => { + validateAndThrowAnimationSequence(steps); + }) .toThrowError( /Not all style\(\) steps within the declared keyframes\(\) contain offsets/); }); @@ -96,7 +100,9 @@ function createDiv() { ])) ]); - expect(() => { validateAndThrowAnimationSequence(steps); }) + expect(() => { + validateAndThrowAnimationSequence(steps); + }) .toThrowError( /The CSS property "opacity" that exists between the times of "0ms" and "2000ms" is also being animated in a parallel animation between the times of "0ms" and "1500ms"/); }); @@ -191,7 +197,9 @@ function createDiv() { })), ]; - expect(() => { validateAndThrowAnimationSequence(steps); }) + expect(() => { + validateAndThrowAnimationSequence(steps); + }) .toThrowError( /state\("final", ...\) must define default values for all the following style substitutions: one, two, three/); @@ -203,7 +211,9 @@ function createDiv() { }), {params: {redColor: 'maroon'}})]; - expect(() => { validateAndThrowAnimationSequence(steps2); }) + expect(() => { + validateAndThrowAnimationSequence(steps2); + }) .toThrowError( /state\("panfinal", ...\) must define default values for all the following style substitutions: greyColor/); }); @@ -211,7 +221,9 @@ function createDiv() { it('should throw an error if an invalid CSS property is used in the animation', () => { const steps = [animate(1000, style({abc: '500px'}))]; - expect(() => { validateAndThrowAnimationSequence(steps); }) + expect(() => { + validateAndThrowAnimationSequence(steps); + }) .toThrowError( /The provided animation property "abc" is not a supported CSS property for animations/); }); @@ -388,7 +400,7 @@ function createDiv() { let players = invokeAnimationSequence(rootElement, steps); expect(players.length).toEqual(1); - let p1 = players.pop() !; + let p1 = players.pop()!; expect(p1.duration).toEqual(1500); expect(p1.keyframes).toEqual([ {width: '*', offset: 0}, @@ -405,7 +417,7 @@ function createDiv() { players = invokeAnimationSequence(rootElement, steps); expect(players.length).toEqual(1); - p1 = players.pop() !; + p1 = players.pop()!; expect(p1.duration).toEqual(1000); expect(p1.keyframes).toEqual([ {width: '100px', offset: 0}, @@ -876,7 +888,9 @@ function createDiv() { const steps = [query('somethingFake', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]; - expect(() => { invokeAnimationSequence(rootElement, steps); }) + expect(() => { + invokeAnimationSequence(rootElement, steps); + }) .toThrowError( /`query\("somethingFake"\)` returned zero elements\. \(Use `query\("somethingFake", \{ optional: true \}\)` if you wish to allow this\.\)/); }); @@ -887,13 +901,17 @@ function createDiv() { 'somethingFake', [style({opacity: 0}), animate(1000, style({opacity: 1}))], {optional: true})]; - expect(() => { invokeAnimationSequence(rootElement, steps); }).not.toThrow(); + expect(() => { + invokeAnimationSequence(rootElement, steps); + }).not.toThrow(); const steps2 = [query( 'fakeSomethings', [style({opacity: 0}), animate(1000, style({opacity: 1}))], {optional: true})]; - expect(() => { invokeAnimationSequence(rootElement, steps2); }).not.toThrow(); + expect(() => { + invokeAnimationSequence(rootElement, steps2); + }).not.toThrow(); }); it('should delay the query operation if a delay option is provided', () => { @@ -1025,8 +1043,7 @@ function createDiv() { const players = invokeAnimationSequence(rootElement, steps, {}, fromStyles, toStyles); expect(players[0].keyframes).toEqual([ - {background: 'blue', offset: 0, easing: 'ease-out'}, - {background: 'red', offset: 1} + {background: 'blue', offset: 0, easing: 'ease-out'}, {background: 'red', offset: 1} ]); }); }); @@ -1042,7 +1059,7 @@ function humanizeOffsets(keyframes: ɵStyleData[], digits: number = 3): ɵStyleD } function invokeAnimationSequence( - element: any, steps: AnimationMetadata | AnimationMetadata[], locals: {[key: string]: any} = {}, + element: any, steps: AnimationMetadata|AnimationMetadata[], locals: {[key: string]: any} = {}, startingStyles: ɵStyleData[] = [], destinationStyles: ɵStyleData[] = [], subInstructions?: ElementInstructionMap): AnimationTimelineInstruction[] { const driver = new MockAnimationDriver(); @@ -1050,7 +1067,7 @@ function invokeAnimationSequence( .buildTimelines(element, startingStyles, destinationStyles, locals, subInstructions); } -function validateAndThrowAnimationSequence(steps: AnimationMetadata | AnimationMetadata[]) { +function validateAndThrowAnimationSequence(steps: AnimationMetadata|AnimationMetadata[]) { const driver = new MockAnimationDriver(); const errors: any[] = []; const ast = buildAnimationAst(driver, steps, errors); diff --git a/packages/animations/browser/test/dsl/animation_trigger_spec.ts b/packages/animations/browser/test/dsl/animation_trigger_spec.ts index 5482075fc6458..f2c2e134b4e6c 100644 --- a/packages/animations/browser/test/dsl/animation_trigger_spec.ts +++ b/packages/animations/browser/test/dsl/animation_trigger_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AnimationOptions, animate, state, style, transition} from '@angular/animations'; +import {animate, AnimationOptions, state, style, transition} from '@angular/animations'; import {AnimationTransitionInstruction} from '@angular/animations/browser/src/dsl/animation_transition_instruction'; import {AnimationTrigger} from '@angular/animations/browser/src/dsl/animation_trigger'; @@ -25,7 +25,9 @@ import {makeTrigger} from '../shared'; document.body.appendChild(element); }); - afterEach(() => { document.body.removeChild(element); }); + afterEach(() => { + document.body.removeChild(element); + }); describe('trigger validation', () => { it('should group errors together for an animation trigger', () => { @@ -36,8 +38,9 @@ import {makeTrigger} from '../shared'; it('should throw an error when a transition within a trigger contains an invalid expression', () => { - expect( - () => { makeTrigger('name', [transition('somethingThatIsWrong', animate(3333))]); }) + expect(() => { + makeTrigger('name', [transition('somethingThatIsWrong', animate(3333))]); + }) .toThrowError( /- The provided transition expression "somethingThatIsWrong" is not supported/); }); @@ -78,7 +81,7 @@ import {makeTrigger} from '../shared'; const result = makeTrigger( 'name', [transition('a => b', animate(1234)), transition('b => c', animate(5678))]); - const trans = buildTransition(result, element, 'b', 'c') !; + const trans = buildTransition(result, element, 'b', 'c')!; expect(trans.timelines.length).toEqual(1); const timeline = trans.timelines[0]; expect(timeline.duration).toEqual(5678); @@ -90,13 +93,13 @@ import {makeTrigger} from '../shared'; transition('* => *', animate(9999)) ]); - let trans = buildTransition(result, element, 'b', 'c') !; + let trans = buildTransition(result, element, 'b', 'c')!; expect(trans.timelines[0].duration).toEqual(5678); - trans = buildTransition(result, element, 'a', 'b') !; + trans = buildTransition(result, element, 'a', 'b')!; expect(trans.timelines[0].duration).toEqual(1234); - trans = buildTransition(result, element, 'c', 'c') !; + trans = buildTransition(result, element, 'c', 'c')!; expect(trans.timelines[0].duration).toEqual(9999); }); @@ -110,23 +113,23 @@ import {makeTrigger} from '../shared'; it('should support bi-directional transition expressions', () => { const result = makeTrigger('name', [transition('a <=> b', animate(2222))]); - const t1 = buildTransition(result, element, 'a', 'b') !; + const t1 = buildTransition(result, element, 'a', 'b')!; expect(t1.timelines[0].duration).toEqual(2222); - const t2 = buildTransition(result, element, 'b', 'a') !; + const t2 = buildTransition(result, element, 'b', 'a')!; expect(t2.timelines[0].duration).toEqual(2222); }); it('should support multiple transition statements in one string', () => { const result = makeTrigger('name', [transition('a => b, b => a, c => *', animate(1234))]); - const t1 = buildTransition(result, element, 'a', 'b') !; + const t1 = buildTransition(result, element, 'a', 'b')!; expect(t1.timelines[0].duration).toEqual(1234); - const t2 = buildTransition(result, element, 'b', 'a') !; + const t2 = buildTransition(result, element, 'b', 'a')!; expect(t2.timelines[0].duration).toEqual(1234); - const t3 = buildTransition(result, element, 'c', 'a') !; + const t3 = buildTransition(result, element, 'c', 'a')!; expect(t3.timelines[0].duration).toEqual(1234); }); @@ -138,7 +141,7 @@ import {makeTrigger} from '../shared'; 'a => b', [style({height: '{{ a }}'}), animate(1000, style({height: '{{ b }}'}))], buildParams({a: '100px', b: '200px'}))]); - const trans = buildTransition(result, element, 'a', 'b') !; + const trans = buildTransition(result, element, 'a', 'b')!; const keyframes = trans.timelines[0].keyframes; expect(keyframes).toEqual([{height: '100px', offset: 0}, {height: '200px', offset: 1}]); }); @@ -153,7 +156,7 @@ import {makeTrigger} from '../shared'; buildParams({a: '100px', b: '200px'}))]); const trans = - buildTransition(result, element, 'a', 'b', {}, buildParams({a: '300px'})) !; + buildTransition(result, element, 'a', 'b', {}, buildParams({a: '300px'}))!; const keyframes = trans.timelines[0].keyframes; expect(keyframes).toEqual( @@ -167,7 +170,7 @@ import {makeTrigger} from '../shared'; transition('true <=> false', animate(1234)) ]); - const trans = buildTransition(result, element, false, true) !; + const trans = buildTransition(result, element, false, true)!; expect(trans.timelines[0].duration).toEqual(1234); }); @@ -177,7 +180,7 @@ import {makeTrigger} from '../shared'; transition('1 <=> 0', animate(4567)) ]); - const trans = buildTransition(result, element, false, true) !; + const trans = buildTransition(result, element, false, true)!; expect(trans.timelines[0].duration).toEqual(4567); }); @@ -188,7 +191,7 @@ import {makeTrigger} from '../shared'; transition('1 <=> 0', animate(4567)) ]); - const trans = buildTransition(result, element, false, true) !; + const trans = buildTransition(result, element, false, true)!; expect(trans.timelines[0].keyframes).toEqual([ {offset: 0, color: 'red'}, {offset: 1, color: 'green'} ]); @@ -201,7 +204,7 @@ import {makeTrigger} from '../shared'; transition('true <=> false', animate(4567)) ]); - const trans = buildTransition(result, element, false, true) !; + const trans = buildTransition(result, element, false, true)!; expect(trans.timelines[0].keyframes).toEqual([ {offset: 0, color: 'orange'}, {offset: 1, color: 'blue'} ]); @@ -214,7 +217,7 @@ import {makeTrigger} from '../shared'; ]); expect(() => { - const trans = buildTransition(result, element, false, true) !; + const trans = buildTransition(result, element, false, true)!; }).not.toThrow(); }); @@ -222,14 +225,14 @@ import {makeTrigger} from '../shared'; it('should alias the :enter transition as void => *', () => { const result = makeTrigger('name', [transition(':enter', animate(3333))]); - const trans = buildTransition(result, element, 'void', 'something') !; + const trans = buildTransition(result, element, 'void', 'something')!; expect(trans.timelines[0].duration).toEqual(3333); }); it('should alias the :leave transition as * => void', () => { const result = makeTrigger('name', [transition(':leave', animate(3333))]); - const trans = buildTransition(result, element, 'something', 'void') !; + const trans = buildTransition(result, element, 'something', 'void')!; expect(trans.timelines[0].duration).toEqual(3333); }); }); @@ -242,12 +245,12 @@ function buildTransition( fromOptions?: AnimationOptions, toOptions?: AnimationOptions): AnimationTransitionInstruction| null { const params = toOptions && toOptions.params || {}; - const trans = trigger.matchTransition(fromState, toState, element, params) !; + const trans = trigger.matchTransition(fromState, toState, element, params)!; if (trans) { const driver = new MockAnimationDriver(); return trans.build( driver, element, fromState, toState, ENTER_CLASSNAME, LEAVE_CLASSNAME, fromOptions, - toOptions) !; + toOptions)!; } return null; } diff --git a/packages/animations/browser/test/dsl/style_normalizer/web_animations_style_normalizer_spec.ts b/packages/animations/browser/test/dsl/style_normalizer/web_animations_style_normalizer_spec.ts index 003df7678cc9a..17d5878ffe775 100644 --- a/packages/animations/browser/test/dsl/style_normalizer/web_animations_style_normalizer_spec.ts +++ b/packages/animations/browser/test/dsl/style_normalizer/web_animations_style_normalizer_spec.ts @@ -16,13 +16,13 @@ import {WebAnimationsStyleNormalizer} from '../../../src/dsl/style_normalization expect(normalizer.normalizePropertyName('width', [])).toEqual('width'); expect(normalizer.normalizePropertyName('border-width', [])).toEqual('borderWidth'); expect(normalizer.normalizePropertyName('borderHeight', [])).toEqual('borderHeight'); - expect(normalizer.normalizePropertyName('-webkit-animation', [ - ])).toEqual('WebkitAnimation'); + expect(normalizer.normalizePropertyName('-webkit-animation', [])) + .toEqual('WebkitAnimation'); }); }); describe('normalizeStyleValue', () => { - function normalize(prop: string, val: string | number): string { + function normalize(prop: string, val: string|number): string { const errors: string[] = []; const result = normalizer.normalizeStyleValue(prop, prop, val, errors); if (errors.length) { diff --git a/packages/animations/browser/test/render/css_keyframes/css_keyframes_driver_spec.ts b/packages/animations/browser/test/render/css_keyframes/css_keyframes_driver_spec.ts index 747a3895751ee..54a4a73d06d1d 100644 --- a/packages/animations/browser/test/render/css_keyframes/css_keyframes_driver_spec.ts +++ b/packages/animations/browser/test/render/css_keyframes/css_keyframes_driver_spec.ts @@ -5,7 +5,7 @@ * 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 {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing'; +import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing'; import {CssKeyframesDriver} from '../../../src/render/css_keyframes/css_keyframes_driver'; import {CssKeyframesPlayer} from '../../../src/render/css_keyframes/css_keyframes_player'; @@ -16,7 +16,7 @@ import {assertElementExistsInDom, createElement, findKeyframeDefinition, forceRe const CSS_KEYFRAME_RULE_TYPE = 7; describe('CssKeyframesDriver tests', () => { - if (isNode || typeof(window as any)['AnimationEvent'] == 'undefined') return; + if (isNode || typeof (window as any)['AnimationEvent'] == 'undefined') return; describe('building keyframes', () => { it('should build CSS keyframe style object containing the keyframe styles', () => { @@ -28,7 +28,7 @@ describe('CssKeyframesDriver tests', () => { {opacity: 1, width: '200px', offset: 1}, ]); - const head = document.querySelector('head') !; + const head = document.querySelector('head')!; head.appendChild(kfElm); forceReflow(); @@ -67,7 +67,7 @@ describe('CssKeyframesDriver tests', () => { {width: '200px', offset: 1}, ]); - const head = document.querySelector('head') !; + const head = document.querySelector('head')!; head.appendChild(kfElm); forceReflow(); @@ -261,7 +261,7 @@ describe('CssKeyframesDriver tests', () => { player.play(); player.finish(); - player.beforeDestroy !(); + player.beforeDestroy!(); expect(player.currentSnapshot).toEqual({ width: '999px', height: '999px', diff --git a/packages/animations/browser/test/render/css_keyframes/direct_style_player_spec.ts b/packages/animations/browser/test/render/css_keyframes/direct_style_player_spec.ts index c04ae07783dfe..86234018ad614 100644 --- a/packages/animations/browser/test/render/css_keyframes/direct_style_player_spec.ts +++ b/packages/animations/browser/test/render/css_keyframes/direct_style_player_spec.ts @@ -5,7 +5,7 @@ * 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 {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing'; +import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing'; import {DirectStylePlayer} from '../../../src/render/css_keyframes/direct_style_player'; diff --git a/packages/animations/browser/test/render/css_keyframes/element_animation_style_handler_spec.ts b/packages/animations/browser/test/render/css_keyframes/element_animation_style_handler_spec.ts index d5b64b5758d1b..ad5984c599add 100644 --- a/packages/animations/browser/test/render/css_keyframes/element_animation_style_handler_spec.ts +++ b/packages/animations/browser/test/render/css_keyframes/element_animation_style_handler_spec.ts @@ -13,7 +13,7 @@ import {assertStyle, createElement, makeAnimationEvent, supportsAnimationEventCr const EMPTY_FN = () => {}; { describe('ElementAnimationStyleHandler', () => { - if (isNode || typeof(window as any)['AnimationEvent'] == 'undefined') return; + if (isNode || typeof (window as any)['AnimationEvent'] == 'undefined') return; it('should add and remove an animation on to an element\'s styling', () => { const element = createElement(); diff --git a/packages/animations/browser/test/render/css_keyframes/shared.ts b/packages/animations/browser/test/render/css_keyframes/shared.ts index e60a4f50ad665..d9592ee5a0f0a 100644 --- a/packages/animations/browser/test/render/css_keyframes/shared.ts +++ b/packages/animations/browser/test/render/css_keyframes/shared.ts @@ -10,7 +10,7 @@ export function forceReflow() { } export function makeAnimationEvent( - startOrEnd: 'start' | 'end', animationName: string, elapsedTime: number, timestamp?: number) { + startOrEnd: 'start'|'end', animationName: string, elapsedTime: number, timestamp?: number) { const e = new AnimationEvent('animation' + startOrEnd, {animationName, elapsedTime}); if (timestamp) { (e as any)._ngTestManualTimestamp = timestamp; diff --git a/packages/animations/browser/test/render/timeline_animation_engine_spec.ts b/packages/animations/browser/test/render/timeline_animation_engine_spec.ts index b9471416335b9..a6fb9e873103a 100644 --- a/packages/animations/browser/test/render/timeline_animation_engine_spec.ts +++ b/packages/animations/browser/test/render/timeline_animation_engine_spec.ts @@ -5,7 +5,7 @@ * 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 {AnimationMetadata, animate, style} from '@angular/animations'; +import {animate, AnimationMetadata, style} from '@angular/animations'; import {AnimationStyleNormalizer, NoopAnimationStyleNormalizer} from '../../src/dsl/style_normalization/animation_style_normalizer'; import {AnimationDriver} from '../../src/render/animation_driver'; @@ -14,98 +14,101 @@ import {TimelineAnimationEngine} from '../../src/render/timeline_animation_engin import {MockAnimationDriver, MockAnimationPlayer} from '../../testing/src/mock_animation_driver'; (function() { - const defaultDriver = new MockAnimationDriver(); +const defaultDriver = new MockAnimationDriver(); - function makeEngine(body: any, driver?: AnimationDriver, normalizer?: AnimationStyleNormalizer) { - return new TimelineAnimationEngine( - body, driver || defaultDriver, normalizer || new NoopAnimationStyleNormalizer()); - } +function makeEngine(body: any, driver?: AnimationDriver, normalizer?: AnimationStyleNormalizer) { + return new TimelineAnimationEngine( + body, driver || defaultDriver, normalizer || new NoopAnimationStyleNormalizer()); +} + +// these tests are only mean't to be run within the DOM +if (isNode) return; + +describe('TimelineAnimationEngine', () => { + let element: any; + + beforeEach(() => { + MockAnimationDriver.log = []; + element = document.createElement('div'); + document.body.appendChild(element); + }); + + afterEach(() => document.body.removeChild(element)); + + it('should animate a timeline', () => { + const engine = makeEngine(getBodyNode()); + const steps = [style({height: 100}), animate(1000, style({height: 0}))]; + expect(MockAnimationDriver.log.length).toEqual(0); + invokeAnimation(engine, element, steps); + expect(MockAnimationDriver.log.length).toEqual(1); + }); - // these tests are only mean't to be run within the DOM - if (isNode) return; - - describe('TimelineAnimationEngine', () => { - let element: any; - - beforeEach(() => { - MockAnimationDriver.log = []; - element = document.createElement('div'); - document.body.appendChild(element); - }); - - afterEach(() => document.body.removeChild(element)); - - it('should animate a timeline', () => { - const engine = makeEngine(getBodyNode()); - const steps = [style({height: 100}), animate(1000, style({height: 0}))]; - expect(MockAnimationDriver.log.length).toEqual(0); - invokeAnimation(engine, element, steps); - expect(MockAnimationDriver.log.length).toEqual(1); - }); - - it('should not destroy timeline-based animations after they have finished', () => { - const engine = makeEngine(getBodyNode()); - - const log: string[] = []; - function capture(value: string) { - return () => { log.push(value); }; - } - - const steps = [style({height: 0}), animate(1000, style({height: 500}))]; - - const player = invokeAnimation(engine, element, steps); - player.onDone(capture('done')); - player.onDestroy(capture('destroy')); - expect(log).toEqual([]); - - player.finish(); - expect(log).toEqual(['done']); - - player.destroy(); - expect(log).toEqual(['done', 'destroy']); - }); - - it('should normalize the style values that are animateTransitioned within an a timeline animation', - () => { - const engine = - makeEngine(getBodyNode(), defaultDriver, new SuffixNormalizer('-normalized')); - - const steps = [ - style({width: '333px'}), - animate(1000, style({width: '999px'})), - ]; - - const player = invokeAnimation(engine, element, steps) as MockAnimationPlayer; - expect(player.keyframes).toEqual([ - {'width-normalized': '333px-normalized', offset: 0}, - {'width-normalized': '999px-normalized', offset: 1} - ]); - }); - - it('should normalize `*` values', () => { - const driver = new SuperMockDriver(); - const engine = makeEngine(getBodyNode(), driver); - - const steps = [ - style({width: '*'}), - animate(1000, style({width: '999px'})), - ]; - - const player = invokeAnimation(engine, element, steps) as MockAnimationPlayer; - expect(player.keyframes).toEqual([{width: '*star*', offset: 0}, {width: '999px', offset: 1}]); - }); + it('should not destroy timeline-based animations after they have finished', () => { + const engine = makeEngine(getBodyNode()); + + const log: string[] = []; + function capture(value: string) { + return () => { + log.push(value); + }; + } + + const steps = [style({height: 0}), animate(1000, style({height: 500}))]; + + const player = invokeAnimation(engine, element, steps); + player.onDone(capture('done')); + player.onDestroy(capture('destroy')); + expect(log).toEqual([]); + + player.finish(); + expect(log).toEqual(['done']); + + player.destroy(); + expect(log).toEqual(['done', 'destroy']); }); + + it('should normalize the style values that are animateTransitioned within an a timeline animation', + () => { + const engine = makeEngine(getBodyNode(), defaultDriver, new SuffixNormalizer('-normalized')); + + const steps = [ + style({width: '333px'}), + animate(1000, style({width: '999px'})), + ]; + + const player = invokeAnimation(engine, element, steps) as MockAnimationPlayer; + expect(player.keyframes).toEqual([ + {'width-normalized': '333px-normalized', offset: 0}, + {'width-normalized': '999px-normalized', offset: 1} + ]); + }); + + it('should normalize `*` values', () => { + const driver = new SuperMockDriver(); + const engine = makeEngine(getBodyNode(), driver); + + const steps = [ + style({width: '*'}), + animate(1000, style({width: '999px'})), + ]; + + const player = invokeAnimation(engine, element, steps) as MockAnimationPlayer; + expect(player.keyframes).toEqual([{width: '*star*', offset: 0}, {width: '999px', offset: 1}]); + }); +}); })(); function invokeAnimation( - engine: TimelineAnimationEngine, element: any, steps: AnimationMetadata | AnimationMetadata[], + engine: TimelineAnimationEngine, element: any, steps: AnimationMetadata|AnimationMetadata[], id: string = 'id') { engine.register(id, steps); return engine.create(id, element); } class SuffixNormalizer extends AnimationStyleNormalizer { - constructor(private _suffix: string) { super(); } + constructor(private _suffix: string) { + super(); + } normalizePropertyName(propertyName: string, errors: string[]): string { return propertyName + this._suffix; @@ -119,5 +122,7 @@ class SuffixNormalizer extends AnimationStyleNormalizer { } class SuperMockDriver extends MockAnimationDriver { - computeStyle(element: any, prop: string, defaultValue?: string): string { return '*star*'; } + computeStyle(element: any, prop: string, defaultValue?: string): string { + return '*star*'; + } } diff --git a/packages/animations/browser/test/render/transition_animation_engine_spec.ts b/packages/animations/browser/test/render/transition_animation_engine_spec.ts index 98d80c294d6c4..21502489e9587 100644 --- a/packages/animations/browser/test/render/transition_animation_engine_spec.ts +++ b/packages/animations/browser/test/render/transition_animation_engine_spec.ts @@ -5,7 +5,7 @@ * 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 {AnimationEvent, AnimationMetadata, AnimationTriggerMetadata, NoopAnimationPlayer, animate, state, style, transition, trigger} from '@angular/animations'; +import {animate, AnimationEvent, AnimationMetadata, AnimationTriggerMetadata, NoopAnimationPlayer, state, style, transition, trigger} from '@angular/animations'; import {TriggerAst} from '../../src/dsl/animation_ast'; import {buildAnimationAst} from '../../src/dsl/animation_ast_builder'; @@ -18,673 +18,680 @@ import {MockAnimationDriver, MockAnimationPlayer} from '../../testing/src/mock_a const DEFAULT_NAMESPACE_ID = 'id'; (function() { - const driver = new MockAnimationDriver(); +const driver = new MockAnimationDriver(); - // these tests are only mean't to be run within the DOM - if (isNode) return; +// these tests are only mean't to be run within the DOM +if (isNode) return; - describe('TransitionAnimationEngine', () => { - let element: any; +describe('TransitionAnimationEngine', () => { + let element: any; - beforeEach(() => { - MockAnimationDriver.log = []; - element = document.createElement('div'); - document.body.appendChild(element); - }); + beforeEach(() => { + MockAnimationDriver.log = []; + element = document.createElement('div'); + document.body.appendChild(element); + }); - afterEach(() => { document.body.removeChild(element); }); + afterEach(() => { + document.body.removeChild(element); + }); - function makeEngine(normalizer?: AnimationStyleNormalizer) { - const engine = new TransitionAnimationEngine( - getBodyNode(), driver, normalizer || new NoopAnimationStyleNormalizer()); - engine.createNamespace(DEFAULT_NAMESPACE_ID, element); - return engine; - } + function makeEngine(normalizer?: AnimationStyleNormalizer) { + const engine = new TransitionAnimationEngine( + getBodyNode(), driver, normalizer || new NoopAnimationStyleNormalizer()); + engine.createNamespace(DEFAULT_NAMESPACE_ID, element); + return engine; + } - describe('trigger registration', () => { - it('should ignore and not throw an error if the same trigger is registered twice', () => { - // TODO (matsko): ask why this is avoided - const engine = makeEngine(); + describe('trigger registration', () => { + it('should ignore and not throw an error if the same trigger is registered twice', () => { + // TODO (matsko): ask why this is avoided + const engine = makeEngine(); + registerTrigger(element, engine, trigger('trig', [])); + expect(() => { registerTrigger(element, engine, trigger('trig', [])); - expect(() => { registerTrigger(element, engine, trigger('trig', [])); }).not.toThrow(); - }); + }).not.toThrow(); }); + }); - describe('property setting', () => { - it('should invoke a transition based on a property change', () => { - const engine = makeEngine(); - const trig = trigger('myTrigger', [ - transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) - ]); + describe('property setting', () => { + it('should invoke a transition based on a property change', () => { + const engine = makeEngine(); + const trig = trigger('myTrigger', [ + transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); - registerTrigger(element, engine, trig); - setProperty(element, engine, 'myTrigger', 'value'); - engine.flush(); - expect(engine.players.length).toEqual(1); + registerTrigger(element, engine, trig); + setProperty(element, engine, 'myTrigger', 'value'); + engine.flush(); + expect(engine.players.length).toEqual(1); - const player = MockAnimationDriver.log.pop() as MockAnimationPlayer; - expect(player.keyframes).toEqual([ - {height: '0px', offset: 0}, {height: '100px', offset: 1} - ]); - }); - - it('should not queue an animation if the property value has not changed at all', () => { - const engine = makeEngine(); + const player = MockAnimationDriver.log.pop() as MockAnimationPlayer; + expect(player.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]); + }); - const trig = trigger('myTrigger', [ - transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) - ]); + it('should not queue an animation if the property value has not changed at all', () => { + const engine = makeEngine(); - registerTrigger(element, engine, trig); - engine.flush(); - expect(engine.players.length).toEqual(0); + const trig = trigger('myTrigger', [ + transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); - setProperty(element, engine, 'myTrigger', 'abc'); - engine.flush(); - expect(engine.players.length).toEqual(1); + registerTrigger(element, engine, trig); + engine.flush(); + expect(engine.players.length).toEqual(0); - setProperty(element, engine, 'myTrigger', 'abc'); - engine.flush(); - expect(engine.players.length).toEqual(1); - }); + setProperty(element, engine, 'myTrigger', 'abc'); + engine.flush(); + expect(engine.players.length).toEqual(1); - it('should throw an error if an animation property without a matching trigger is changed', - () => { - const engine = makeEngine(); - expect(() => { - setProperty(element, engine, 'myTrigger', 'no'); - }).toThrowError(/The provided animation trigger "myTrigger" has not been registered!/); - }); + setProperty(element, engine, 'myTrigger', 'abc'); + engine.flush(); + expect(engine.players.length).toEqual(1); }); - describe('removal operations', () => { - it('should cleanup all inner state that\'s tied to an element once removed', () => { - const engine = makeEngine(); + it('should throw an error if an animation property without a matching trigger is changed', + () => { + const engine = makeEngine(); + expect(() => { + setProperty(element, engine, 'myTrigger', 'no'); + }).toThrowError(/The provided animation trigger "myTrigger" has not been registered!/); + }); + }); + + describe('removal operations', () => { + it('should cleanup all inner state that\'s tied to an element once removed', () => { + const engine = makeEngine(); - const trig = trigger('myTrigger', [ - transition(':leave', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) - ]); + const trig = trigger('myTrigger', [ + transition(':leave', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); - registerTrigger(element, engine, trig); - setProperty(element, engine, 'myTrigger', 'value'); - engine.flush(); + registerTrigger(element, engine, trig); + setProperty(element, engine, 'myTrigger', 'value'); + engine.flush(); - expect(engine.elementContainsData(DEFAULT_NAMESPACE_ID, element)).toBeTruthy(); + expect(engine.elementContainsData(DEFAULT_NAMESPACE_ID, element)).toBeTruthy(); - engine.removeNode(DEFAULT_NAMESPACE_ID, element, true, true); - engine.flush(); + engine.removeNode(DEFAULT_NAMESPACE_ID, element, true, true); + engine.flush(); - expect(engine.elementContainsData(DEFAULT_NAMESPACE_ID, element)).toBeTruthy(); - }); + expect(engine.elementContainsData(DEFAULT_NAMESPACE_ID, element)).toBeTruthy(); + }); - it('should create and recreate a namespace for a host element with the same component source', - () => { - const engine = makeEngine(); - - const trig = - trigger('myTrigger', [transition('* => *', animate(1234, style({color: 'red'})))]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, 'myTrigger', 'value'); - engine.flush(); - expect(((engine.players[0] as TransitionAnimationPlayer) - .getRealPlayer() as MockAnimationPlayer) - .duration) - .toEqual(1234); - - engine.destroy(DEFAULT_NAMESPACE_ID, null); - - registerTrigger(element, engine, trig); - setProperty(element, engine, 'myTrigger', 'value2'); - engine.flush(); - expect(((engine.players[0] as TransitionAnimationPlayer) - .getRealPlayer() as MockAnimationPlayer) - .duration) - .toEqual(1234); - }); - - it('should clear child node data when a parent node with leave transition is removed', () => { - const engine = makeEngine(); - const child = document.createElement('div'); - const parentTrigger = trigger('parent', [ - transition(':leave', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) - ]); - const childTrigger = trigger( - 'child', - [transition(':enter', [style({opacity: '0'}), animate(1000, style({opacity: '1'}))])]); - - registerTrigger(element, engine, parentTrigger); - registerTrigger(child, engine, childTrigger); - - element.appendChild(child); - engine.insertNode(DEFAULT_NAMESPACE_ID, child, element, true); - - setProperty(element, engine, 'parent', 'value'); - setProperty(child, engine, 'child', 'visible'); - engine.flush(); + it('should create and recreate a namespace for a host element with the same component source', + () => { + const engine = makeEngine(); + + const trig = + trigger('myTrigger', [transition('* => *', animate(1234, style({color: 'red'})))]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, 'myTrigger', 'value'); + engine.flush(); + expect(((engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as + MockAnimationPlayer) + .duration) + .toEqual(1234); + + engine.destroy(DEFAULT_NAMESPACE_ID, null); + + registerTrigger(element, engine, trig); + setProperty(element, engine, 'myTrigger', 'value2'); + engine.flush(); + expect(((engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as + MockAnimationPlayer) + .duration) + .toEqual(1234); + }); + + it('should clear child node data when a parent node with leave transition is removed', () => { + const engine = makeEngine(); + const child = document.createElement('div'); + const parentTrigger = trigger('parent', [ + transition(':leave', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); + const childTrigger = trigger( + 'child', + [transition(':enter', [style({opacity: '0'}), animate(1000, style({opacity: '1'}))])]); + + registerTrigger(element, engine, parentTrigger); + registerTrigger(child, engine, childTrigger); + + element.appendChild(child); + engine.insertNode(DEFAULT_NAMESPACE_ID, child, element, true); + + setProperty(element, engine, 'parent', 'value'); + setProperty(child, engine, 'child', 'visible'); + engine.flush(); + + expect(engine.statesByElement.has(element)).toBe(true, 'Expected parent data to be defined.'); + expect(engine.statesByElement.has(child)).toBe(true, 'Expected child data to be defined.'); + + engine.removeNode(DEFAULT_NAMESPACE_ID, element, true, true); + engine.flush(); + engine.players[0].finish(); + + expect(engine.statesByElement.has(element)) + .toBe(false, 'Expected parent data to be cleared.'); + expect(engine.statesByElement.has(child)).toBe(false, 'Expected child data to be cleared.'); + }); + }); - expect(engine.statesByElement.has(element)) - .toBe(true, 'Expected parent data to be defined.'); - expect(engine.statesByElement.has(child)).toBe(true, 'Expected child data to be defined.'); + describe('event listeners', () => { + it('should listen to the onStart operation for the animation', () => { + const engine = makeEngine(); - engine.removeNode(DEFAULT_NAMESPACE_ID, element, true, true); - engine.flush(); - engine.players[0].finish(); + const trig = trigger('myTrigger', [ + transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); - expect(engine.statesByElement.has(element)) - .toBe(false, 'Expected parent data to be cleared.'); - expect(engine.statesByElement.has(child)).toBe(false, 'Expected child data to be cleared.'); - }); + let count = 0; + registerTrigger(element, engine, trig); + listen(element, engine, 'myTrigger', 'start', () => count++); + setProperty(element, engine, 'myTrigger', 'value'); + expect(count).toEqual(0); + engine.flush(); + expect(count).toEqual(1); }); - describe('event listeners', () => { - it('should listen to the onStart operation for the animation', () => { - const engine = makeEngine(); + it('should listen to the onDone operation for the animation', () => { + const engine = makeEngine(); - const trig = trigger('myTrigger', [ - transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) - ]); + const trig = trigger('myTrigger', [ + transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); - let count = 0; - registerTrigger(element, engine, trig); - listen(element, engine, 'myTrigger', 'start', () => count++); - setProperty(element, engine, 'myTrigger', 'value'); - expect(count).toEqual(0); + let count = 0; + registerTrigger(element, engine, trig); + listen(element, engine, 'myTrigger', 'done', () => count++); + setProperty(element, engine, 'myTrigger', 'value'); + expect(count).toEqual(0); - engine.flush(); - expect(count).toEqual(1); - }); + engine.flush(); + expect(count).toEqual(0); - it('should listen to the onDone operation for the animation', () => { - const engine = makeEngine(); + engine.players[0].finish(); + expect(count).toEqual(1); + }); - const trig = trigger('myTrigger', [ - transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) - ]); + it('should throw an error when an event is listened to that isn\'t supported', () => { + const engine = makeEngine(); + const trig = trigger('myTrigger', []); + registerTrigger(element, engine, trig); - let count = 0; - registerTrigger(element, engine, trig); - listen(element, engine, 'myTrigger', 'done', () => count++); - setProperty(element, engine, 'myTrigger', 'value'); - expect(count).toEqual(0); + expect(() => { + listen(element, engine, 'myTrigger', 'explode', () => {}); + }) + .toThrowError( + /The provided animation trigger event "explode" for the animation trigger "myTrigger" is not supported!/); + }); - engine.flush(); - expect(count).toEqual(0); + it('should throw an error when an event is listened for a trigger that doesn\'t exist', () => { + const engine = makeEngine(); + expect(() => { + listen(element, engine, 'myTrigger', 'explode', () => {}); + }) + .toThrowError( + /Unable to listen on the animation trigger event "explode" because the animation trigger "myTrigger" doesn\'t exist!/); + }); - engine.players[0].finish(); - expect(count).toEqual(1); - }); + it('should throw an error when an undefined event is listened for', () => { + const engine = makeEngine(); + const trig = trigger('myTrigger', []); + registerTrigger(element, engine, trig); + expect(() => { + listen(element, engine, 'myTrigger', '', () => {}); + }) + .toThrowError( + /Unable to listen on the animation trigger "myTrigger" because the provided event is undefined!/); + }); - it('should throw an error when an event is listened to that isn\'t supported', () => { - const engine = makeEngine(); - const trig = trigger('myTrigger', []); - registerTrigger(element, engine, trig); + it('should retain event listeners and call them for successive animation state changes', () => { + const engine = makeEngine(); + const trig = trigger('myTrigger', [ + transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); - expect(() => { listen(element, engine, 'myTrigger', 'explode', () => {}); }) - .toThrowError( - /The provided animation trigger event "explode" for the animation trigger "myTrigger" is not supported!/); - }); + registerTrigger(element, engine, trig); - it('should throw an error when an event is listened for a trigger that doesn\'t exist', () => { - const engine = makeEngine(); - expect(() => { listen(element, engine, 'myTrigger', 'explode', () => {}); }) - .toThrowError( - /Unable to listen on the animation trigger event "explode" because the animation trigger "myTrigger" doesn\'t exist!/); - }); + let count = 0; + listen(element, engine, 'myTrigger', 'start', () => count++); - it('should throw an error when an undefined event is listened for', () => { - const engine = makeEngine(); - const trig = trigger('myTrigger', []); - registerTrigger(element, engine, trig); - expect(() => { listen(element, engine, 'myTrigger', '', () => {}); }) - .toThrowError( - /Unable to listen on the animation trigger "myTrigger" because the provided event is undefined!/); - }); + setProperty(element, engine, 'myTrigger', '123'); + engine.flush(); + expect(count).toEqual(1); - it('should retain event listeners and call them for successive animation state changes', - () => { - const engine = makeEngine(); - const trig = trigger( - 'myTrigger', - [transition( - '* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))])]); - - registerTrigger(element, engine, trig); - - let count = 0; - listen(element, engine, 'myTrigger', 'start', () => count++); - - setProperty(element, engine, 'myTrigger', '123'); - engine.flush(); - expect(count).toEqual(1); - - setProperty(element, engine, 'myTrigger', '456'); - engine.flush(); - expect(count).toEqual(2); - }); - - it('should only fire event listener changes for when the corresponding trigger changes state', - () => { - const engine = makeEngine(); - const trig1 = trigger( - 'myTrigger1', - [transition( - '* => 123', [style({height: '0px'}), animate(1000, style({height: '100px'}))])]); - registerTrigger(element, engine, trig1); - - const trig2 = trigger( - 'myTrigger2', - [transition( - '* => 123', [style({width: '0px'}), animate(1000, style({width: '100px'}))])]); - registerTrigger(element, engine, trig2); - - let count = 0; - listen(element, engine, 'myTrigger1', 'start', () => count++); - - setProperty(element, engine, 'myTrigger1', '123'); - engine.flush(); - expect(count).toEqual(1); - - setProperty(element, engine, 'myTrigger2', '123'); - engine.flush(); - expect(count).toEqual(1); - }); - - it('should allow a listener to be deregistered, but only after a flush occurs', () => { - const engine = makeEngine(); - const trig = trigger( - 'myTrigger', - [transition( - '* => 123', [style({height: '0px'}), animate(1000, style({height: '100px'}))])]); - registerTrigger(element, engine, trig); - - let count = 0; - const deregisterFn = listen(element, engine, 'myTrigger', 'start', () => count++); - setProperty(element, engine, 'myTrigger', '123'); - engine.flush(); - expect(count).toEqual(1); + setProperty(element, engine, 'myTrigger', '456'); + engine.flush(); + expect(count).toEqual(2); + }); - deregisterFn(); - engine.flush(); + it('should only fire event listener changes for when the corresponding trigger changes state', + () => { + const engine = makeEngine(); + const trig1 = trigger('myTrigger1', [ + transition('* => 123', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); + registerTrigger(element, engine, trig1); + + const trig2 = trigger('myTrigger2', [ + transition('* => 123', [style({width: '0px'}), animate(1000, style({width: '100px'}))]) + ]); + registerTrigger(element, engine, trig2); + + let count = 0; + listen(element, engine, 'myTrigger1', 'start', () => count++); + + setProperty(element, engine, 'myTrigger1', '123'); + engine.flush(); + expect(count).toEqual(1); + + setProperty(element, engine, 'myTrigger2', '123'); + engine.flush(); + expect(count).toEqual(1); + }); + + it('should allow a listener to be deregistered, but only after a flush occurs', () => { + const engine = makeEngine(); + const trig = trigger('myTrigger', [ + transition('* => 123', [style({height: '0px'}), animate(1000, style({height: '100px'}))]) + ]); + registerTrigger(element, engine, trig); + + let count = 0; + const deregisterFn = listen(element, engine, 'myTrigger', 'start', () => count++); + setProperty(element, engine, 'myTrigger', '123'); + engine.flush(); + expect(count).toEqual(1); + + deregisterFn(); + engine.flush(); + + setProperty(element, engine, 'myTrigger', '456'); + engine.flush(); + expect(count).toEqual(1); + }); - setProperty(element, engine, 'myTrigger', '456'); - engine.flush(); - expect(count).toEqual(1); + it('should trigger a listener callback with an AnimationEvent argument', () => { + const engine = makeEngine(); + registerTrigger( + element, engine, trigger('myTrigger', [ + transition('* => *', [style({height: '0px'}), animate(1234, style({height: '100px'}))]) + ])); + + // we do this so that the next transition has a starting value that isn't null + setProperty(element, engine, 'myTrigger', '123'); + engine.flush(); + + let capture: AnimationEvent = null!; + listen(element, engine, 'myTrigger', 'start', e => capture = e); + listen(element, engine, 'myTrigger', 'done', e => capture = e); + setProperty(element, engine, 'myTrigger', '456'); + engine.flush(); + + delete (capture as any)['_data']; + expect(capture).toEqual({ + element, + triggerName: 'myTrigger', + phaseName: 'start', + fromState: '123', + toState: '456', + totalTime: 1234, + disabled: false }); - it('should trigger a listener callback with an AnimationEvent argument', () => { - const engine = makeEngine(); - registerTrigger( - element, engine, trigger('myTrigger', [ - transition( - '* => *', [style({height: '0px'}), animate(1234, style({height: '100px'}))]) - ])); - - // we do this so that the next transition has a starting value that isn't null - setProperty(element, engine, 'myTrigger', '123'); - engine.flush(); - - let capture: AnimationEvent = null !; - listen(element, engine, 'myTrigger', 'start', e => capture = e); - listen(element, engine, 'myTrigger', 'done', e => capture = e); - setProperty(element, engine, 'myTrigger', '456'); - engine.flush(); - - delete (capture as any)['_data']; - expect(capture).toEqual({ - element, - triggerName: 'myTrigger', - phaseName: 'start', - fromState: '123', - toState: '456', - totalTime: 1234, - disabled: false - }); - - capture = null !; - const player = engine.players.pop() !; - player.finish(); - - delete (capture as any)['_data']; - expect(capture).toEqual({ - element, - triggerName: 'myTrigger', - phaseName: 'done', - fromState: '123', - toState: '456', - totalTime: 1234, - disabled: false - }); + capture = null!; + const player = engine.players.pop()!; + player.finish(); + + delete (capture as any)['_data']; + expect(capture).toEqual({ + element, + triggerName: 'myTrigger', + phaseName: 'done', + fromState: '123', + toState: '456', + totalTime: 1234, + disabled: false }); }); + }); - describe('transition operations', () => { - it('should persist the styles on the element as actual styles once the animation is complete', - () => { - const engine = makeEngine(); - const trig = trigger('something', [ - state('on', style({height: '100px'})), state('off', style({height: '0px'})), - transition('on => off', animate(9876)) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'on'); - setProperty(element, engine, trig.name, 'off'); - engine.flush(); - - expect(element.style.height).not.toEqual('0px'); - engine.players[0].finish(); - expect(element.style.height).toEqual('0px'); - }); - - it('should remove all existing state styling from an element when a follow-up transition occurs on the same trigger', - () => { - const engine = makeEngine(); - const trig = trigger('something', [ - state('a', style({height: '100px'})), state('b', style({height: '500px'})), - state('c', style({width: '200px'})), transition('* => *', animate(9876)) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'a'); - setProperty(element, engine, trig.name, 'b'); - engine.flush(); - - const player1 = engine.players[0]; - player1.finish(); - expect(element.style.height).toEqual('500px'); - - setProperty(element, engine, trig.name, 'c'); - engine.flush(); - - const player2 = engine.players[0]; - expect(element.style.height).not.toEqual('500px'); - player2.finish(); - expect(element.style.width).toEqual('200px'); - expect(element.style.height).not.toEqual('500px'); - }); - - it('should allow two animation transitions with different triggers to animate in parallel', - () => { - const engine = makeEngine(); - const trig1 = trigger('something1', [ - state('a', style({width: '100px'})), state('b', style({width: '200px'})), - transition('* => *', animate(1000)) - ]); - - const trig2 = trigger('something2', [ - state('x', style({height: '500px'})), state('y', style({height: '1000px'})), - transition('* => *', animate(2000)) - ]); - - registerTrigger(element, engine, trig1); - registerTrigger(element, engine, trig2); - - let doneCount = 0; - function doneCallback() { doneCount++; } - - setProperty(element, engine, trig1.name, 'a'); - setProperty(element, engine, trig1.name, 'b'); - setProperty(element, engine, trig2.name, 'x'); - setProperty(element, engine, trig2.name, 'y'); - engine.flush(); - - const player1 = engine.players[0] !; - player1.onDone(doneCallback); - expect(doneCount).toEqual(0); - - const player2 = engine.players[1] !; - player2.onDone(doneCallback); - expect(doneCount).toEqual(0); - - player1.finish(); - expect(doneCount).toEqual(1); - - player2.finish(); - expect(doneCount).toEqual(2); - - expect(element.style.width).toEqual('200px'); - expect(element.style.height).toEqual('1000px'); - }); - - it('should cancel a previously running animation when a follow-up transition kicks off on the same trigger', - () => { - const engine = makeEngine(); - const trig = trigger('something', [ - state('x', style({opacity: 0})), - state('y', style({opacity: .5})), - state('z', style({opacity: 1})), - transition('* => *', animate(1000)), - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'x'); - setProperty(element, engine, trig.name, 'y'); - engine.flush(); - - expect(parseFloat(element.style.opacity)).not.toEqual(.5); - - const player1 = engine.players[0]; - setProperty(element, engine, trig.name, 'z'); - engine.flush(); - - const player2 = engine.players[0]; - - expect(parseFloat(element.style.opacity)).not.toEqual(.5); - - player2.finish(); - expect(parseFloat(element.style.opacity)).toEqual(1); - - player1.finish(); - expect(parseFloat(element.style.opacity)).toEqual(1); - }); - - it('should pass in the previously running players into the follow-up transition player when cancelled', - () => { - const engine = makeEngine(); - const trig = trigger('something', [ - state('x', style({opacity: 0})), state('y', style({opacity: .5})), - state('z', style({opacity: 1})), transition('* => *', animate(1000)) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'x'); - setProperty(element, engine, trig.name, 'y'); - engine.flush(); - - const player1 = MockAnimationDriver.log.pop() !as MockAnimationPlayer; - player1.setPosition(0.5); - - setProperty(element, engine, trig.name, 'z'); - engine.flush(); - - const player2 = MockAnimationDriver.log.pop() !as MockAnimationPlayer; - expect(player2.previousPlayers).toEqual([player1]); - player2.finish(); - - setProperty(element, engine, trig.name, 'x'); - engine.flush(); - - const player3 = MockAnimationDriver.log.pop() !as MockAnimationPlayer; - expect(player3.previousPlayers).toEqual([]); - }); - - it('should cancel all existing players if a removal animation is set to occur', () => { - const engine = makeEngine(); - const trig = trigger('something', [ - state('m', style({opacity: 0})), state('n', style({opacity: 1})), - transition('* => *', animate(1000)) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'm'); - setProperty(element, engine, trig.name, 'n'); - engine.flush(); + describe('transition operations', () => { + it('should persist the styles on the element as actual styles once the animation is complete', + () => { + const engine = makeEngine(); + const trig = trigger('something', [ + state('on', style({height: '100px'})), state('off', style({height: '0px'})), + transition('on => off', animate(9876)) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'on'); + setProperty(element, engine, trig.name, 'off'); + engine.flush(); + + expect(element.style.height).not.toEqual('0px'); + engine.players[0].finish(); + expect(element.style.height).toEqual('0px'); + }); + + it('should remove all existing state styling from an element when a follow-up transition occurs on the same trigger', + () => { + const engine = makeEngine(); + const trig = trigger('something', [ + state('a', style({height: '100px'})), state('b', style({height: '500px'})), + state('c', style({width: '200px'})), transition('* => *', animate(9876)) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'a'); + setProperty(element, engine, trig.name, 'b'); + engine.flush(); + + const player1 = engine.players[0]; + player1.finish(); + expect(element.style.height).toEqual('500px'); + + setProperty(element, engine, trig.name, 'c'); + engine.flush(); + + const player2 = engine.players[0]; + expect(element.style.height).not.toEqual('500px'); + player2.finish(); + expect(element.style.width).toEqual('200px'); + expect(element.style.height).not.toEqual('500px'); + }); + + it('should allow two animation transitions with different triggers to animate in parallel', + () => { + const engine = makeEngine(); + const trig1 = trigger('something1', [ + state('a', style({width: '100px'})), state('b', style({width: '200px'})), + transition('* => *', animate(1000)) + ]); + + const trig2 = trigger('something2', [ + state('x', style({height: '500px'})), state('y', style({height: '1000px'})), + transition('* => *', animate(2000)) + ]); + + registerTrigger(element, engine, trig1); + registerTrigger(element, engine, trig2); + + let doneCount = 0; + function doneCallback() { + doneCount++; + } + + setProperty(element, engine, trig1.name, 'a'); + setProperty(element, engine, trig1.name, 'b'); + setProperty(element, engine, trig2.name, 'x'); + setProperty(element, engine, trig2.name, 'y'); + engine.flush(); + + const player1 = engine.players[0]!; + player1.onDone(doneCallback); + expect(doneCount).toEqual(0); + + const player2 = engine.players[1]!; + player2.onDone(doneCallback); + expect(doneCount).toEqual(0); + + player1.finish(); + expect(doneCount).toEqual(1); + + player2.finish(); + expect(doneCount).toEqual(2); + + expect(element.style.width).toEqual('200px'); + expect(element.style.height).toEqual('1000px'); + }); + + it('should cancel a previously running animation when a follow-up transition kicks off on the same trigger', + () => { + const engine = makeEngine(); + const trig = trigger('something', [ + state('x', style({opacity: 0})), + state('y', style({opacity: .5})), + state('z', style({opacity: 1})), + transition('* => *', animate(1000)), + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'x'); + setProperty(element, engine, trig.name, 'y'); + engine.flush(); + + expect(parseFloat(element.style.opacity)).not.toEqual(.5); + + const player1 = engine.players[0]; + setProperty(element, engine, trig.name, 'z'); + engine.flush(); + + const player2 = engine.players[0]; + + expect(parseFloat(element.style.opacity)).not.toEqual(.5); + + player2.finish(); + expect(parseFloat(element.style.opacity)).toEqual(1); + + player1.finish(); + expect(parseFloat(element.style.opacity)).toEqual(1); + }); + + it('should pass in the previously running players into the follow-up transition player when cancelled', + () => { + const engine = makeEngine(); + const trig = trigger('something', [ + state('x', style({opacity: 0})), state('y', style({opacity: .5})), + state('z', style({opacity: 1})), transition('* => *', animate(1000)) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'x'); + setProperty(element, engine, trig.name, 'y'); + engine.flush(); + + const player1 = MockAnimationDriver.log.pop()! as MockAnimationPlayer; + player1.setPosition(0.5); + + setProperty(element, engine, trig.name, 'z'); + engine.flush(); + + const player2 = MockAnimationDriver.log.pop()! as MockAnimationPlayer; + expect(player2.previousPlayers).toEqual([player1]); + player2.finish(); + + setProperty(element, engine, trig.name, 'x'); + engine.flush(); + + const player3 = MockAnimationDriver.log.pop()! as MockAnimationPlayer; + expect(player3.previousPlayers).toEqual([]); + }); - let doneCount = 0; - function doneCallback() { doneCount++; } + it('should cancel all existing players if a removal animation is set to occur', () => { + const engine = makeEngine(); + const trig = trigger('something', [ + state('m', style({opacity: 0})), state('n', style({opacity: 1})), + transition('* => *', animate(1000)) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'm'); + setProperty(element, engine, trig.name, 'n'); + engine.flush(); - const player1 = engine.players[0]; - player1.onDone(doneCallback); + let doneCount = 0; + function doneCallback() { + doneCount++; + } - expect(doneCount).toEqual(0); + const player1 = engine.players[0]; + player1.onDone(doneCallback); - setProperty(element, engine, trig.name, 'void'); - engine.flush(); + expect(doneCount).toEqual(0); - expect(doneCount).toEqual(1); - }); - - it('should only persist styles that exist in the final state styles and not the last keyframe', - () => { - const engine = makeEngine(); - const trig = trigger('something', [ - state('0', style({width: '0px'})), state('1', style({width: '100px'})), - transition('* => *', [animate(1000, style({height: '200px'}))]) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, '0'); - setProperty(element, engine, trig.name, '1'); - engine.flush(); - - const player = engine.players[0] !; - expect(element.style.width).not.toEqual('100px'); - - player.finish(); - expect(element.style.height).not.toEqual('200px'); - expect(element.style.width).toEqual('100px'); - }); - - it('should default to using styling from the `*` state if a matching state is not found', - () => { - const engine = makeEngine(); - const trig = trigger('something', [ - state('a', style({opacity: 0})), state('*', style({opacity: .5})), - transition('* => *', animate(1000)) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'a'); - setProperty(element, engine, trig.name, 'z'); - engine.flush(); - - engine.players[0].finish(); - expect(parseFloat(element.style.opacity)).toEqual(.5); - }); - - it('should treat `void` as `void`', () => { - const engine = makeEngine(); - const trig = trigger('something', [ - state('a', style({opacity: 0})), state('void', style({opacity: .8})), - transition('* => *', animate(1000)) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'a'); - setProperty(element, engine, trig.name, 'void'); - engine.flush(); + setProperty(element, engine, trig.name, 'void'); + engine.flush(); - engine.players[0].finish(); - expect(parseFloat(element.style.opacity)).toEqual(.8); - }); + expect(doneCount).toEqual(1); }); - describe('style normalizer', () => { - it('should normalize the style values that are animateTransitioned within an a transition animation', - () => { - const engine = makeEngine(new SuffixNormalizer('-normalized')); - - const trig = trigger('something', [ - state('on', style({height: 100})), state('off', style({height: 0})), - transition('on => off', animate(9876)) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'on'); - setProperty(element, engine, trig.name, 'off'); - engine.flush(); - - const player = MockAnimationDriver.log.pop() as MockAnimationPlayer; - expect(player.keyframes).toEqual([ - {'height-normalized': '100-normalized', offset: 0}, - {'height-normalized': '0-normalized', offset: 1} - ]); - }); - - it('should throw an error when normalization fails within a transition animation', () => { - const engine = makeEngine(new ExactCssValueNormalizer({left: '100px'})); - - const trig = trigger('something', [ - state('a', style({left: '0px', width: '200px'})), - state('b', style({left: '100px', width: '100px'})), transition('a => b', animate(9876)) - ]); - - registerTrigger(element, engine, trig); - setProperty(element, engine, trig.name, 'a'); - setProperty(element, engine, trig.name, 'b'); - - let errorMessage = ''; - try { - engine.flush(); - } catch (e) { - errorMessage = e.toString(); - } - - expect(errorMessage).toMatch(/Unable to animate due to the following errors:/); - expect(errorMessage).toMatch(/- The CSS property `left` is not allowed to be `0px`/); - expect(errorMessage).toMatch(/- The CSS property `width` is not allowed/); - }); + it('should only persist styles that exist in the final state styles and not the last keyframe', + () => { + const engine = makeEngine(); + const trig = trigger('something', [ + state('0', style({width: '0px'})), state('1', style({width: '100px'})), + transition('* => *', [animate(1000, style({height: '200px'}))]) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, '0'); + setProperty(element, engine, trig.name, '1'); + engine.flush(); + + const player = engine.players[0]!; + expect(element.style.width).not.toEqual('100px'); + + player.finish(); + expect(element.style.height).not.toEqual('200px'); + expect(element.style.width).toEqual('100px'); + }); + + it('should default to using styling from the `*` state if a matching state is not found', + () => { + const engine = makeEngine(); + const trig = trigger('something', [ + state('a', style({opacity: 0})), state('*', style({opacity: .5})), + transition('* => *', animate(1000)) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'a'); + setProperty(element, engine, trig.name, 'z'); + engine.flush(); + + engine.players[0].finish(); + expect(parseFloat(element.style.opacity)).toEqual(.5); + }); + + it('should treat `void` as `void`', () => { + const engine = makeEngine(); + const trig = trigger('something', [ + state('a', style({opacity: 0})), state('void', style({opacity: .8})), + transition('* => *', animate(1000)) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'a'); + setProperty(element, engine, trig.name, 'void'); + engine.flush(); + + engine.players[0].finish(); + expect(parseFloat(element.style.opacity)).toEqual(.8); }); + }); + + describe('style normalizer', () => { + it('should normalize the style values that are animateTransitioned within an a transition animation', + () => { + const engine = makeEngine(new SuffixNormalizer('-normalized')); + + const trig = trigger('something', [ + state('on', style({height: 100})), state('off', style({height: 0})), + transition('on => off', animate(9876)) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'on'); + setProperty(element, engine, trig.name, 'off'); + engine.flush(); + + const player = MockAnimationDriver.log.pop() as MockAnimationPlayer; + expect(player.keyframes).toEqual([ + {'height-normalized': '100-normalized', offset: 0}, + {'height-normalized': '0-normalized', offset: 1} + ]); + }); + + it('should throw an error when normalization fails within a transition animation', () => { + const engine = makeEngine(new ExactCssValueNormalizer({left: '100px'})); + + const trig = trigger('something', [ + state('a', style({left: '0px', width: '200px'})), + state('b', style({left: '100px', width: '100px'})), transition('a => b', animate(9876)) + ]); + + registerTrigger(element, engine, trig); + setProperty(element, engine, trig.name, 'a'); + setProperty(element, engine, trig.name, 'b'); + + let errorMessage = ''; + try { + engine.flush(); + } catch (e) { + errorMessage = e.toString(); + } - describe('view operations', () => { - it('should perform insert operations immediately ', () => { - const engine = makeEngine(); + expect(errorMessage).toMatch(/Unable to animate due to the following errors:/); + expect(errorMessage).toMatch(/- The CSS property `left` is not allowed to be `0px`/); + expect(errorMessage).toMatch(/- The CSS property `width` is not allowed/); + }); + }); - const child1 = document.createElement('div'); - const child2 = document.createElement('div'); - element.appendChild(child1); - element.appendChild(child2); + describe('view operations', () => { + it('should perform insert operations immediately ', () => { + const engine = makeEngine(); - element.appendChild(child1); - engine.insertNode(DEFAULT_NAMESPACE_ID, child1, element, true); - element.appendChild(child2); - engine.insertNode(DEFAULT_NAMESPACE_ID, child2, element, true); + const child1 = document.createElement('div'); + const child2 = document.createElement('div'); + element.appendChild(child1); + element.appendChild(child2); - expect(element.contains(child1)).toBe(true); - expect(element.contains(child2)).toBe(true); - }); + element.appendChild(child1); + engine.insertNode(DEFAULT_NAMESPACE_ID, child1, element, true); + element.appendChild(child2); + engine.insertNode(DEFAULT_NAMESPACE_ID, child2, element, true); - it('should not throw an error if a missing namespace is used', () => { - const engine = makeEngine(); - const ID = 'foo'; - const TRIGGER = 'fooTrigger'; - expect(() => { engine.trigger(ID, element, TRIGGER, 'something'); }).not.toThrow(); - }); + expect(element.contains(child1)).toBe(true); + expect(element.contains(child2)).toBe(true); + }); - it('should still apply state-styling to an element even if it is not yet inserted into the DOM', - () => { - const engine = makeEngine(); - const orphanElement = document.createElement('div'); - orphanElement.classList.add('orphan'); - - registerTrigger( - orphanElement, engine, trigger('trig', [ - state('go', style({opacity: 0.5})), transition('* => go', animate(1000)) - ])); - - setProperty(orphanElement, engine, 'trig', 'go'); - engine.flush(); - expect(engine.players.length).toEqual(0); - expect(orphanElement.style.opacity).toEqual('0.5'); - }); + it('should not throw an error if a missing namespace is used', () => { + const engine = makeEngine(); + const ID = 'foo'; + const TRIGGER = 'fooTrigger'; + expect(() => { + engine.trigger(ID, element, TRIGGER, 'something'); + }).not.toThrow(); }); + + it('should still apply state-styling to an element even if it is not yet inserted into the DOM', + () => { + const engine = makeEngine(); + const orphanElement = document.createElement('div'); + orphanElement.classList.add('orphan'); + + registerTrigger(orphanElement, engine, trigger('trig', [ + state('go', style({opacity: 0.5})), transition('* => go', animate(1000)) + ])); + + setProperty(orphanElement, engine, 'trig', 'go'); + engine.flush(); + expect(engine.players.length).toEqual(0); + expect(orphanElement.style.opacity).toEqual('0.5'); + }); }); +}); })(); class SuffixNormalizer extends AnimationStyleNormalizer { - constructor(private _suffix: string) { super(); } + constructor(private _suffix: string) { + super(); + } normalizePropertyName(propertyName: string, errors: string[]): string { return propertyName + this._suffix; @@ -698,7 +705,9 @@ class SuffixNormalizer extends AnimationStyleNormalizer { } class ExactCssValueNormalizer extends AnimationStyleNormalizer { - constructor(private _allowedValues: {[propName: string]: any}) { super(); } + constructor(private _allowedValues: {[propName: string]: any}) { + super(); + } normalizePropertyName(propertyName: string, errors: string[]): string { if (!this._allowedValues[propertyName]) { diff --git a/packages/animations/browser/test/render/web_animations/web_animations_player_spec.ts b/packages/animations/browser/test/render/web_animations/web_animations_player_spec.ts index c8e44bc11ca83..cbcdceda78f63 100644 --- a/packages/animations/browser/test/render/web_animations/web_animations_player_spec.ts +++ b/packages/animations/browser/test/render/web_animations/web_animations_player_spec.ts @@ -13,7 +13,9 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat let innerPlayer: MockDomAnimation|null = null; beforeEach(() => { element = {}; - element['animate'] = () => { return innerPlayer = new MockDomAnimation(); }; + element['animate'] = () => { + return innerPlayer = new MockDomAnimation(); + }; }); describe('WebAnimationsPlayer tests', () => { @@ -26,7 +28,7 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat const player = new WebAnimationsPlayer(element, keyframes, {duration: 1000}); player.init(); - const p = innerPlayer !; + const p = innerPlayer!; expect(p.log).toEqual(['pause']); player.play(); @@ -42,7 +44,7 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat const player = new WebAnimationsPlayer(element, keyframes, {duration: 1000}); player.play(); - const p = innerPlayer !; + const p = innerPlayer!; expect(p.log).toEqual(['play']); }); @@ -70,10 +72,18 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat class MockDomAnimation implements DOMAnimation { log: string[] = []; - cancel(): void { this.log.push('cancel'); } - play(): void { this.log.push('play'); } - pause(): void { this.log.push('pause'); } - finish(): void { this.log.push('finish'); } + cancel(): void { + this.log.push('cancel'); + } + play(): void { + this.log.push('play'); + } + pause(): void { + this.log.push('pause'); + } + finish(): void { + this.log.push('finish'); + } onfinish: Function = () => {}; position: number = 0; currentTime: number = 0; diff --git a/packages/animations/browser/test/shared.ts b/packages/animations/browser/test/shared.ts index 12904ef157232..f89b4ddf79341 100644 --- a/packages/animations/browser/test/shared.ts +++ b/packages/animations/browser/test/shared.ts @@ -21,8 +21,8 @@ export function makeTrigger( const triggerAst = buildAnimationAst(driver, triggerData, errors) as TriggerAst; if (!skipErrors && errors.length) { const LINE_START = '\n - '; - throw new Error( - `Animation parsing for the ${name} trigger have failed:${LINE_START}${errors.join(LINE_START)}`); + throw new Error(`Animation parsing for the ${name} trigger have failed:${LINE_START}${ + errors.join(LINE_START)}`); } return buildTrigger(name, triggerAst); } diff --git a/packages/animations/browser/testing/src/mock_animation_driver.ts b/packages/animations/browser/testing/src/mock_animation_driver.ts index 367d9e4a0128f..d21fc192d3e40 100644 --- a/packages/animations/browser/testing/src/mock_animation_driver.ts +++ b/packages/animations/browser/testing/src/mock_animation_driver.ts @@ -5,7 +5,7 @@ * 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 {AUTO_STYLE, AnimationPlayer, NoopAnimationPlayer, ɵStyleData} from '@angular/animations'; +import {AnimationPlayer, AUTO_STYLE, NoopAnimationPlayer, ɵStyleData} from '@angular/animations'; import {AnimationDriver, ɵallowPreviousPlayerStylesMerge as allowPreviousPlayerStylesMerge, ɵcontainsElement as containsElement, ɵinvokeQuery as invokeQuery, ɵmatchesElement as matchesElement, ɵvalidateStyleProperty as validateStyleProperty} from '@angular/animations/browser'; @@ -15,13 +15,17 @@ import {AnimationDriver, ɵallowPreviousPlayerStylesMerge as allowPreviousPlayer export class MockAnimationDriver implements AnimationDriver { static log: AnimationPlayer[] = []; - validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); } + validateStyleProperty(prop: string): boolean { + return validateStyleProperty(prop); + } matchesElement(element: any, selector: string): boolean { return matchesElement(element, selector); } - containsElement(elm1: any, elm2: any): boolean { return containsElement(elm1, elm2); } + containsElement(elm1: any, elm2: any): boolean { + return containsElement(elm1, elm2); + } query(element: any, selector: string, multi: boolean): any[] { return invokeQuery(element, selector, multi); @@ -32,7 +36,7 @@ export class MockAnimationDriver implements AnimationDriver { } animate( - element: any, keyframes: {[key: string]: string | number}[], duration: number, delay: number, + element: any, keyframes: {[key: string]: string|number}[], duration: number, delay: number, easing: string, previousPlayers: any[] = []): MockAnimationPlayer { const player = new MockAnimationPlayer(element, keyframes, duration, delay, easing, previousPlayers); @@ -47,12 +51,12 @@ export class MockAnimationDriver implements AnimationDriver { export class MockAnimationPlayer extends NoopAnimationPlayer { private __finished = false; private __started = false; - public previousStyles: {[key: string]: string | number} = {}; + public previousStyles: {[key: string]: string|number} = {}; private _onInitFns: (() => any)[] = []; public currentSnapshot: ɵStyleData = {}; constructor( - public element: any, public keyframes: {[key: string]: string | number}[], + public element: any, public keyframes: {[key: string]: string|number}[], public duration: number, public delay: number, public easing: string, public previousPlayers: any[]) { super(duration, delay); @@ -68,7 +72,9 @@ export class MockAnimationPlayer extends NoopAnimationPlayer { } /* @internal */ - onInit(fn: () => any) { this._onInitFns.push(fn); } + onInit(fn: () => any) { + this._onInitFns.push(fn); + } /* @internal */ init() { @@ -95,7 +101,9 @@ export class MockAnimationPlayer extends NoopAnimationPlayer { this.__started = true; } - hasStarted() { return this.__started; } + hasStarted() { + return this.__started; + } beforeDestroy() { const captures: ɵStyleData = {}; diff --git a/packages/animations/src/animation_metadata.ts b/packages/animations/src/animation_metadata.ts index 89492cf68845e..079744bf7391f 100755 --- a/packages/animations/src/animation_metadata.ts +++ b/packages/animations/src/animation_metadata.ts @@ -9,7 +9,9 @@ /** * Represents a set of CSS styles for use in an animation style. */ -export interface ɵStyleData { [key: string]: string|number; } +export interface ɵStyleData { + [key: string]: string|number; +} /** * Represents animation-step timing parameters for an animation step. @@ -67,10 +69,10 @@ export declare interface AnimationOptions { */ delay?: number|string; /** - * A set of developer-defined parameters that modify styling and timing - * when an animation action starts. An array of key-value pairs, where the provided value - * is used as a default. - */ + * A set of developer-defined parameters that modify styling and timing + * when an animation action starts. An array of key-value pairs, where the provided value + * is used as a default. + */ params?: {[name: string]: any}; } @@ -81,7 +83,9 @@ export declare interface AnimationOptions { * * @publicApi */ -export declare interface AnimateChildOptions extends AnimationOptions { duration?: number|string; } +export declare interface AnimateChildOptions extends AnimationOptions { + duration?: number|string; +} /** * @description Constants for the categories of parameters that can be defined for animations. @@ -171,7 +175,9 @@ export const AUTO_STYLE = '*'; * * @publicApi */ -export interface AnimationMetadata { type: AnimationMetadataType; } +export interface AnimationMetadata { + type: AnimationMetadataType; +} /** * Contains an animation trigger. Instantiated and returned by the @@ -181,8 +187,8 @@ export interface AnimationMetadata { type: AnimationMetadataType; } */ export interface AnimationTriggerMetadata extends AnimationMetadata { /** - * The trigger name, used to associate it with an element. Unique within the component. - */ + * The trigger name, used to associate it with an element. Unique within the component. + */ name: string; /** * An animation definition object, containing an array of state and transition declarations. @@ -654,8 +660,9 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati * @publicApi */ export function animate( - timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata | - null = null): AnimationAnimateMetadata { + timings: string|number, + styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata|null = + null): AnimationAnimateMetadata { return {type: AnimationMetadataType.Animate, styles, timings}; } @@ -693,7 +700,7 @@ export function animate( * @publicApi */ export function group( - steps: AnimationMetadata[], options: AnimationOptions | null = null): AnimationGroupMetadata { + steps: AnimationMetadata[], options: AnimationOptions|null = null): AnimationGroupMetadata { return {type: AnimationMetadataType.Group, steps, options}; } @@ -721,7 +728,8 @@ export function group( * @usageNotes * When you pass an array of steps to a * `transition()` call, the steps run sequentially by default. - * Compare this to the `{@link animations/group group()}` call, which runs animation steps in parallel. + * Compare this to the `{@link animations/group group()}` call, which runs animation steps in + *parallel. * * When a sequence is used within a `{@link animations/group group()}` or a `transition()` call, * execution continues to the next instruction only after each of the inner animation @@ -729,8 +737,8 @@ export function group( * * @publicApi **/ -export function sequence(steps: AnimationMetadata[], options: AnimationOptions | null = null): - AnimationSequenceMetadata { +export function sequence( + steps: AnimationMetadata[], options: AnimationOptions|null = null): AnimationSequenceMetadata { return {type: AnimationMetadataType.Sequence, steps, options}; } @@ -773,9 +781,8 @@ export function sequence(steps: AnimationMetadata[], options: AnimationOptions | * * @publicApi **/ -export function style( - tokens: '*' | {[key: string]: string | number} | - Array<'*'|{[key: string]: string | number}>): AnimationStyleMetadata { +export function style(tokens: '*'|{[key: string]: string | number}| + Array<'*'|{[key: string]: string | number}>): AnimationStyleMetadata { return {type: AnimationMetadataType.Style, styles: tokens, offset: null}; } @@ -1032,10 +1039,10 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe * @publicApi **/ export function transition( - stateChangeExpr: string | ((fromState: string, toState: string, element?: any, - params?: {[key: string]: any}) => boolean), - steps: AnimationMetadata | AnimationMetadata[], - options: AnimationOptions | null = null): AnimationTransitionMetadata { + stateChangeExpr: string| + ((fromState: string, toState: string, element?: any, params?: {[key: string]: any}) => boolean), + steps: AnimationMetadata|AnimationMetadata[], + options: AnimationOptions|null = null): AnimationTransitionMetadata { return {type: AnimationMetadataType.Transition, expr: stateChangeExpr, animation: steps, options}; } @@ -1085,8 +1092,8 @@ export function transition( * @publicApi */ export function animation( - steps: AnimationMetadata | AnimationMetadata[], - options: AnimationOptions | null = null): AnimationReferenceMetadata { + steps: AnimationMetadata|AnimationMetadata[], + options: AnimationOptions|null = null): AnimationReferenceMetadata { return {type: AnimationMetadataType.Reference, animation: steps, options}; } @@ -1109,7 +1116,7 @@ export function animation( * * @publicApi */ -export function animateChild(options: AnimateChildOptions | null = null): +export function animateChild(options: AnimateChildOptions|null = null): AnimationAnimateChildMetadata { return {type: AnimationMetadataType.AnimateChild, options}; } @@ -1126,7 +1133,7 @@ export function animateChild(options: AnimateChildOptions | null = null): */ export function useAnimation( animation: AnimationReferenceMetadata, - options: AnimationOptions | null = null): AnimationAnimateRefMetadata { + options: AnimationOptions|null = null): AnimationAnimateRefMetadata { return {type: AnimationMetadataType.AnimateRef, animation, options}; } @@ -1179,7 +1186,7 @@ export function useAnimation( * ### Usage Example * * The following example queries for inner elements and animates them - * individually using `animate()`. + * individually using `animate()`. * * ```typescript * @Component({ @@ -1218,8 +1225,8 @@ export function useAnimation( * @publicApi */ export function query( - selector: string, animation: AnimationMetadata | AnimationMetadata[], - options: AnimationQueryOptions | null = null): AnimationQueryMetadata { + selector: string, animation: AnimationMetadata|AnimationMetadata[], + options: AnimationQueryOptions|null = null): AnimationQueryMetadata { return {type: AnimationMetadataType.Query, selector, animation, options}; } @@ -1303,8 +1310,7 @@ export function query( * * @publicApi */ -export function stagger( - timings: string | number, - animation: AnimationMetadata | AnimationMetadata[]): AnimationStaggerMetadata { +export function stagger(timings: string|number, animation: AnimationMetadata|AnimationMetadata[]): + AnimationStaggerMetadata { return {type: AnimationMetadataType.Stagger, timings, animation}; } diff --git a/packages/animations/src/animations.ts b/packages/animations/src/animations.ts index 7f8acfadc9c2d..66482a0d79fc1 100644 --- a/packages/animations/src/animations.ts +++ b/packages/animations/src/animations.ts @@ -13,7 +13,7 @@ */ export {AnimationBuilder, AnimationFactory} from './animation_builder'; export {AnimationEvent} from './animation_event'; -export {AUTO_STYLE, AnimateChildOptions, AnimateTimings, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, animate, animateChild, animation, group, keyframes, query, sequence, stagger, state, style, transition, trigger, useAnimation, ɵStyleData} from './animation_metadata'; +export {animate, animateChild, AnimateChildOptions, AnimateTimings, animation, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, AUTO_STYLE, group, keyframes, query, sequence, stagger, state, style, transition, trigger, useAnimation, ɵStyleData} from './animation_metadata'; export {AnimationPlayer, NoopAnimationPlayer} from './players/animation_player'; export * from './private_export'; diff --git a/packages/animations/src/players/animation_group_player.ts b/packages/animations/src/players/animation_group_player.ts index c1f6a40c23aba..79f6611735411 100644 --- a/packages/animations/src/players/animation_group_player.ts +++ b/packages/animations/src/players/animation_group_player.ts @@ -69,9 +69,13 @@ export class AnimationGroupPlayer implements AnimationPlayer { } } - init(): void { this.players.forEach(player => player.init()); } + init(): void { + this.players.forEach(player => player.init()); + } - onStart(fn: () => void): void { this._onStartFns.push(fn); } + onStart(fn: () => void): void { + this._onStartFns.push(fn); + } private _onStart() { if (!this.hasStarted()) { @@ -81,11 +85,17 @@ export class AnimationGroupPlayer implements AnimationPlayer { } } - onDone(fn: () => void): void { this._onDoneFns.push(fn); } + onDone(fn: () => void): void { + this._onDoneFns.push(fn); + } - onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); } + onDestroy(fn: () => void): void { + this._onDestroyFns.push(fn); + } - hasStarted() { return this._started; } + hasStarted() { + return this._started; + } play() { if (!this.parentPlayer) { @@ -95,16 +105,22 @@ export class AnimationGroupPlayer implements AnimationPlayer { this.players.forEach(player => player.play()); } - pause(): void { this.players.forEach(player => player.pause()); } + pause(): void { + this.players.forEach(player => player.pause()); + } - restart(): void { this.players.forEach(player => player.restart()); } + restart(): void { + this.players.forEach(player => player.restart()); + } finish(): void { this._onFinish(); this.players.forEach(player => player.finish()); } - destroy(): void { this._onDestroy(); } + destroy(): void { + this._onDestroy(); + } private _onDestroy() { if (!this._destroyed) { diff --git a/packages/animations/src/players/animation_player.ts b/packages/animations/src/players/animation_player.ts index 3a183db7c2012..6ec35d6b81c39 100644 --- a/packages/animations/src/players/animation_player.ts +++ b/packages/animations/src/players/animation_player.ts @@ -94,11 +94,13 @@ export interface AnimationPlayer { * Provides a callback to invoke before the animation is destroyed. */ beforeDestroy?: () => any; - /** @internal + /** + * @internal * Internal */ triggerCallback?: (phaseName: string) => void; - /** @internal + /** + * @internal * Internal */ disabled?: boolean; @@ -124,7 +126,9 @@ export class NoopAnimationPlayer implements AnimationPlayer { private _finished = false; public parentPlayer: AnimationPlayer|null = null; public readonly totalTime: number; - constructor(duration: number = 0, delay: number = 0) { this.totalTime = duration + delay; } + constructor(duration: number = 0, delay: number = 0) { + this.totalTime = duration + delay; + } private _onFinish() { if (!this._finished) { this._finished = true; @@ -132,10 +136,18 @@ export class NoopAnimationPlayer implements AnimationPlayer { this._onDoneFns = []; } } - onStart(fn: () => void): void { this._onStartFns.push(fn); } - onDone(fn: () => void): void { this._onDoneFns.push(fn); } - onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); } - hasStarted(): boolean { return this._started; } + onStart(fn: () => void): void { + this._onStartFns.push(fn); + } + onDone(fn: () => void): void { + this._onDoneFns.push(fn); + } + onDestroy(fn: () => void): void { + this._onDestroyFns.push(fn); + } + hasStarted(): boolean { + return this._started; + } init(): void {} play(): void { if (!this.hasStarted()) { @@ -146,7 +158,9 @@ export class NoopAnimationPlayer implements AnimationPlayer { } /** @internal */ - triggerMicrotask() { scheduleMicroTask(() => this._onFinish()); } + triggerMicrotask() { + scheduleMicroTask(() => this._onFinish()); + } private _onStart() { this._onStartFns.forEach(fn => fn()); @@ -155,7 +169,9 @@ export class NoopAnimationPlayer implements AnimationPlayer { pause(): void {} restart(): void {} - finish(): void { this._onFinish(); } + finish(): void { + this._onFinish(); + } destroy(): void { if (!this._destroyed) { this._destroyed = true; @@ -169,7 +185,9 @@ export class NoopAnimationPlayer implements AnimationPlayer { } reset(): void {} setPosition(position: number): void {} - getPosition(): number { return 0; } + getPosition(): number { + return 0; + } /** @internal */ triggerCallback(phaseName: string): void { diff --git a/packages/animations/test/animation_player_spec.ts b/packages/animations/test/animation_player_spec.ts index 7ad7152d6018f..2bf205fcbb9cc 100644 --- a/packages/animations/test/animation_player_spec.ts +++ b/packages/animations/test/animation_player_spec.ts @@ -69,7 +69,9 @@ import {scheduleMicroTask} from '../src/util'; const log: string[] = []; const player = new NoopAnimationPlayer(); - player.onStart(() => { scheduleMicroTask(() => log.push('started')); }); + player.onStart(() => { + scheduleMicroTask(() => log.push('started')); + }); player.onDone(() => log.push('done')); expect(log).toEqual([]); diff --git a/packages/bazel/src/api-extractor/index.ts b/packages/bazel/src/api-extractor/index.ts index 71e46ca72c148..b89304cfe8998 100644 --- a/packages/bazel/src/api-extractor/index.ts +++ b/packages/bazel/src/api-extractor/index.ts @@ -38,7 +38,7 @@ export function runMain( // API extractor doesn't always support the version of TypeScript used in the repo // example: at the moment it is not compatable with 3.2 // to use the internal TypeScript we shall not create a program but rather pass a parsed tsConfig. - const parsedTsConfig = parsedConfig !.config as any; + const parsedTsConfig = parsedConfig!.config as any; const compilerOptions = parsedTsConfig.compilerOptions; for (const [key, values] of Object.entries<string[]>(compilerOptions.paths)) { if (key === '*') { @@ -113,8 +113,8 @@ api-extractor: running with const dtsBundleOuts = dtsBundleOut.split(','); if (entryPoints.length !== entryPoints.length) { - throw new Error( - `Entry points count (${entryPoints.length}) does not match Bundle out count (${dtsBundleOuts.length})`); + throw new Error(`Entry points count (${entryPoints.length}) does not match Bundle out count (${ + dtsBundleOuts.length})`); } for (let i = 0; i < entryPoints.length; i++) { diff --git a/packages/bazel/src/builders/bazel.ts b/packages/bazel/src/builders/bazel.ts index e2dd5cb82f624..b737c9c969ed6 100644 --- a/packages/bazel/src/builders/bazel.ts +++ b/packages/bazel/src/builders/bazel.ts @@ -9,12 +9,12 @@ /// <reference types='node'/> import {spawn} from 'child_process'; -import {copyFileSync, existsSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync} from 'fs'; +import {copyFileSync, existsSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync} from 'fs'; import {platform} from 'os'; import {dirname, join, normalize} from 'path'; -export type Executable = 'bazel' | 'ibazel'; -export type Command = 'build' | 'test' | 'run' | 'coverage' | 'query'; +export type Executable = 'bazel'|'ibazel'; +export type Command = 'build'|'test'|'run'|'coverage'|'query'; /** * Spawn the Bazel process. Trap SINGINT to make sure Bazel process is killed. diff --git a/packages/bazel/src/builders/files/WORKSPACE.template b/packages/bazel/src/builders/files/WORKSPACE.template index 873c0fe46b208..85bb2ef5dd535 100644 --- a/packages/bazel/src/builders/files/WORKSPACE.template +++ b/packages/bazel/src/builders/files/WORKSPACE.template @@ -15,8 +15,8 @@ workspace( load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -RULES_NODEJS_VERSION = "1.4.1" -RULES_NODEJS_SHA256 = "2eca5b934dee47b5ff304f502ae187c40ec4e33e12bcbce872a2eeb786e23269" +RULES_NODEJS_VERSION = "1.6.0" +RULES_NODEJS_SHA256 = "f9e7b9f42ae202cc2d2ce6d698ccb49a9f7f7ea572a78fd451696d03ef2ee116" http_archive( name = "build_bazel_rules_nodejs", sha256 = RULES_NODEJS_SHA256, diff --git a/packages/bazel/src/builders/index.ts b/packages/bazel/src/builders/index.ts index 99d6549f5f989..54ac7e90a485b 100644 --- a/packages/bazel/src/builders/index.ts +++ b/packages/bazel/src/builders/index.ts @@ -13,27 +13,29 @@ import {JsonObject} from '@angular-devkit/core'; import {checkInstallation, copyBazelFiles, deleteBazelFiles, getTemplateDir, runBazel} from './bazel'; import {Schema} from './schema'; -async function _bazelBuilder(options: JsonObject & Schema, context: BuilderContext, ): - Promise<BuilderOutput> { - const {logger, workspaceRoot} = context; - const {bazelCommand, leaveBazelFilesOnDisk, targetLabel, watch} = options; - const executable = watch ? 'ibazel' : 'bazel'; - const binary = checkInstallation(executable, workspaceRoot); - const templateDir = getTemplateDir(workspaceRoot); - const bazelFiles = copyBazelFiles(workspaceRoot, templateDir); +async function _bazelBuilder( + options: JsonObject&Schema, + context: BuilderContext, + ): Promise<BuilderOutput> { + const {logger, workspaceRoot} = context; + const {bazelCommand, leaveBazelFilesOnDisk, targetLabel, watch} = options; + const executable = watch ? 'ibazel' : 'bazel'; + const binary = checkInstallation(executable, workspaceRoot); + const templateDir = getTemplateDir(workspaceRoot); + const bazelFiles = copyBazelFiles(workspaceRoot, templateDir); - try { - const flags: string[] = []; - await runBazel(workspaceRoot, binary, bazelCommand, targetLabel, flags); - return {success: true}; - } catch (err) { - logger.error(err.message); - return {success: false}; - } finally { - if (!leaveBazelFilesOnDisk) { - deleteBazelFiles(bazelFiles); // this will never throw - } - } + try { + const flags: string[] = []; + await runBazel(workspaceRoot, binary, bazelCommand, targetLabel, flags); + return {success: true}; + } catch (err) { + logger.error(err.message); + return {success: false}; + } finally { + if (!leaveBazelFilesOnDisk) { + deleteBazelFiles(bazelFiles); // this will never throw } + } +} export default createBuilder(_bazelBuilder); diff --git a/packages/bazel/src/ng_package/packager.ts b/packages/bazel/src/ng_package/packager.ts index c4f17fb77f22c..10d269e0c5360 100644 --- a/packages/bazel/src/ng_package/packager.ts +++ b/packages/bazel/src/ng_package/packager.ts @@ -22,7 +22,9 @@ function main(args: string[]): number { const paramFilePath = args[0]; // Bazel params may be surrounded with quotes - function unquoteParameter(s: string) { return s.replace(/^'(.*)'$/, '$1'); } + function unquoteParameter(s: string) { + return s.replace(/^'(.*)'$/, '$1'); + } // Parameters are specified in the file one per line. const params = fs.readFileSync(paramFilePath, 'utf-8').split('\n').map(unquoteParameter); @@ -109,7 +111,7 @@ function main(args: string[]): number { * @param inputPath Path to the file in the input tree. * @param fileContent Content of the file. */ - function writeFileFromInputPath(inputPath: string, fileContent: string | Buffer) { + function writeFileFromInputPath(inputPath: string, fileContent: string|Buffer) { // We want the relative path from the given file to its ancestor "root" directory. // This root depends on whether the file lives in the source tree (srcDir) as a basic file // input to ng_package, the bin output tree (binDir) as the output of another rule, or @@ -164,9 +166,15 @@ function main(args: string[]): number { esm2015.forEach(file => writeEsmFile(file, '', 'esm2015')); esm5.forEach(file => writeEsmFile(file, '.esm5', 'esm5')); - bundles.forEach(bundle => { copyFile(bundle, out, 'bundles'); }); - fesm2015.forEach(file => { copyFile(file, out, 'fesm2015'); }); - fesm5.forEach(file => { copyFile(file, out, 'fesm5'); }); + bundles.forEach(bundle => { + copyFile(bundle, out, 'bundles'); + }); + fesm2015.forEach(file => { + copyFile(file, out, 'fesm2015'); + }); + fesm5.forEach(file => { + copyFile(file, out, 'fesm5'); + }); // Copy all type definitions into the package. This is necessary so that developers can use // the package with type definitions. @@ -419,14 +427,16 @@ export * from '${srcDirRelative(inputPath, typingsFile.replace(/\.d\.tsx?$/, '') * Normalizes the specified path by replacing backslash separators with Posix * forward slash separators. */ - function normalizeSeparators(path: string): string { return path.replace(/\\/g, '/'); } + function normalizeSeparators(path: string): string { + return path.replace(/\\/g, '/'); + } /** - * Rewires metadata to point to the flattened dts file. - * - * @param metadataPath the metadata file path - * @param typingsPath the typings bundle entrypoint - */ + * Rewires metadata to point to the flattened dts file. + * + * @param metadataPath the metadata file path + * @param typingsPath the typings bundle entrypoint + */ function rewireMetadata(metadataPath: string, typingsPath: string): string { const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8')); @@ -470,7 +480,7 @@ export function newArray<T>(size: number, value: T): T[]; export function newArray<T>(size: number, value?: T): T[] { const list: T[] = []; for (let i = 0; i < size; i++) { - list.push(value !); + list.push(value!); } return list; } diff --git a/packages/bazel/src/ngc-wrapped/index.ts b/packages/bazel/src/ngc-wrapped/index.ts index e78b1064548f4..a80c00cd846db 100644 --- a/packages/bazel/src/ngc-wrapped/index.ts +++ b/packages/bazel/src/ngc-wrapped/index.ts @@ -7,7 +7,7 @@ */ import * as ng from '@angular/compiler-cli'; -import {BazelOptions, CachedFileLoader, CompilerHost, FileCache, FileLoader, UncachedFileLoader, constructManifest, debug, parseTsconfig, resolveNormalizedPath, runAsWorker, runWorkerLoop} from '@bazel/typescript'; +import {BazelOptions, CachedFileLoader, CompilerHost, constructManifest, debug, FileCache, FileLoader, parseTsconfig, resolveNormalizedPath, runAsWorker, runWorkerLoop, UncachedFileLoader} from '@bazel/typescript'; import * as fs from 'fs'; import * as path from 'path'; import * as tsickle from 'tsickle'; @@ -123,7 +123,12 @@ export function runOneBuild(args: string[], inputs?: {[path: string]: string}): const {diagnostics} = compile({ allDepsCompiledWithBazel: ALL_DEPS_COMPILED_WITH_BAZEL, useManifestPathsAsModuleName: _useManifestPathsAsModuleName, - expectedOuts: expectedOut, compilerOpts, tsHost, bazelOpts, files, inputs, + expectedOuts: expectedOut, + compilerOpts, + tsHost, + bazelOpts, + files, + inputs, }); if (diagnostics.length) { console.error(ng.formatDiagnostics(diagnostics)); @@ -142,16 +147,24 @@ export function relativeToRootDirs(filePath: string, rootDirs: string[]): string return filePath; } -export function compile({allDepsCompiledWithBazel = true, useManifestPathsAsModuleName, - compilerOpts, tsHost, bazelOpts, files, inputs, expectedOuts, - gatherDiagnostics, bazelHost}: { +export function compile({ + allDepsCompiledWithBazel = true, + useManifestPathsAsModuleName, + compilerOpts, + tsHost, + bazelOpts, + files, + inputs, + expectedOuts, + gatherDiagnostics, + bazelHost +}: { allDepsCompiledWithBazel?: boolean, - useManifestPathsAsModuleName?: boolean, - compilerOpts: ng.CompilerOptions, - tsHost: ts.CompilerHost, inputs?: {[path: string]: string}, - bazelOpts: BazelOptions, - files: string[], - expectedOuts: string[], + useManifestPathsAsModuleName?: boolean, compilerOpts: ng.CompilerOptions, tsHost: ts.CompilerHost, + inputs?: {[path: string]: string}, + bazelOpts: BazelOptions, + files: string[], + expectedOuts: string[], gatherDiagnostics?: (program: ng.Program) => ng.Diagnostics, bazelHost?: CompilerHost, }): {diagnostics: ng.Diagnostics, program: ng.Program} { @@ -362,7 +375,7 @@ export function compile({allDepsCompiledWithBazel = true, useManifestPathsAsModu if ((compilerOpts.module === ts.ModuleKind.UMD || compilerOpts.module === ts.ModuleKind.AMD) && ngHost.amdModuleName) { - return ngHost.amdModuleName({ fileName: importedFilePath } as ts.SourceFile); + return ngHost.amdModuleName({fileName: importedFilePath} as ts.SourceFile); } // If no AMD module name has been set for the source file by the `@bazel/typescript` compiler @@ -434,8 +447,10 @@ export function compile({allDepsCompiledWithBazel = true, useManifestPathsAsModu const {diagnostics, emitResult, program} = ng.performCompilation({ rootNames: files, options: compilerOpts, - host: ngHost, emitCallback, - mergeEmitResultsCallback: tsickle.mergeEmitResults, gatherDiagnostics + host: ngHost, + emitCallback, + mergeEmitResultsCallback: tsickle.mergeEmitResults, + gatherDiagnostics }); const tsickleEmitResult = emitResult as tsickle.EmitResult; let externs = '/** @externs */\n'; @@ -512,9 +527,9 @@ function convertToForwardSlashPath(filePath: string): string { function gatherDiagnosticsForInputsOnly( options: ng.CompilerOptions, bazelOpts: BazelOptions, - ngProgram: ng.Program): (ng.Diagnostic | ts.Diagnostic)[] { + ngProgram: ng.Program): (ng.Diagnostic|ts.Diagnostic)[] { const tsProgram = ngProgram.getTsProgram(); - const diagnostics: (ng.Diagnostic | ts.Diagnostic)[] = []; + const diagnostics: (ng.Diagnostic|ts.Diagnostic)[] = []; // These checks mirror ts.getPreEmitDiagnostics, with the important // exception of avoiding b/30708240, which is that if you call // program.getDeclarationDiagnostics() it somehow corrupts the emit. diff --git a/packages/bazel/src/schematics/ng-add/index.ts b/packages/bazel/src/schematics/ng-add/index.ts index 2998bbf16c4ab..b5bdd6dad0661 100644 --- a/packages/bazel/src/schematics/ng-add/index.ts +++ b/packages/bazel/src/schematics/ng-add/index.ts @@ -9,10 +9,10 @@ */ import {JsonAstObject, parseJsonAst} from '@angular-devkit/core'; -import {Rule, SchematicContext, SchematicsException, Tree, apply, applyTemplates, chain, mergeWith, url} from '@angular-devkit/schematics'; +import {apply, applyTemplates, chain, mergeWith, Rule, SchematicContext, SchematicsException, Tree, url} from '@angular-devkit/schematics'; import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks'; import {getWorkspace, getWorkspacePath} from '@schematics/angular/utility/config'; -import {NodeDependencyType, addPackageJsonDependency, getPackageJsonDependency, removePackageJsonDependency} from '@schematics/angular/utility/dependencies'; +import {addPackageJsonDependency, getPackageJsonDependency, NodeDependencyType, removePackageJsonDependency} from '@schematics/angular/utility/dependencies'; import {findPropertyInAstObject, insertPropertyInAstObjectInOrder} from '@schematics/angular/utility/json-utils'; import {validateProjectName} from '@schematics/angular/utility/validation'; @@ -40,11 +40,11 @@ function addDevDependenciesToPackageJson(options: Schema) { ['@angular/bazel', angularCore.version], ['@bazel/bazel', '2.1.0'], ['@bazel/ibazel', '0.12.3'], - ['@bazel/karma', '1.4.1'], - ['@bazel/protractor', '1.4.1'], - ['@bazel/rollup', '1.4.1'], - ['@bazel/terser', '1.4.1'], - ['@bazel/typescript', '1.4.1'], + ['@bazel/karma', '1.6.0'], + ['@bazel/protractor', '1.6.0'], + ['@bazel/rollup', '1.6.0'], + ['@bazel/terser', '1.6.0'], + ['@bazel/typescript', '1.6.0'], ['history-server', '1.3.1'], ['html-insert-assets', '0.5.0'], ['karma', '4.4.1'], @@ -137,7 +137,7 @@ function updateGitignore() { */ function updateAngularJsonToUseBazelBuilder(options: Schema): Rule { return (host: Tree) => { - const name = options.name !; + const name = options.name!; const workspacePath = getWorkspacePath(host); if (!workspacePath) { throw new Error('Could not find angular.json'); @@ -235,7 +235,8 @@ function backupAngularJson(): Rule { return; } host.create( - `${workspacePath}.bak`, '// This is a backup file of the original angular.json. ' + + `${workspacePath}.bak`, + '// This is a backup file of the original angular.json. ' + 'This file is needed in case you want to revert to the workflow without Bazel.\n\n' + host.read(workspacePath)); }; diff --git a/packages/bazel/src/schematics/ng-add/index_spec.ts b/packages/bazel/src/schematics/ng-add/index_spec.ts index 133584ac9e109..7dc0f7ca58fdf 100644 --- a/packages/bazel/src/schematics/ng-add/index_spec.ts +++ b/packages/bazel/src/schematics/ng-add/index_spec.ts @@ -10,7 +10,6 @@ import {HostTree} from '@angular-devkit/schematics'; import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing'; describe('ng-add schematic', () => { - const defaultOptions = {name: 'demo'}; let host: UnitTestTree; let schematicRunner: SchematicTestRunner; @@ -61,7 +60,7 @@ describe('ng-add schematic', () => { new SchematicTestRunner('@angular/bazel', require.resolve('../collection.json')); }); - it('throws if package.json is not found', async() => { + it('throws if package.json is not found', async () => { expect(host.files).toContain('/package.json'); host.delete('/package.json'); @@ -76,7 +75,7 @@ describe('ng-add schematic', () => { expect(message).toBe('Could not read package.json.'); }); - it('throws if angular.json is not found', async() => { + it('throws if angular.json is not found', async () => { expect(host.files).toContain('/angular.json'); host.delete('/angular.json'); @@ -91,7 +90,7 @@ describe('ng-add schematic', () => { expect(message).toBe('Could not find angular.json'); }); - it('should add @angular/bazel to package.json dependencies', async() => { + it('should add @angular/bazel to package.json dependencies', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const {files} = host; expect(files).toContain('/package.json'); @@ -106,7 +105,7 @@ describe('ng-add schematic', () => { expect(Object.keys(json.devDependencies)).toContain(bazel); }); - it('should add @bazel/* dev dependencies', async() => { + it('should add @bazel/* dev dependencies', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const content = host.readContent('/package.json'); const json = JSON.parse(content); @@ -118,7 +117,7 @@ describe('ng-add schematic', () => { expect(devDeps).toContain('@bazel/typescript'); }); - it('should replace an existing dev dependency', async() => { + it('should replace an existing dev dependency', async () => { expect(host.files).toContain('/package.json'); const packageJson = JSON.parse(host.readContent('/package.json')); packageJson.devDependencies['@angular/bazel'] = '4.2.42'; @@ -126,12 +125,12 @@ describe('ng-add schematic', () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const content = host.readContent('/package.json'); // It is possible that a dep gets added twice if the package already exists. - expect(content.match(/@angular\/bazel/g) !.length).toEqual(1); + expect(content.match(/@angular\/bazel/g)!.length).toEqual(1); const json = JSON.parse(content); expect(json.devDependencies['@angular/bazel']).toBe('1.2.3'); }); - it('should remove an existing dependency', async() => { + it('should remove an existing dependency', async () => { expect(host.files).toContain('/package.json'); const packageJson = JSON.parse(host.readContent('/package.json')); packageJson.dependencies['@angular/bazel'] = '4.2.42'; @@ -144,7 +143,7 @@ describe('ng-add schematic', () => { expect(json.devDependencies['@angular/bazel']).toBe('1.2.3'); }); - it('should remove unneeded dependencies', async() => { + it('should remove unneeded dependencies', async () => { const packageJson = JSON.parse(host.readContent('/package.json')); packageJson.devDependencies['@angular-devkit/build-angular'] = '1.2.3'; host.overwrite('/package.json', JSON.stringify(packageJson)); @@ -154,7 +153,7 @@ describe('ng-add schematic', () => { expect(json.devDependencies['angular-devkit/build-angular']).toBeUndefined(); }); - it('should append to scripts.postinstall if it already exists', async() => { + it('should append to scripts.postinstall if it already exists', async () => { const packageJson = JSON.parse(host.readContent('/package.json')); packageJson['scripts'] = { postinstall: 'angular rocks', @@ -167,7 +166,7 @@ describe('ng-add schematic', () => { .toBe('angular rocks; ngcc --properties es2015 browser module main'); }); - it('should update ngcc in scripts.postinstall if it already exists', async() => { + it('should update ngcc in scripts.postinstall if it already exists', async () => { const packageJson = JSON.parse(host.readContent('/package.json')); packageJson['scripts'] = { postinstall: @@ -180,14 +179,14 @@ describe('ng-add schematic', () => { expect(json.scripts['postinstall']).toBe('ngcc --properties es2015 browser module main'); }); - it('should not create Bazel workspace file', async() => { + it('should not create Bazel workspace file', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const {files} = host; expect(files).not.toContain('/WORKSPACE'); expect(files).not.toContain('/BUILD.bazel'); }); - it('should produce main.dev.ts and main.prod.ts for AOT', async() => { + it('should produce main.dev.ts and main.prod.ts for AOT', async () => { host.create('/src/main.ts', 'generated by CLI'); host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const {files} = host; @@ -199,7 +198,7 @@ describe('ng-add schematic', () => { expect(files).toContain('/src/main.ts'); }); - it('should not overwrite index.html with script tags', async() => { + it('should not overwrite index.html with script tags', async () => { host.create('/src/index.html', '<html>Hello World</html>'); host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const {files} = host; @@ -209,14 +208,14 @@ describe('ng-add schematic', () => { expect(content).not.toMatch('<script src="/bundle.min.js"></script>'); }); - it('should generate main.dev.ts and main.prod.ts', async() => { + it('should generate main.dev.ts and main.prod.ts', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const {files} = host; expect(files).toContain('/src/main.dev.ts'); expect(files).toContain('/src/main.prod.ts'); }); - it('should overwrite .gitignore for bazel-out directory', async() => { + it('should overwrite .gitignore for bazel-out directory', async () => { host.create('.gitignore', '\n# compiled output\n'); host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const {files} = host; @@ -225,7 +224,7 @@ describe('ng-add schematic', () => { expect(content).toMatch('\n# compiled output\n/bazel-out\n'); }); - it('should create a backup for original angular.json', async() => { + it('should create a backup for original angular.json', async () => { expect(host.files).toContain('/angular.json'); const original = host.readContent('/angular.json'); host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); @@ -235,7 +234,7 @@ describe('ng-add schematic', () => { expect(content).toMatch(original); }); - it('should update angular.json to use Bazel builder', async() => { + it('should update angular.json to use Bazel builder', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const {files} = host; expect(files).toContain('/angular.json'); @@ -256,7 +255,7 @@ describe('ng-add schematic', () => { expect(lint.builder).toBe('@angular-devkit/build-angular:tslint'); }); - it('should get defaultProject if name is not provided', async() => { + it('should get defaultProject if name is not provided', async () => { const options = {}; host = await schematicRunner.runSchematicAsync('ng-add', options, host).toPromise(); const content = host.readContent('/angular.json'); @@ -281,7 +280,7 @@ describe('ng-add schematic', () => { ['~7.0.1', false], ]; for (const [version, upgrade] of cases) { - it(`should ${upgrade ? '' : 'not '}upgrade v${version}')`, async() => { + it(`should ${upgrade ? '' : 'not '}upgrade v${version}')`, async () => { host.overwrite('package.json', JSON.stringify({ name: 'demo', dependencies: { @@ -305,7 +304,7 @@ describe('ng-add schematic', () => { } }); - it('should add a postinstall step to package.json', async() => { + it('should add a postinstall step to package.json', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); expect(host.files).toContain('/package.json'); const content = host.readContent('/package.json'); @@ -313,7 +312,7 @@ describe('ng-add schematic', () => { expect(json.scripts.postinstall).toBe('ngcc --properties es2015 browser module main'); }); - it('should work when run on a minimal project (without test and e2e targets)', async() => { + it('should work when run on a minimal project (without test and e2e targets)', async () => { host.overwrite('angular.json', JSON.stringify({ projects: { 'demo': { @@ -338,5 +337,4 @@ describe('ng-add schematic', () => { expect(error).toBeNull(); }); - }); diff --git a/packages/bazel/src/schematics/ng-new/index.ts b/packages/bazel/src/schematics/ng-new/index.ts index 94a3826399ca1..51e52e5051f82 100644 --- a/packages/bazel/src/schematics/ng-new/index.ts +++ b/packages/bazel/src/schematics/ng-new/index.ts @@ -8,8 +8,9 @@ * @fileoverview Schematics for ng-new project that builds with Bazel. */ -import {Rule, Tree, chain, externalSchematic, schematic} from '@angular-devkit/schematics'; +import {chain, externalSchematic, Rule, schematic, Tree} from '@angular-devkit/schematics'; import {validateProjectName} from '@schematics/angular/utility/validation'; + import {Schema} from './schema'; export default function(options: Schema): Rule { diff --git a/packages/bazel/src/schematics/ng-new/index_spec.ts b/packages/bazel/src/schematics/ng-new/index_spec.ts index 3d1fef16bf787..1da740b925f36 100644 --- a/packages/bazel/src/schematics/ng-new/index_spec.ts +++ b/packages/bazel/src/schematics/ng-new/index_spec.ts @@ -9,14 +9,16 @@ import {SchematicTestRunner} from '@angular-devkit/schematics/testing'; describe('ng-new schematic', () => { - const schematicRunner = - new SchematicTestRunner('@angular/bazel', require.resolve('../collection.json'), ); + const schematicRunner = new SchematicTestRunner( + '@angular/bazel', + require.resolve('../collection.json'), + ); const defaultOptions = { name: 'demo', version: '7.0.0', }; - it('should call external @schematics/angular', async() => { + it('should call external @schematics/angular', async () => { const options = {...defaultOptions}; const host = await schematicRunner.runSchematicAsync('ng-new', options).toPromise(); const {files} = host; @@ -25,7 +27,7 @@ describe('ng-new schematic', () => { expect(files).toContain('/demo/package.json'); }); - it('should call ng-add to generate additional files needed by Bazel', async() => { + it('should call ng-add to generate additional files needed by Bazel', async () => { const options = {...defaultOptions}; const host = await schematicRunner.runSchematicAsync('ng-new', options).toPromise(); const {files} = host; diff --git a/packages/bazel/src/schematics/ng-new/schema.d.ts b/packages/bazel/src/schematics/ng-new/schema.d.ts index 9cf013db9e82f..913cf2bc26622 100644 --- a/packages/bazel/src/schematics/ng-new/schema.d.ts +++ b/packages/bazel/src/schematics/ng-new/schema.d.ts @@ -86,8 +86,8 @@ export interface Schema { viewEncapsulation?: ViewEncapsulation; } /** -* Initial git repository commit information. -*/ + * Initial git repository commit information. + */ export declare type CommitUnion = boolean | CommitObject; export interface CommitObject { email: string; @@ -95,16 +95,16 @@ export interface CommitObject { name: string; } /** -* The file extension or preprocessor to use for style files. -*/ + * The file extension or preprocessor to use for style files. + */ export declare enum Style { Css = 'css', Sass = 'sass', Scss = 'scss', } /** -* The view encapsulation strategy to use in the initial project. -*/ + * The view encapsulation strategy to use in the initial project. + */ export declare enum ViewEncapsulation { Emulated = 'Emulated', Native = 'Native', diff --git a/packages/bazel/src/schematics/utility/json-utils.ts b/packages/bazel/src/schematics/utility/json-utils.ts index 0c42716dbdba1..449ea6d62a573 100644 --- a/packages/bazel/src/schematics/utility/json-utils.ts +++ b/packages/bazel/src/schematics/utility/json-utils.ts @@ -42,7 +42,7 @@ export function removeKeyValueInAstObject( let length = end - start; const match = content.slice(end).match(/^[,\s]+/); if (match) { - length += match.pop() !.length; + length += match.pop()!.length; } recorder.remove(start, length); if (i === node.properties.length - 1) { // last property @@ -60,6 +60,6 @@ export function removeKeyValueInAstObject( /** * Returns true if the specified 'node' is a JsonAstObject, false otherwise. */ -export function isJsonAstObject(node: JsonAstNode | null): node is JsonAstObject { +export function isJsonAstObject(node: JsonAstNode|null): node is JsonAstObject { return !!node && node.kind === 'object'; } diff --git a/packages/bazel/src/schematics/utility/json-utils_spec.ts b/packages/bazel/src/schematics/utility/json-utils_spec.ts index 0f7e2d24aa902..4341d083459ed 100644 --- a/packages/bazel/src/schematics/utility/json-utils_spec.ts +++ b/packages/bazel/src/schematics/utility/json-utils_spec.ts @@ -12,9 +12,10 @@ import {UnitTestTree} from '@angular-devkit/schematics/testing'; import {isJsonAstObject, removeKeyValueInAstObject, replacePropertyInAstObject} from './json-utils'; describe('JsonUtils', () => { - let tree: UnitTestTree; - beforeEach(() => { tree = new UnitTestTree(new HostTree()); }); + beforeEach(() => { + tree = new UnitTestTree(new HostTree()); + }); describe('replacePropertyInAstObject', () => { it('should replace property', () => { diff --git a/packages/bazel/test/ng_package/common_package.spec.ts b/packages/bazel/test/ng_package/common_package.spec.ts index 5966ae1f248a0..cfe1144e234e4 100644 --- a/packages/bazel/test/ng_package/common_package.spec.ts +++ b/packages/bazel/test/ng_package/common_package.spec.ts @@ -30,8 +30,9 @@ describe('@angular/common ng_package', () => { }); // regression test for https://github.com/angular/angular/issues/23217 // Note, we don't have an e2e test that covers this - it('doesn\'t pass require in a way that breaks webpack static analysis', - () => { expect(shx.cat('locales/fr.js')).not.toContain('factory(require, exports)'); }); + it('doesn\'t pass require in a way that breaks webpack static analysis', () => { + expect(shx.cat('locales/fr.js')).not.toContain('factory(require, exports)'); + }); }); it('should have right bundle files', () => { @@ -59,8 +60,9 @@ describe('@angular/common ng_package', () => { ]); }); - it('should reference core using global symbol in umd', - () => { expect(shx.cat('bundles/common.umd.js')).toContain('global.ng.core'); }); + it('should reference core using global symbol in umd', () => { + expect(shx.cat('bundles/common.umd.js')).toContain('global.ng.core'); + }); it('should have right fesm files', () => { const expected = [ diff --git a/packages/bazel/test/ng_package/core_package.spec.ts b/packages/bazel/test/ng_package/core_package.spec.ts index bbfe3594acf9b..0f6bda62c5235 100644 --- a/packages/bazel/test/ng_package/core_package.spec.ts +++ b/packages/bazel/test/ng_package/core_package.spec.ts @@ -41,8 +41,9 @@ describe('@angular/core ng_package', () => { describe('package.json', () => { const packageJson = 'package.json'; - it('should have a package.json file', - () => { expect(shx.grep('"name":', packageJson)).toContain(`@angular/core`); }); + it('should have a package.json file', () => { + expect(shx.grep('"name":', packageJson)).toContain(`@angular/core`); + }); it('should contain correct version number with the PLACEHOLDER string replaced', () => { expect(shx.grep('"version":', packageJson)).toMatch(/\d+\.\d+\.\d+(?!-PLACEHOLDER)/); @@ -66,16 +67,20 @@ describe('@angular/core ng_package', () => { describe('typescript support', () => { if (ivyEnabled) { - it('should have an index d.ts file', - () => { expect(shx.cat('core.d.ts')).toContain(`export *`); }); + it('should have an index d.ts file', () => { + expect(shx.cat('core.d.ts')).toContain(`export *`); + }); - it('should not have amd module names', - () => { expect(shx.cat('public_api.d.ts')).not.toContain('<amd-module name'); }); + it('should not have amd module names', () => { + expect(shx.cat('public_api.d.ts')).not.toContain('<amd-module name'); + }); } else { - it('should have an index d.ts file', - () => { expect(shx.cat('core.d.ts')).toContain('export declare'); }); - it('should have an r3_symbols d.ts file', - () => { expect(shx.cat('src/r3_symbols.d.ts')).toContain('export declare'); }); + it('should have an index d.ts file', () => { + expect(shx.cat('core.d.ts')).toContain('export declare'); + }); + it('should have an r3_symbols d.ts file', () => { + expect(shx.cat('src/r3_symbols.d.ts')).toContain('export declare'); + }); } }); @@ -87,15 +92,18 @@ describe('@angular/core ng_package', () => { obsoleteInIvy('metadata files are no longer needed or produced in Ivy') .describe('angular metadata', () => { - it('should have metadata.json files', - () => { expect(shx.cat('core.metadata.json')).toContain(`"__symbolic":"module"`); }); - it('should not have self-references in metadata.json', - () => { expect(shx.cat('core.metadata.json')).not.toContain(`"from":"./core"`); }); + it('should have metadata.json files', () => { + expect(shx.cat('core.metadata.json')).toContain(`"__symbolic":"module"`); + }); + it('should not have self-references in metadata.json', () => { + expect(shx.cat('core.metadata.json')).not.toContain(`"from":"./core"`); + }); }); describe('fesm2015', () => { - it('should have a fesm15 file in the /fesm2015 directory', - () => { expect(shx.cat('fesm2015/core.js')).toContain(`export {`); }); + it('should have a fesm15 file in the /fesm2015 directory', () => { + expect(shx.cat('fesm2015/core.js')).toContain(`export {`); + }); it('should have a source map', () => { expect(shx.cat('fesm2015/core.js.map')) @@ -114,8 +122,9 @@ describe('@angular/core ng_package', () => { }); describe('fesm5', () => { - it('should have a fesm5 file in the /fesm5 directory', - () => { expect(shx.cat('fesm5/core.js')).toContain(`export {`); }); + it('should have a fesm5 file in the /fesm5 directory', () => { + expect(shx.cat('fesm5/core.js')).toContain(`export {`); + }); it('should have a source map', () => { expect(shx.cat('fesm5/core.js.map')).toContain(`{"version":3,"file":"core.js","sources":`); @@ -131,12 +140,14 @@ describe('@angular/core ng_package', () => { expect(shx.cat('fesm5/core.js')).toContain('.ɵprov = '); }); } else { - it('should have decorators', - () => { expect(shx.cat('fesm5/core.js')).toContain('__decorate'); }); + it('should have decorators', () => { + expect(shx.cat('fesm5/core.js')).toContain('__decorate'); + }); // See: https://github.com/angular/angular/pull/32069 - it('should retain access to const', - () => { expect(shx.cat('fesm5/core.js')).toContain('!ivyEnabled'); }); + it('should retain access to const', () => { + expect(shx.cat('fesm5/core.js')).toContain('!ivyEnabled'); + }); } it('should load tslib from external bundle', () => { @@ -145,8 +156,9 @@ describe('@angular/core ng_package', () => { }); obsoleteInIvy('we no longer need to export private symbols') - .it('should have been built from the generated bundle index', - () => { expect(shx.cat('fesm5/core.js')).toMatch('export {.*makeParamDecorator'); }); + .it('should have been built from the generated bundle index', () => { + expect(shx.cat('fesm5/core.js')).toMatch('export {.*makeParamDecorator'); + }); }); describe('esm2015', () => { @@ -160,25 +172,31 @@ describe('@angular/core ng_package', () => { }); describe('esm5', () => { - it('should not contain any *.ngfactory.js files', - () => { expect(shx.find('esm5').filter(f => f.endsWith('.ngfactory.js'))).toEqual([]); }); + it('should not contain any *.ngfactory.js files', () => { + expect(shx.find('esm5').filter(f => f.endsWith('.ngfactory.js'))).toEqual([]); + }); - it('should not contain any *.ngsummary.js files', - () => { expect(shx.find('esm5').filter(f => f.endsWith('.ngsummary.js'))).toEqual([]); }); + it('should not contain any *.ngsummary.js files', () => { + expect(shx.find('esm5').filter(f => f.endsWith('.ngsummary.js'))).toEqual([]); + }); }); describe('umd', () => { - it('should have a umd file in the /bundles directory', - () => { expect(shx.ls('bundles/core.umd.js').length).toBe(1, 'File not found'); }); + it('should have a umd file in the /bundles directory', () => { + expect(shx.ls('bundles/core.umd.js').length).toBe(1, 'File not found'); + }); - it('should have a source map next to the umd file', - () => { expect(shx.ls('bundles/core.umd.js.map').length).toBe(1, 'File not found'); }); + it('should have a source map next to the umd file', () => { + expect(shx.ls('bundles/core.umd.js.map').length).toBe(1, 'File not found'); + }); - it('should have a minified umd file in the /bundles directory', - () => { expect(shx.ls('bundles/core.umd.min.js').length).toBe(1, 'File not found'); }); + it('should have a minified umd file in the /bundles directory', () => { + expect(shx.ls('bundles/core.umd.min.js').length).toBe(1, 'File not found'); + }); - it('should have a source map next to the minified umd file', - () => { expect(shx.ls('bundles/core.umd.min.js.map').length).toBe(1, 'File not found'); }); + it('should have a source map next to the minified umd file', () => { + expect(shx.ls('bundles/core.umd.min.js.map').length).toBe(1, 'File not found'); + }); it('should have the version info in the header', () => { expect(shx.cat('bundles/core.umd.js')) @@ -189,22 +207,25 @@ describe('@angular/core ng_package', () => { expect(shx.cat('bundles/core.umd.js')).toContain('function __extends'); expect(shx.cat('bundles/core.umd.js')).not.toContain('undefined.__extends'); }); - it('should have an AMD name', - () => { expect(shx.cat('bundles/core.umd.js')).toContain('define(\'@angular/core\''); }); - it('should define ng global symbols', - () => { expect(shx.cat('bundles/core.umd.js')).toContain('global.ng.core = {}'); }); + it('should have an AMD name', () => { + expect(shx.cat('bundles/core.umd.js')).toContain('define(\'@angular/core\''); + }); + it('should define ng global symbols', () => { + expect(shx.cat('bundles/core.umd.js')).toContain('global.ng.core = {}'); + }); }); }); describe('secondary entry-point', () => { describe('package.json', () => { - const packageJson = p `testing/package.json`; + const packageJson = p`testing/package.json`; - it('should have a package.json file', - () => { expect(shx.grep('"name":', packageJson)).toContain(`@angular/core/testing`); }); + it('should have a package.json file', () => { + expect(shx.grep('"name":', packageJson)).toContain(`@angular/core/testing`); + }); it('should have its module resolution mappings defined in the nested package.json', () => { - const packageJson = p `testing/package.json`; + const packageJson = p`testing/package.json`; expect(shx.grep('"main":', packageJson)).toContain(`../bundles/core-testing.umd.js`); expect(shx.grep('"module":', packageJson)).toContain(`../fesm5/testing.js`); expect(shx.grep('"es2015":', packageJson)).toContain(`../fesm2015/testing.js`); @@ -216,13 +237,15 @@ describe('@angular/core ng_package', () => { describe('typings', () => { if (ivyEnabled) { - const typingsFile = p `testing/index.d.ts`; - it('should have a typings file', - () => { expect(shx.cat(typingsFile)).toContain(`export * from './public_api';`); }); + const typingsFile = p`testing/index.d.ts`; + it('should have a typings file', () => { + expect(shx.cat(typingsFile)).toContain(`export * from './public_api';`); + }); } else { - const typingsFile = p `testing/testing.d.ts`; - it('should have a typings file', - () => { expect(shx.cat(typingsFile)).toContain('export declare'); }); + const typingsFile = p`testing/testing.d.ts`; + it('should have a typings file', () => { + expect(shx.cat(typingsFile)).toContain('export declare'); + }); } obsoleteInIvy( @@ -242,8 +265,9 @@ describe('@angular/core ng_package', () => { }); describe('fesm2015', () => { - it('should have a fesm15 file in the /fesm2015 directory', - () => { expect(shx.cat('fesm2015/testing.js')).toContain(`export {`); }); + it('should have a fesm15 file in the /fesm2015 directory', () => { + expect(shx.cat('fesm2015/testing.js')).toContain(`export {`); + }); it('should have a source map', () => { expect(shx.cat('fesm2015/testing.js.map')) @@ -257,8 +281,9 @@ describe('@angular/core ng_package', () => { }); describe('fesm5', () => { - it('should have a fesm5 file in the /fesm5 directory', - () => { expect(shx.cat('fesm5/testing.js')).toContain(`export {`); }); + it('should have a fesm5 file in the /fesm5 directory', () => { + expect(shx.cat('fesm5/testing.js')).toContain(`export {`); + }); it('should have a source map', () => { expect(shx.cat('fesm5/testing.js.map')) @@ -267,8 +292,9 @@ describe('@angular/core ng_package', () => { }); describe('umd', () => { - it('should have a umd file in the /bundles directory', - () => { expect(shx.ls('bundles/core-testing.umd.js').length).toBe(1, 'File not found'); }); + it('should have a umd file in the /bundles directory', () => { + expect(shx.ls('bundles/core-testing.umd.js').length).toBe(1, 'File not found'); + }); it('should have a source map next to the umd file', () => { expect(shx.ls('bundles/core-testing.umd.js.map').length).toBe(1, 'File not found'); diff --git a/packages/bazel/test/ng_package/example_package.golden b/packages/bazel/test/ng_package/example_package.golden index 68ea796744546..1896ba68be779 100644 --- a/packages/bazel/test/ng_package/example_package.golden +++ b/packages/bazel/test/ng_package/example_package.golden @@ -266,10 +266,11 @@ Hello } function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } @@ -307,14 +308,15 @@ Hello } function __values(o) { - var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); - return { + if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { @@ -395,6 +397,21 @@ Hello return (mod && mod.__esModule) ? mod : { default: mod }; } + function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); + } + + function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; + } + /** * @license * Copyright Google Inc. All Rights Reserved. @@ -551,10 +568,11 @@ var o=function n(e,t,o,r){var f,c=arguments.length,l=c<3?t:null===r?r=Object.get } function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } @@ -592,14 +610,15 @@ var o=function n(e,t,o,r){var f,c=arguments.length,l=c<3?t:null===r?r=Object.get } function __values(o) { - var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); - return { + if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { @@ -680,6 +699,21 @@ var o=function n(e,t,o,r){var f,c=arguments.length,l=c<3?t:null===r?r=Object.get return (mod && mod.__esModule) ? mod : { default: mod }; } + function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); + } + + function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; + } + var MySecondService = /** @class */ (function () { function MySecondService() { } @@ -836,10 +870,11 @@ e.MyService=c,e.ɵangular_packages_bazel_test_ng_package_example_imports_imports } function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } @@ -877,14 +912,15 @@ e.MyService=c,e.ɵangular_packages_bazel_test_ng_package_example_imports_imports } function __values(o) { - var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); - return { + if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { @@ -965,6 +1001,21 @@ e.MyService=c,e.ɵangular_packages_bazel_test_ng_package_example_imports_imports return (mod && mod.__esModule) ? mod : { default: mod }; } + function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); + } + + function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; + } + /** * @license * Copyright Google Inc. All Rights Reserved. @@ -1124,10 +1175,11 @@ e.SecondaryModule=o,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})})); } function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } @@ -1165,14 +1217,15 @@ e.SecondaryModule=o,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})})); } function __values(o) { - var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); - return { + if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { @@ -1253,6 +1306,21 @@ e.SecondaryModule=o,e.a=1,Object.defineProperty(e,"__esModule",{value:!0})})); return (mod && mod.__esModule) ? mod : { default: mod }; } + function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); + } + + function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; + } + /** * @license * Copyright Google Inc. All Rights Reserved. diff --git a/packages/bazel/test/ng_package/example_with_ts_library_package.golden b/packages/bazel/test/ng_package/example_with_ts_library_package.golden index 459aeb7f022d4..b48686126803c 100644 --- a/packages/bazel/test/ng_package/example_with_ts_library_package.golden +++ b/packages/bazel/test/ng_package/example_with_ts_library_package.golden @@ -159,10 +159,11 @@ License: MIT } function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } @@ -200,14 +201,15 @@ License: MIT } function __values(o) { - var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); - return { + if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { @@ -288,6 +290,21 @@ License: MIT return (mod && mod.__esModule) ? mod : { default: mod }; } + function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); + } + + function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; + } + /** * @license * Copyright Google Inc. All Rights Reserved. diff --git a/packages/bazel/test/ngc-wrapped/flat_module_test.ts b/packages/bazel/test/ngc-wrapped/flat_module_test.ts index b7fdec9b79d63..664a4e736f55e 100644 --- a/packages/bazel/test/ngc-wrapped/flat_module_test.ts +++ b/packages/bazel/test/ngc-wrapped/flat_module_test.ts @@ -11,7 +11,6 @@ import {existsSync, readFileSync} from 'fs'; import {dirname, join} from 'path'; describe('flat_module ng_module', () => { - let packageOutput: string; let flatModuleOutFile: string; @@ -21,11 +20,11 @@ describe('flat_module ng_module', () => { flatModuleOutFile = join(packageOutput, 'flat_module.js'); }); - it('should have a flat module out file', - () => { expect(existsSync(flatModuleOutFile)).toBe(true); }); + it('should have a flat module out file', () => { + expect(existsSync(flatModuleOutFile)).toBe(true); + }); describe('flat module out file', () => { - obsoleteInIvy('Ngtsc computes the AMD module name differently than NGC') .it('should have a proper AMD module name', () => { expect(readFileSync(flatModuleOutFile, 'utf8')) diff --git a/packages/bazel/test/ngc-wrapped/index_test.ts b/packages/bazel/test/ngc-wrapped/index_test.ts index 5c22cc2d7bb05..3932730149b8c 100644 --- a/packages/bazel/test/ngc-wrapped/index_test.ts +++ b/packages/bazel/test/ngc-wrapped/index_test.ts @@ -11,7 +11,6 @@ import * as path from 'path'; import {setup} from './test_support'; describe('ngc_wrapped', () => { - it('should work', () => { const {read, write, runOneBuild, writeConfig, shouldExist, basePath, typesRoots} = setup(); diff --git a/packages/bazel/test/ngc-wrapped/test_support.ts b/packages/bazel/test/ngc-wrapped/test_support.ts index 9f0dc054a8d24..69098ba0538bc 100644 --- a/packages/bazel/test/ngc-wrapped/test_support.ts +++ b/packages/bazel/test/ngc-wrapped/test_support.ts @@ -19,7 +19,9 @@ export interface TestSupport { angularCorePath: string; typesRoots: string; writeConfig({ - srcTargetPath, depPaths, pathMapping, + srcTargetPath, + depPaths, + pathMapping, }: { srcTargetPath: string, depPaths?: string[], @@ -33,13 +35,13 @@ export interface TestSupport { runOneBuild(): boolean; } -export function setup( - { - bazelBin = 'bazel-bin', tsconfig = 'tsconfig.json', - }: { - bazelBin?: string, - tsconfig?: string, - } = {}): TestSupport { +export function setup({ + bazelBin = 'bazel-bin', + tsconfig = 'tsconfig.json', +}: { + bazelBin?: string, + tsconfig?: string, +} = {}): TestSupport { const runfilesPath = process.env['TEST_SRCDIR']; const basePath = makeTempDir(runfilesPath); @@ -93,12 +95,17 @@ export function setup( } function writeFiles(...mockDirs: {[fileName: string]: string}[]) { - mockDirs.forEach( - (dir) => { Object.keys(dir).forEach((fileName) => { write(fileName, dir[fileName]); }); }); + mockDirs.forEach((dir) => { + Object.keys(dir).forEach((fileName) => { + write(fileName, dir[fileName]); + }); + }); } function writeConfig({ - srcTargetPath, depPaths = [], pathMapping = [], + srcTargetPath, + depPaths = [], + pathMapping = [], }: { srcTargetPath: string, depPaths?: string[], @@ -133,7 +140,8 @@ export function setup( defaultTsConfig: emptyTsConfig.config, rootDir: basePath, target: target, - outDir: bazelBinPath, compilationTargetSrc, + outDir: bazelBinPath, + compilationTargetSrc, files: files, pathMapping: pathMappingObj, }); @@ -153,7 +161,9 @@ export function setup( } } - function runOneBuildImpl(): boolean { return runOneBuild(['@' + tsConfigJsonPath]); } + function runOneBuildImpl(): boolean { + return runOneBuild(['@' + tsConfigJsonPath]); + } } function makeTempDir(baseDir: string): string { diff --git a/packages/benchpress/BUILD.bazel b/packages/benchpress/BUILD.bazel index 8ac5a7c81dbb9..7a5e3d727c8ac 100644 --- a/packages/benchpress/BUILD.bazel +++ b/packages/benchpress/BUILD.bazel @@ -14,6 +14,8 @@ ts_library( "//packages:types", "//packages/core", "@npm//@types/node", + "@npm//@types/q", + "@npm//q", "@npm//reflect-metadata", ], ) diff --git a/packages/benchpress/index.ts b/packages/benchpress/index.ts index 716714669c7e6..0808d5f55f77a 100644 --- a/packages/benchpress/index.ts +++ b/packages/benchpress/index.ts @@ -24,7 +24,7 @@ export {JsonFileReporter} from './src/reporter/json_file_reporter'; export {MultiReporter} from './src/reporter/multi_reporter'; export {Runner} from './src/runner'; export {SampleDescription} from './src/sample_description'; -export {SampleState, Sampler} from './src/sampler'; +export {Sampler, SampleState} from './src/sampler'; export {Validator} from './src/validator'; export {RegressionSlopeValidator} from './src/validator/regression_slope_validator'; export {SizeValidator} from './src/validator/size_validator'; diff --git a/packages/benchpress/package.json b/packages/benchpress/package.json index 98c522fda30ca..66f304ffa0ea5 100644 --- a/packages/benchpress/package.json +++ b/packages/benchpress/package.json @@ -6,11 +6,12 @@ "typings": "./index.d.ts", "strictNullChecks": true, "dependencies": { - "@angular/core": "^2.0.0-rc.7", + "@angular/core": "^9.0.0", + "@types/node": "^12.11.1", + "@types/q": "^1.5.2", + "protractor": "^5.4.2", + "q": "^1.5.1", "reflect-metadata": "^0.1.2", - "rxjs": "^6.5.3", - "jpm": "1.1.4", - "firefox-profile": "0.4.0", "selenium-webdriver": "^2.53.3" }, "repository": { diff --git a/packages/benchpress/src/firefox_extension/.gitignore b/packages/benchpress/src/firefox_extension/.gitignore deleted file mode 100644 index fc1a520a40df6..0000000000000 --- a/packages/benchpress/src/firefox_extension/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.xpi -addon-sdk* diff --git a/packages/benchpress/src/firefox_extension/data/installed_script.ts b/packages/benchpress/src/firefox_extension/data/installed_script.ts deleted file mode 100644 index 1e14d5cc6b6c0..0000000000000 --- a/packages/benchpress/src/firefox_extension/data/installed_script.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @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 - */ - -declare var exportFunction: any; -declare var unsafeWindow: any; - -exportFunction(function() { - const curTime = unsafeWindow.performance.now(); - (<any>self).port.emit('startProfiler', curTime); -}, unsafeWindow, {defineAs: 'startProfiler'}); - -exportFunction(function() { - (<any>self).port.emit('stopProfiler'); -}, unsafeWindow, {defineAs: 'stopProfiler'}); - -exportFunction(function(cb: Function) { - (<any>self).port.once('perfProfile', cb); - (<any>self).port.emit('getProfile'); -}, unsafeWindow, {defineAs: 'getProfile'}); - -exportFunction(function() { - (<any>self).port.emit('forceGC'); -}, unsafeWindow, {defineAs: 'forceGC'}); - -exportFunction(function(name: string) { - const curTime = unsafeWindow.performance.now(); - (<any>self).port.emit('markStart', name, curTime); -}, unsafeWindow, {defineAs: 'markStart'}); - -exportFunction(function(name: string) { - const curTime = unsafeWindow.performance.now(); - (<any>self).port.emit('markEnd', name, curTime); -}, unsafeWindow, {defineAs: 'markEnd'}); diff --git a/packages/benchpress/src/firefox_extension/lib/main.ts b/packages/benchpress/src/firefox_extension/lib/main.ts deleted file mode 100644 index 348014637c467..0000000000000 --- a/packages/benchpress/src/firefox_extension/lib/main.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @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 - */ - -const {Cc, Ci, Cu} = require('chrome'); -const os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService); -const ParserUtil = require('./parser_util'); - -class Profiler { - private _profiler: any; - // TODO(issue/24571): remove '!'. - private _markerEvents !: any[]; - // TODO(issue/24571): remove '!'. - private _profilerStartTime !: number; - - constructor() { this._profiler = Cc['@mozilla.org/tools/profiler;1'].getService(Ci.nsIProfiler); } - - start(entries: any, interval: any, features: any, timeStarted: any) { - this._profiler.StartProfiler(entries, interval, features, features.length); - this._profilerStartTime = timeStarted; - this._markerEvents = []; - } - - stop() { this._profiler.StopProfiler(); } - - getProfilePerfEvents() { - const profileData = this._profiler.getProfileData(); - let perfEvents = ParserUtil.convertPerfProfileToEvents(profileData); - perfEvents = this._mergeMarkerEvents(perfEvents); - perfEvents.sort(function(event1: any, event2: any) { - return event1.ts - event2.ts; - }); // Sort by ts - return perfEvents; - } - - /** @internal */ - private _mergeMarkerEvents(perfEvents: any[]): any[] { - this._markerEvents.forEach(function(markerEvent) { perfEvents.push(markerEvent); }); - return perfEvents; - } - - addStartEvent(name: string, timeStarted: number) { - this._markerEvents.push({ph: 'B', ts: timeStarted - this._profilerStartTime, name: name}); - } - - addEndEvent(name: string, timeEnded: number) { - this._markerEvents.push({ph: 'E', ts: timeEnded - this._profilerStartTime, name: name}); - } -} - -function forceGC() { - Cu.forceGC(); - os.notifyObservers(null, 'child-gc-request', null); -} - -const mod = require('sdk/page-mod'); -const data = require('sdk/self').data; -const profiler = new Profiler(); -mod.PageMod({ - include: ['*'], - contentScriptFile: data.url('installed_script.js'), - onAttach: (worker: any) => { - worker.port.on( - 'startProfiler', - (timeStarted: any) => profiler.start( - /* = profiler memory */ 3000000, 0.1, ['leaf', 'js', 'stackwalk', 'gc'], timeStarted)); - worker.port.on('stopProfiler', () => profiler.stop()); - worker.port.on( - 'getProfile', () => worker.port.emit('perfProfile', profiler.getProfilePerfEvents())); - worker.port.on('forceGC', forceGC); - worker.port.on( - 'markStart', (name: string, timeStarted: any) => profiler.addStartEvent(name, timeStarted)); - worker.port.on( - 'markEnd', (name: string, timeEnded: any) => profiler.addEndEvent(name, timeEnded)); - } -}); diff --git a/packages/benchpress/src/firefox_extension/lib/parser_util.ts b/packages/benchpress/src/firefox_extension/lib/parser_util.ts deleted file mode 100644 index 6bccc3c876245..0000000000000 --- a/packages/benchpress/src/firefox_extension/lib/parser_util.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * @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 - */ - -/** - * @param {Object} perfProfile The perf profile JSON object. - * @return {Object[]} An array of recognized events that are captured - * within the perf profile. - */ -export function convertPerfProfileToEvents(perfProfile: any): any[] { - const inProgressEvents = new Map(); // map from event name to start time - const finishedEvents: {[key: string]: any}[] = []; // Event[] finished events - const addFinishedEvent = function(eventName: string, startTime: number, endTime: number) { - const categorizedEventName = categorizeEvent(eventName); - let args: {[key: string]: any}|undefined = undefined; - if (categorizedEventName == 'gc') { - // TODO: We cannot measure heap size at the moment - args = {usedHeapSize: 0}; - } - if (startTime == endTime) { - // Finished instantly - finishedEvents.push({ph: 'X', ts: startTime, name: categorizedEventName, args: args}); - } else { - // Has duration - finishedEvents.push({ph: 'B', ts: startTime, name: categorizedEventName, args: args}); - finishedEvents.push({ph: 'E', ts: endTime, name: categorizedEventName, args: args}); - } - }; - - const samples = perfProfile.threads[0].samples; - // In perf profile, firefox samples all the frames in set time intervals. Here - // we go through all the samples and construct the start and end time for each - // event. - for (let i = 0; i < samples.length; ++i) { - const sample = samples[i]; - const sampleTime = sample.time; - - // Add all the frames into a set so it's easier/faster to find the set - // differences - const sampleFrames = new Set(); - sample.frames.forEach(function(frame: {[key: string]: any}) { - sampleFrames.add(frame['location']); - }); - - // If an event is in the inProgressEvents map, but not in the current sample, - // then it must have just finished. We add this event to the finishedEvents - // array and remove it from the inProgressEvents map. - const previousSampleTime = (i == 0 ? /* not used */ -1 : samples[i - 1].time); - inProgressEvents.forEach(function(startTime, eventName) { - if (!(sampleFrames.has(eventName))) { - addFinishedEvent(eventName, startTime, previousSampleTime); - inProgressEvents.delete(eventName); - } - }); - - // If an event is in the current sample, but not in the inProgressEvents map, - // then it must have just started. We add this event to the inProgressEvents - // map. - sampleFrames.forEach(function(eventName) { - if (!(inProgressEvents.has(eventName))) { - inProgressEvents.set(eventName, sampleTime); - } - }); - } - - // If anything is still in progress, we need to included it as a finished event - // since recording ended. - const lastSampleTime = samples[samples.length - 1].time; - inProgressEvents.forEach(function(startTime, eventName) { - addFinishedEvent(eventName, startTime, lastSampleTime); - }); - - // Remove all the unknown categories. - return finishedEvents.filter(function(event) { return event['name'] != 'unknown'; }); -} - -// TODO: this is most likely not exhaustive. -export function categorizeEvent(eventName: string): string { - if (eventName.indexOf('PresShell::Paint') > -1) { - return 'render'; - } else if (eventName.indexOf('FirefoxDriver.prototype.executeScript') > -1) { - return 'script'; - } else if (eventName.indexOf('forceGC') > -1) { - return 'gc'; - } else { - return 'unknown'; - } -} diff --git a/packages/benchpress/src/firefox_extension/lib/test_helper.ts b/packages/benchpress/src/firefox_extension/lib/test_helper.ts deleted file mode 100644 index 97cfd2519d289..0000000000000 --- a/packages/benchpress/src/firefox_extension/lib/test_helper.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @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 - */ - -const q = require('q'); -const FirefoxProfile = require('firefox-profile'); -const jpm = require('jpm/lib/xpi'); -const pathUtil = require('path'); - -const PERF_ADDON_PACKAGE_JSON_DIR = '..'; - -exports.getAbsolutePath = function(path: string) { - const normalizedPath = pathUtil.normalize(path); - if (pathUtil.resolve(normalizedPath) == normalizedPath) { - // Already absolute path - return normalizedPath; - } else { - return pathUtil.join(__dirname, normalizedPath); - } -}; - -exports.getFirefoxProfile = function(extensionPath: string) { - const deferred = q.defer(); - - const firefoxProfile = new FirefoxProfile(); - firefoxProfile.addExtensions([extensionPath], () => { - firefoxProfile.encoded((err: any, encodedProfile: string) => { - const multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}]; - deferred.resolve(multiCapabilities); - }); - }); - - return deferred.promise; -}; - -exports.getFirefoxProfileWithExtension = function() { - const absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR); - const packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json')); - - const savedCwd = process.cwd(); - process.chdir(absPackageJsonDir); - - return jpm(packageJson).then((xpiPath: string) => { - process.chdir(savedCwd); - return exports.getFirefoxProfile(xpiPath); - }); -}; diff --git a/packages/benchpress/src/firefox_extension/package.json b/packages/benchpress/src/firefox_extension/package.json deleted file mode 100644 index f3e5ca260841d..0000000000000 --- a/packages/benchpress/src/firefox_extension/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "version" : "0.0.1", "main" : "lib/main.js", "name" : "ffperf-addon" } diff --git a/packages/benchpress/src/metric.ts b/packages/benchpress/src/metric.ts index 60fb0a8f0bcb5..228282ff1a2fc 100644 --- a/packages/benchpress/src/metric.ts +++ b/packages/benchpress/src/metric.ts @@ -14,18 +14,24 @@ export abstract class Metric { /** * Starts measuring */ - beginMeasure(): Promise<any> { throw new Error('NYI'); } + beginMeasure(): Promise<any> { + throw new Error('NYI'); + } /** * Ends measuring and reports the data * since the begin call. * @param restart: Whether to restart right after this. */ - endMeasure(restart: boolean): Promise<{[key: string]: any}> { throw new Error('NYI'); } + endMeasure(restart: boolean): Promise<{[key: string]: any}> { + throw new Error('NYI'); + } /** * Describes the metrics provided by this metric implementation. * (e.g. units, ...) */ - describe(): {[key: string]: string} { throw new Error('NYI'); } + describe(): {[key: string]: string} { + throw new Error('NYI'); + } } diff --git a/packages/benchpress/src/metric/multi_metric.ts b/packages/benchpress/src/metric/multi_metric.ts index 47310e74835cf..05994585d4813 100644 --- a/packages/benchpress/src/metric/multi_metric.ts +++ b/packages/benchpress/src/metric/multi_metric.ts @@ -26,7 +26,9 @@ export class MultiMetric extends Metric { ]; } - constructor(private _metrics: Metric[]) { super(); } + constructor(private _metrics: Metric[]) { + super(); + } /** * Starts measuring @@ -56,7 +58,11 @@ export class MultiMetric extends Metric { function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} { const result: {[key: string]: string} = {}; - maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); }); + maps.forEach(map => { + Object.keys(map).forEach(prop => { + result[prop] = map[prop]; + }); + }); return result; } diff --git a/packages/benchpress/src/metric/perflog_metric.ts b/packages/benchpress/src/metric/perflog_metric.ts index ac285e7a959e7..7a1dbd99a7203 100644 --- a/packages/benchpress/src/metric/perflog_metric.ts +++ b/packages/benchpress/src/metric/perflog_metric.ts @@ -23,11 +23,12 @@ export class PerflogMetric extends Metric { static PROVIDERS = [ { provide: PerflogMetric, - deps: [ - WebDriverExtension, PerflogMetric.SET_TIMEOUT, Options.MICRO_METRICS, Options.FORCE_GC, - Options.CAPTURE_FRAMES, Options.RECEIVED_DATA, Options.REQUEST_COUNT, - PerflogMetric.IGNORE_NAVIGATION - ] + deps: + [ + WebDriverExtension, PerflogMetric.SET_TIMEOUT, Options.MICRO_METRICS, Options.FORCE_GC, + Options.CAPTURE_FRAMES, Options.RECEIVED_DATA, Options.REQUEST_COUNT, + PerflogMetric.IGNORE_NAVIGATION + ] }, { provide: PerflogMetric.SET_TIMEOUT, @@ -169,7 +170,9 @@ export class PerflogMetric extends Metric { return result; } let resolve: (result: any) => void; - const promise = new Promise<{[key: string]: number}>(res => { resolve = res; }); + const promise = new Promise<{[key: string]: number}>(res => { + resolve = res; + }); this._setTimeout(() => resolve(this._readUntilEndMark(markName, loopCount + 1)), 100); return promise; }); @@ -188,7 +191,7 @@ export class PerflogMetric extends Metric { } startEvent['ph'] = 'B'; endEvent['ph'] = 'E'; - endEvent['ts'] = startEvent['ts'] ! + startEvent['dur'] !; + endEvent['ts'] = startEvent['ts']! + startEvent['dur']!; this._remainingEvents.push(startEvent); this._remainingEvents.push(endEvent); } else { @@ -198,7 +201,7 @@ export class PerflogMetric extends Metric { if (needSort) { // Need to sort because of the ph==='X' events this._remainingEvents.sort((a, b) => { - const diff = a['ts'] ! - b['ts'] !; + const diff = a['ts']! - b['ts']!; return diff > 0 ? 1 : diff < 0 ? -1 : 0; }); } @@ -230,8 +233,8 @@ export class PerflogMetric extends Metric { result['requestCount'] = 0; } - let markStartEvent: PerfLogEvent = null !; - let markEndEvent: PerfLogEvent = null !; + let markStartEvent: PerfLogEvent = null!; + let markEndEvent: PerfLogEvent = null!; events.forEach((event) => { const ph = event['ph']; const name = event['name']; @@ -267,7 +270,7 @@ export class PerflogMetric extends Metric { let inMeasureRange = false; events.forEach((event) => { const ph = event['ph']; - let name = event['name'] !; + let name = event['name']!; let microIterations = 1; const microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX); if (microIterationsMatch) { @@ -286,7 +289,7 @@ export class PerflogMetric extends Metric { if (this._requestCount && name === 'sendRequest') { result['requestCount'] += 1; } else if (this._receivedData && name === 'receivedData' && ph === 'I') { - result['receivedData'] += event['args'] !['encodedDataLength'] !; + result['receivedData'] += event['args']!['encodedDataLength']!; } if (ph === 'B' && name === _MARK_NAME_FRAME_CAPTURE) { if (frameCaptureStartEvent) { @@ -305,7 +308,7 @@ export class PerflogMetric extends Metric { } if (ph === 'I' && frameCaptureStartEvent && !frameCaptureEndEvent && name === 'frame') { - frameTimestamps.push(event['ts'] !); + frameTimestamps.push(event['ts']!); if (frameTimestamps.length >= 2) { frameTimes.push( frameTimestamps[frameTimestamps.length - 1] - @@ -324,14 +327,14 @@ export class PerflogMetric extends Metric { intervalStartCount[name]--; if (intervalStartCount[name] === 0) { const startEvent = intervalStarts[name]; - const duration = (event['ts'] ! - startEvent['ts'] !); - intervalStarts[name] = null !; + const duration = (event['ts']! - startEvent['ts']!); + intervalStarts[name] = null!; if (name === 'gc') { result['gcTime'] += duration; const amount = - (startEvent['args'] !['usedHeapSize'] ! - event['args'] !['usedHeapSize'] !) / 1000; + (startEvent['args']!['usedHeapSize']! - event['args']!['usedHeapSize']!) / 1000; result['gcAmount'] += amount; - const majorGc = event['args'] !['majorGc']; + const majorGc = event['args']!['majorGc']; if (majorGc && majorGc) { result['majorGcTime'] += duration; } @@ -374,7 +377,9 @@ export class PerflogMetric extends Metric { frameTimes.filter(t => t < _FRAME_TIME_SMOOTH_THRESHOLD).length / frameTimes.length; } - private _markName(index: number) { return `${_MARK_NAME_PREFIX}${index}`; } + private _markName(index: number) { + return `${_MARK_NAME_PREFIX}${index}`; + } } const _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/; diff --git a/packages/benchpress/src/metric/user_metric.ts b/packages/benchpress/src/metric/user_metric.ts index 0adf2f08ef6ae..d08964a75f6b4 100644 --- a/packages/benchpress/src/metric/user_metric.ts +++ b/packages/benchpress/src/metric/user_metric.ts @@ -26,7 +26,9 @@ export class UserMetric extends Metric { /** * Starts measuring */ - beginMeasure(): Promise<any> { return Promise.resolve(true); } + beginMeasure(): Promise<any> { + return Promise.resolve(true); + } /** * Ends measuring. @@ -34,8 +36,7 @@ export class UserMetric extends Metric { endMeasure(restart: boolean): Promise<{[key: string]: any}> { let resolve: (result: any) => void; let reject: (error: any) => void; - const promise = new Promise < { [key: string]: any; } - > ((res, rej) => { + const promise = new Promise<{[key: string]: any;}>((res, rej) => { resolve = res; reject = rej; }); @@ -67,5 +68,7 @@ export class UserMetric extends Metric { * Describes the metrics provided by this metric implementation. * (e.g. units, ...) */ - describe(): {[key: string]: any} { return this._userMetrics; } + describe(): {[key: string]: any} { + return this._userMetrics; + } } diff --git a/packages/benchpress/src/reporter.ts b/packages/benchpress/src/reporter.ts index 0a75e79614a57..842fd31d89d92 100644 --- a/packages/benchpress/src/reporter.ts +++ b/packages/benchpress/src/reporter.ts @@ -12,7 +12,9 @@ import {MeasureValues} from './measure_values'; * A reporter reports measure values and the valid sample. */ export abstract class Reporter { - reportMeasureValues(values: MeasureValues): Promise<any> { throw new Error('NYI'); } + reportMeasureValues(values: MeasureValues): Promise<any> { + throw new Error('NYI'); + } reportSample(completeSample: MeasureValues[], validSample: MeasureValues[]): Promise<any> { throw new Error('NYI'); diff --git a/packages/benchpress/src/reporter/console_reporter.ts b/packages/benchpress/src/reporter/console_reporter.ts index 1659640010591..e2dc0e519c386 100644 --- a/packages/benchpress/src/reporter/console_reporter.ts +++ b/packages/benchpress/src/reporter/console_reporter.ts @@ -28,10 +28,11 @@ export class ConsoleReporter extends Reporter { }, {provide: ConsoleReporter.COLUMN_WIDTH, useValue: 18}, { provide: ConsoleReporter.PRINT, - useValue: function(v: any) { - // tslint:disable-next-line:no-console - console.log(v); - } + useValue: + function(v: any) { + // tslint:disable-next-line:no-console + console.log(v); + } } ]; @@ -58,7 +59,9 @@ export class ConsoleReporter extends Reporter { this._print(`BENCHMARK ${sampleDescription.id}`); this._print('Description:'); const props = sortedProps(sampleDescription.description); - props.forEach((prop) => { this._print(`- ${prop}: ${sampleDescription.description[prop]}`); }); + props.forEach((prop) => { + this._print(`- ${prop}: ${sampleDescription.description[prop]}`); + }); this._print('Metrics:'); this._metricNames.forEach((metricName) => { this._print(`- ${metricName}: ${sampleDescription.metrics[metricName]}`); diff --git a/packages/benchpress/src/reporter/json_file_reporter.ts b/packages/benchpress/src/reporter/json_file_reporter.ts index aaca71e9164ad..702b94ca5d695 100644 --- a/packages/benchpress/src/reporter/json_file_reporter.ts +++ b/packages/benchpress/src/reporter/json_file_reporter.ts @@ -37,7 +37,9 @@ export class JsonFileReporter extends Reporter { super(); } - reportMeasureValues(measureValues: MeasureValues): Promise<any> { return Promise.resolve(null); } + reportMeasureValues(measureValues: MeasureValues): Promise<any> { + return Promise.resolve(null); + } reportSample(completeSample: MeasureValues[], validSample: MeasureValues[]): Promise<any> { const stats: {[key: string]: string} = {}; diff --git a/packages/benchpress/src/reporter/multi_reporter.ts b/packages/benchpress/src/reporter/multi_reporter.ts index 07b507148ac24..355ef4482f12f 100644 --- a/packages/benchpress/src/reporter/multi_reporter.ts +++ b/packages/benchpress/src/reporter/multi_reporter.ts @@ -27,7 +27,9 @@ export class MultiReporter extends Reporter { ]; } - constructor(private _reporters: Reporter[]) { super(); } + constructor(private _reporters: Reporter[]) { + super(); + } reportMeasureValues(values: MeasureValues): Promise<any[]> { return Promise.all(this._reporters.map(reporter => reporter.reportMeasureValues(values))); diff --git a/packages/benchpress/src/runner.ts b/packages/benchpress/src/runner.ts index 7ba28dbf8a330..2b6e2f65fc270 100644 --- a/packages/benchpress/src/runner.ts +++ b/packages/benchpress/src/runner.ts @@ -17,7 +17,7 @@ import {Reporter} from './reporter'; import {ConsoleReporter} from './reporter/console_reporter'; import {MultiReporter} from './reporter/multi_reporter'; import {SampleDescription} from './sample_description'; -import {SampleState, Sampler} from './sampler'; +import {Sampler, SampleState} from './sampler'; import {Validator} from './validator'; import {RegressionSlopeValidator} from './validator/regression_slope_validator'; import {SizeValidator} from './validator/size_validator'; diff --git a/packages/benchpress/src/sample_description.ts b/packages/benchpress/src/sample_description.ts index ccecf159b5754..c99d65f902dbb 100644 --- a/packages/benchpress/src/sample_description.ts +++ b/packages/benchpress/src/sample_description.ts @@ -29,10 +29,11 @@ export class SampleDescription { userDesc ], metric.describe()), - deps: [ - Metric, Options.SAMPLE_ID, Options.FORCE_GC, Options.USER_AGENT, Validator, - Options.DEFAULT_DESCRIPTION, Options.SAMPLE_DESCRIPTION - ] + deps: + [ + Metric, Options.SAMPLE_ID, Options.FORCE_GC, Options.USER_AGENT, Validator, + Options.DEFAULT_DESCRIPTION, Options.SAMPLE_DESCRIPTION + ] }]; description: {[key: string]: any}; @@ -41,9 +42,13 @@ export class SampleDescription { public metrics: {[key: string]: any}) { this.description = {}; descriptions.forEach(description => { - Object.keys(description).forEach(prop => { this.description[prop] = description[prop]; }); + Object.keys(description).forEach(prop => { + this.description[prop] = description[prop]; + }); }); } - toJson() { return {'id': this.id, 'description': this.description, 'metrics': this.metrics}; } + toJson() { + return {'id': this.id, 'description': this.description, 'metrics': this.metrics}; + } } diff --git a/packages/benchpress/src/sampler.ts b/packages/benchpress/src/sampler.ts index 720fe621a5355..1d3999682b2fc 100644 --- a/packages/benchpress/src/sampler.ts +++ b/packages/benchpress/src/sampler.ts @@ -28,9 +28,11 @@ import {WebDriverAdapter} from './web_driver_adapter'; export class Sampler { static PROVIDERS = <StaticProvider[]>[{ provide: Sampler, - deps: [ - WebDriverAdapter, Metric, Reporter, Validator, Options.PREPARE, Options.EXECUTE, Options.NOW - ] + deps: + [ + WebDriverAdapter, Metric, Reporter, Validator, Options.PREPARE, Options.EXECUTE, + Options.NOW + ] }]; constructor( private _driver: WebDriverAdapter, private _metric: Metric, private _reporter: Reporter, diff --git a/packages/benchpress/src/validator.ts b/packages/benchpress/src/validator.ts index 536c22949d6e0..8838af7a2f772 100644 --- a/packages/benchpress/src/validator.ts +++ b/packages/benchpress/src/validator.ts @@ -17,11 +17,15 @@ export abstract class Validator { /** * Calculates a valid sample out of the complete sample */ - validate(completeSample: MeasureValues[]): MeasureValues[]|null { throw new Error('NYI'); } + validate(completeSample: MeasureValues[]): MeasureValues[]|null { + throw new Error('NYI'); + } /** * Returns a Map that describes the properties of the validator * (e.g. sample size, ...) */ - describe(): {[key: string]: any} { throw new Error('NYI'); } + describe(): {[key: string]: any} { + throw new Error('NYI'); + } } diff --git a/packages/benchpress/src/validator/size_validator.ts b/packages/benchpress/src/validator/size_validator.ts index 7d546c186a436..61e6c8cf71914 100644 --- a/packages/benchpress/src/validator/size_validator.ts +++ b/packages/benchpress/src/validator/size_validator.ts @@ -22,9 +22,13 @@ export class SizeValidator extends Validator { {provide: SizeValidator.SAMPLE_SIZE, useValue: 10} ]; - constructor(@Inject(SizeValidator.SAMPLE_SIZE) private _sampleSize: number) { super(); } + constructor(@Inject(SizeValidator.SAMPLE_SIZE) private _sampleSize: number) { + super(); + } - describe(): {[key: string]: any} { return {'sampleSize': this._sampleSize}; } + describe(): {[key: string]: any} { + return {'sampleSize': this._sampleSize}; + } validate(completeSample: MeasureValues[]): MeasureValues[]|null { if (completeSample.length >= this._sampleSize) { diff --git a/packages/benchpress/src/web_driver_adapter.ts b/packages/benchpress/src/web_driver_adapter.ts index d43e022f0e477..7e671a59ad89c 100644 --- a/packages/benchpress/src/web_driver_adapter.ts +++ b/packages/benchpress/src/web_driver_adapter.ts @@ -14,9 +14,19 @@ * Needs one implementation for every supported WebDriver client. */ export abstract class WebDriverAdapter { - waitFor(callback: Function): Promise<any> { throw new Error('NYI'); } - executeScript(script: string): Promise<any> { throw new Error('NYI'); } - executeAsyncScript(script: string): Promise<any> { throw new Error('NYI'); } - capabilities(): Promise<{[key: string]: any}> { throw new Error('NYI'); } - logs(type: string): Promise<any[]> { throw new Error('NYI'); } + waitFor(callback: Function): Promise<any> { + throw new Error('NYI'); + } + executeScript(script: string): Promise<any> { + throw new Error('NYI'); + } + executeAsyncScript(script: string): Promise<any> { + throw new Error('NYI'); + } + capabilities(): Promise<{[key: string]: any}> { + throw new Error('NYI'); + } + logs(type: string): Promise<any[]> { + throw new Error('NYI'); + } } diff --git a/packages/benchpress/src/web_driver_extension.ts b/packages/benchpress/src/web_driver_extension.ts index f5d1739763811..87e83c29bdb1c 100644 --- a/packages/benchpress/src/web_driver_extension.ts +++ b/packages/benchpress/src/web_driver_extension.ts @@ -12,7 +12,7 @@ import {Options} from './common_options'; export type PerfLogEvent = { [key: string]: any -} & { +}&{ ph?: 'X' | 'B' | 'E' | 'I', ts?: number, dur?: number, @@ -43,7 +43,7 @@ export abstract class WebDriverExtension { { provide: WebDriverExtension, useFactory: (children: WebDriverExtension[], capabilities: {[key: string]: any}) => { - let delegate: WebDriverExtension = undefined !; + let delegate: WebDriverExtension = undefined!; children.forEach(extension => { if (extension.supports(capabilities)) { delegate = extension; @@ -60,11 +60,17 @@ export abstract class WebDriverExtension { return res; } - gc(): Promise<any> { throw new Error('NYI'); } + gc(): Promise<any> { + throw new Error('NYI'); + } - timeBegin(name: string): Promise<any> { throw new Error('NYI'); } + timeBegin(name: string): Promise<any> { + throw new Error('NYI'); + } - timeEnd(name: string, restartName: string|null): Promise<any> { throw new Error('NYI'); } + timeEnd(name: string, restartName: string|null): Promise<any> { + throw new Error('NYI'); + } /** * Format: @@ -78,11 +84,17 @@ export abstract class WebDriverExtension { * Based on [Chrome Trace Event *Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit) **/ - readPerfLog(): Promise<PerfLogEvent[]> { throw new Error('NYI'); } + readPerfLog(): Promise<PerfLogEvent[]> { + throw new Error('NYI'); + } - perfLogFeatures(): PerfLogFeatures { throw new Error('NYI'); } + perfLogFeatures(): PerfLogFeatures { + throw new Error('NYI'); + } - supports(capabilities: {[key: string]: any}): boolean { return true; } + supports(capabilities: {[key: string]: any}): boolean { + return true; + } } export class PerfLogFeatures { diff --git a/packages/benchpress/src/webdriver/chrome_driver_extension.ts b/packages/benchpress/src/webdriver/chrome_driver_extension.ts index 9dba35fb28cbe..649c8f925082e 100644 --- a/packages/benchpress/src/webdriver/chrome_driver_extension.ts +++ b/packages/benchpress/src/webdriver/chrome_driver_extension.ts @@ -54,7 +54,9 @@ export class ChromeDriverExtension extends WebDriverExtension { return parseInt(v, 10); } - gc() { return this.driver.executeScript('window.gc()'); } + gc() { + return this.driver.executeScript('window.gc()'); + } async timeBegin(name: string): Promise<any> { if (this._firstRun) { @@ -108,7 +110,7 @@ export class ChromeDriverExtension extends WebDriverExtension { chromeEvents.forEach((event) => { const categories = this._parseCategories(event['cat']); const normalizedEvent = this._convertEvent(event, categories); - if (normalizedEvent != null) normalizedEvents !.push(normalizedEvent); + if (normalizedEvent != null) normalizedEvents!.push(normalizedEvent); }); return normalizedEvents; } @@ -184,7 +186,9 @@ export class ChromeDriverExtension extends WebDriverExtension { return null; // nothing useful in this event } - private _parseCategories(categories: string): string[] { return categories.split(','); } + private _parseCategories(categories: string): string[] { + return categories.split(','); + } private _isEvent( eventCategories: string[], eventName: string, expectedCategories: string[], diff --git a/packages/benchpress/src/webdriver/firefox_driver_extension.ts b/packages/benchpress/src/webdriver/firefox_driver_extension.ts index 2d34742505618..796eaef34d0d8 100644 --- a/packages/benchpress/src/webdriver/firefox_driver_extension.ts +++ b/packages/benchpress/src/webdriver/firefox_driver_extension.ts @@ -22,7 +22,9 @@ export class FirefoxDriverExtension extends WebDriverExtension { this._profilerStarted = false; } - gc() { return this._driver.executeScript('window.forceGC()'); } + gc() { + return this._driver.executeScript('window.forceGC()'); + } timeBegin(name: string): Promise<any> { if (!this._profilerStarted) { @@ -44,7 +46,9 @@ export class FirefoxDriverExtension extends WebDriverExtension { return this._driver.executeAsyncScript('var cb = arguments[0]; window.getProfile(cb);'); } - perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true, gc: true}); } + perfLogFeatures(): PerfLogFeatures { + return new PerfLogFeatures({render: true, gc: true}); + } supports(capabilities: {[key: string]: any}): boolean { return capabilities['browserName'].toLowerCase() === 'firefox'; diff --git a/packages/benchpress/src/webdriver/ios_driver_extension.ts b/packages/benchpress/src/webdriver/ios_driver_extension.ts index fefef6314c006..5200ee57e614f 100644 --- a/packages/benchpress/src/webdriver/ios_driver_extension.ts +++ b/packages/benchpress/src/webdriver/ios_driver_extension.ts @@ -15,9 +15,13 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e export class IOsDriverExtension extends WebDriverExtension { static PROVIDERS = [{provide: IOsDriverExtension, deps: [WebDriverAdapter]}]; - constructor(private _driver: WebDriverAdapter) { super(); } + constructor(private _driver: WebDriverAdapter) { + super(); + } - gc(): Promise<any> { throw new Error('Force GC is not supported on iOS'); } + gc(): Promise<any> { + throw new Error('Force GC is not supported on iOS'); + } timeBegin(name: string): Promise<any> { return this._driver.executeScript(`console.time('${name}');`); @@ -62,16 +66,16 @@ export class IOsDriverExtension extends WebDriverExtension { const endTime = record['endTime']; if (type === 'FunctionCall' && (data == null || data['scriptName'] !== 'InjectedScript')) { - events !.push(createStartEvent('script', startTime)); + events!.push(createStartEvent('script', startTime)); endEvent = createEndEvent('script', endTime); } else if (type === 'Time') { - events !.push(createMarkStartEvent(data['message'], startTime)); + events!.push(createMarkStartEvent(data['message'], startTime)); } else if (type === 'TimeEnd') { - events !.push(createMarkEndEvent(data['message'], startTime)); + events!.push(createMarkEndEvent(data['message'], startTime)); } else if ( type === 'RecalculateStyles' || type === 'Layout' || type === 'UpdateLayerTree' || type === 'Paint' || type === 'Rasterize' || type === 'CompositeLayers') { - events !.push(createStartEvent('render', startTime)); + events!.push(createStartEvent('render', startTime)); endEvent = createEndEvent('render', endTime); } // Note: ios used to support GCEvent up until iOS 6 :-( @@ -79,21 +83,22 @@ export class IOsDriverExtension extends WebDriverExtension { this._convertPerfRecordsToEvents(record['children'], events); } if (endEvent != null) { - events !.push(endEvent); + events!.push(endEvent); } }); return events; } - perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true}); } + perfLogFeatures(): PerfLogFeatures { + return new PerfLogFeatures({render: true}); + } supports(capabilities: {[key: string]: any}): boolean { return capabilities['browserName'].toLowerCase() === 'safari'; } } -function createEvent( - ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) { +function createEvent(ph: 'X'|'B'|'E'|'B'|'E', name: string, time: number, args: any = null) { const result: PerfLogEvent = { 'cat': 'timeline', 'name': name, diff --git a/packages/benchpress/src/webdriver/selenium_webdriver_adapter.ts b/packages/benchpress/src/webdriver/selenium_webdriver_adapter.ts index 3f7f7c4aa1fd7..74cc405dd3ff0 100644 --- a/packages/benchpress/src/webdriver/selenium_webdriver_adapter.ts +++ b/packages/benchpress/src/webdriver/selenium_webdriver_adapter.ts @@ -21,11 +21,17 @@ export class SeleniumWebDriverAdapter extends WebDriverAdapter { deps: [] }]; - constructor(private _driver: any) { super(); } + constructor(private _driver: any) { + super(); + } - waitFor(callback: () => any): Promise<any> { return this._driver.call(callback); } + waitFor(callback: () => any): Promise<any> { + return this._driver.call(callback); + } - executeScript(script: string): Promise<any> { return this._driver.executeScript(script); } + executeScript(script: string): Promise<any> { + return this._driver.executeScript(script); + } executeAsyncScript(script: string): Promise<any> { return this._driver.executeAsyncScript(script); @@ -58,7 +64,9 @@ class Command { private parameters_: {[key: string]: any} = {}; constructor(private name_: string) {} - getName() { return this.name_; } + getName() { + return this.name_; + } setParameter(name: string, value: any) { this.parameters_[name] = value; @@ -70,7 +78,11 @@ class Command { return this; } - getParameter(key: string) { return this.parameters_[key]; } + getParameter(key: string) { + return this.parameters_[key]; + } - getParameters() { return this.parameters_; } + getParameters() { + return this.parameters_; + } } diff --git a/packages/benchpress/test/firefox_extension/conf.ts b/packages/benchpress/test/firefox_extension/conf.ts deleted file mode 100644 index 4be1898f2bea9..0000000000000 --- a/packages/benchpress/test/firefox_extension/conf.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @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 - */ - -require('core-js'); -require('reflect-metadata'); -const testHelper = require('../../src/firefox_extension/lib/test_helper.js'); - -exports.config = { - specs: ['spec.js', 'sample_benchmark.js'], - - framework: 'jasmine2', - - jasmineNodeOpts: {showColors: true, defaultTimeoutInterval: 1200000}, - - getMultiCapabilities: function() { return testHelper.getFirefoxProfileWithExtension(); } -}; diff --git a/packages/benchpress/test/firefox_extension/parser_util_spec.ts b/packages/benchpress/test/firefox_extension/parser_util_spec.ts deleted file mode 100644 index 1669d0703651a..0000000000000 --- a/packages/benchpress/test/firefox_extension/parser_util_spec.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @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 {convertPerfProfileToEvents} from '../../src/firefox_extension/lib/parser_util'; - -function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) { - expect(actualEvents.length == expectedEvents.length); - for (let i = 0; i < actualEvents.length; ++i) { - const actualEvent = actualEvents[i]; - const expectedEvent = expectedEvents[i]; - for (const key in actualEvent) { - expect(actualEvent[key]).toEqual(expectedEvent[key]); - } - } -} - -{ - describe('convertPerfProfileToEvents', function() { - it('should convert single instantaneous event', function() { - const profileData = { - threads: [ - {samples: [{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}]} - ] - }; - const perfEvents = convertPerfProfileToEvents(profileData); - assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]); - }); - - it('should convert single non-instantaneous event', function() { - const profileData = { - threads: [{ - samples: [ - {time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}, - {time: 2, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}, - {time: 100, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]} - ] - }] - }; - const perfEvents = convertPerfProfileToEvents(profileData); - assertEventsEqual( - perfEvents, [{ph: 'B', ts: 1, name: 'script'}, {ph: 'E', ts: 100, name: 'script'}]); - }); - - it('should convert multiple instantaneous events', function() { - const profileData = { - threads: [{ - samples: [ - {time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}, - {time: 2, frames: [{location: 'PresShell::Paint'}]} - ] - }] - }; - const perfEvents = convertPerfProfileToEvents(profileData); - assertEventsEqual( - perfEvents, [{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'}]); - }); - - it('should convert multiple mixed events', function() { - const profileData = { - threads: [{ - samples: [ - {time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}, - {time: 2, frames: [{location: 'PresShell::Paint'}]}, - {time: 5, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}, - {time: 10, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]} - ] - }] - }; - const perfEvents = convertPerfProfileToEvents(profileData); - assertEventsEqual(perfEvents, [ - {ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'}, - {ph: 'B', ts: 5, name: 'script'}, {ph: 'E', ts: 10, name: 'script'} - ]); - }); - - it('should add args to gc events', function() { - const profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]}; - const perfEvents = convertPerfProfileToEvents(profileData); - assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'gc', args: {usedHeapSize: 0}}]); - }); - - it('should skip unknown events', function() { - const profileData = { - threads: [{ - samples: [ - {time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}, - {time: 2, frames: [{location: 'foo'}]} - ] - }] - }; - const perfEvents = convertPerfProfileToEvents(profileData); - assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]); - }); - }); -} diff --git a/packages/benchpress/test/firefox_extension/sample_benchmark_spec.ts b/packages/benchpress/test/firefox_extension/sample_benchmark_spec.ts deleted file mode 100644 index 2657a2347a147..0000000000000 --- a/packages/benchpress/test/firefox_extension/sample_benchmark_spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @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 {$, browser} from 'protractor'; - -const benchpress = require('../../index.js'); - -// TODO: this test is currnetly failing. it seems that it didn't run on the ci for a while -xdescribe('deep tree baseline', function() { - const runner = new benchpress.Runner([ - // use protractor as Webdriver client - benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS, - // use RegressionSlopeValidator to validate samples - benchpress.Validator.bind(benchpress.RegressionSlopeValidator), - // use 10 samples to calculate slope regression - benchpress.bind(benchpress.RegressionSlopeValidator.SAMPLE_SIZE).toValue(20), - // use the script metric to calculate slope regression - benchpress.bind(benchpress.RegressionSlopeValidator.METRIC).toValue('scriptTime'), - benchpress.bind(benchpress.Options.FORCE_GC).toValue(true) - ]); - - - it('should be fast!', function(done) { - browser.ignoreSynchronization = true; - browser.get('http://localhost:8001/playground/src/benchpress/'); - - /* - * Tell benchpress to click the buttons to destroy and re-create the tree for each sample. - * Benchpress will log the collected metrics after each sample is collected, and will stop - * sampling as soon as the calculated regression slope for last 20 samples is stable. - */ - runner - .sample({ - id: 'baseline', - execute: function() { $('button').click(); }, - providers: [benchpress.bind(benchpress.Options.SAMPLE_DESCRIPTION).toValue({depth: 9})] - }) - .then(done, done.fail); - }); -}); diff --git a/packages/benchpress/test/firefox_extension/spec.ts b/packages/benchpress/test/firefox_extension/spec.ts deleted file mode 100644 index 754ea37212340..0000000000000 --- a/packages/benchpress/test/firefox_extension/spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @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 - */ - -/* tslint:disable:no-console */ -import {browser} from 'protractor'; - -const assertEventsContainsName = function(events: any[], eventName: string) { - let found = false; - for (let i = 0; i < events.length; ++i) { - if (events[i].name == eventName) { - found = true; - break; - } - } - expect(found).toBeTruthy(); -}; - -// TODO: this test is currnetly failing. it seems that it didn't run on the ci for a while -xdescribe('firefox extension', function() { - const TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html'; - - it('should measure performance', function() { - browser.sleep(3000); // wait for extension to load - - browser.driver.get(TEST_URL); - - browser.executeScript('window.startProfiler()').then(function() { - console.log('started measuring perf'); - }); - - browser.executeAsyncScript('setTimeout(arguments[0], 1000);'); - browser.executeScript('window.forceGC()'); - - browser.executeAsyncScript('var cb = arguments[0]; window.getProfile(cb);') - .then(function(profile: any) { - assertEventsContainsName(profile, 'gc'); - assertEventsContainsName(profile, 'script'); - }); - }); -}); diff --git a/packages/benchpress/test/metric/multi_metric_spec.ts b/packages/benchpress/test/metric/multi_metric_spec.ts index 1eb8bda5bc8b2..16335f7fcd676 100644 --- a/packages/benchpress/test/metric/multi_metric_spec.ts +++ b/packages/benchpress/test/metric/multi_metric_spec.ts @@ -11,50 +11,52 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/te import {Injector, Metric, MultiMetric} from '../../index'; (function() { - function createMetric(ids: any[]) { - const m = Injector - .create([ - ids.map(id => ({provide: id, useValue: new MockMetric(id)})), - MultiMetric.provideWith(ids) - ]) - .get<MultiMetric>(MultiMetric); - return Promise.resolve(m); - } +function createMetric(ids: any[]) { + const m = Injector + .create([ + ids.map(id => ({provide: id, useValue: new MockMetric(id)})), + MultiMetric.provideWith(ids) + ]) + .get<MultiMetric>(MultiMetric); + return Promise.resolve(m); +} - describe('multi metric', () => { - it('should merge descriptions', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createMetric(['m1', 'm2']).then((m) => { - expect(m.describe()).toEqual({'m1': 'describe', 'm2': 'describe'}); - async.done(); - }); - })); +describe('multi metric', () => { + it('should merge descriptions', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + createMetric(['m1', 'm2']).then((m) => { + expect(m.describe()).toEqual({'m1': 'describe', 'm2': 'describe'}); + async.done(); + }); + })); - it('should merge all beginMeasure calls', + it('should merge all beginMeasure calls', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + createMetric(['m1', 'm2']).then((m) => m.beginMeasure()).then((values) => { + expect(values).toEqual(['m1_beginMeasure', 'm2_beginMeasure']); + async.done(); + }); + })); + + [false, true].forEach((restartFlag) => { + it(`should merge all endMeasure calls for restart=${restartFlag}`, inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createMetric(['m1', 'm2']).then((m) => m.beginMeasure()).then((values) => { - expect(values).toEqual(['m1_beginMeasure', 'm2_beginMeasure']); + createMetric(['m1', 'm2']).then((m) => m.endMeasure(restartFlag)).then((values) => { + expect(values).toEqual({'m1': {'restart': restartFlag}, 'm2': {'restart': restartFlag}}); async.done(); }); })); - - [false, true].forEach((restartFlag) => { - it(`should merge all endMeasure calls for restart=${restartFlag}`, - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createMetric(['m1', 'm2']).then((m) => m.endMeasure(restartFlag)).then((values) => { - expect(values).toEqual( - {'m1': {'restart': restartFlag}, 'm2': {'restart': restartFlag}}); - async.done(); - }); - })); - }); - }); +}); })(); class MockMetric extends Metric { - constructor(private _id: string) { super(); } + constructor(private _id: string) { + super(); + } - beginMeasure(): Promise<string> { return Promise.resolve(`${this._id}_beginMeasure`); } + beginMeasure(): Promise<string> { + return Promise.resolve(`${this._id}_beginMeasure`); + } endMeasure(restart: boolean): Promise<{[key: string]: any}> { const result: {[key: string]: any} = {}; diff --git a/packages/benchpress/test/metric/perflog_metric_spec.ts b/packages/benchpress/test/metric/perflog_metric_spec.ts index 7c068403a7de6..0133ff65db3f8 100644 --- a/packages/benchpress/test/metric/perflog_metric_spec.ts +++ b/packages/benchpress/test/metric/perflog_metric_spec.ts @@ -13,691 +13,677 @@ import {Injector, Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, import {TraceEventFactory} from '../trace_event_factory'; (function() { - let commandLog: any[]; - const eventFactory = new TraceEventFactory('timeline', 'pid0'); - - function createMetric( - perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures, - {microMetrics, forceGc, captureFrames, receivedData, requestCount, ignoreNavigation}: { - microMetrics?: {[key: string]: string}, - forceGc?: boolean, - captureFrames?: boolean, - receivedData?: boolean, - requestCount?: boolean, - ignoreNavigation?: boolean - } = {}): Metric { - commandLog = []; - if (!perfLogFeatures) { - perfLogFeatures = - new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); - } - if (!microMetrics) { - microMetrics = {}; - } - const providers: StaticProvider[] = [ - Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS, - {provide: Options.MICRO_METRICS, useValue: microMetrics}, { - provide: PerflogMetric.SET_TIMEOUT, - useValue: (fn: Function, millis: number) => { - commandLog.push(['setTimeout', millis]); - fn(); - }, +let commandLog: any[]; +const eventFactory = new TraceEventFactory('timeline', 'pid0'); + +function createMetric( + perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures, + {microMetrics, forceGc, captureFrames, receivedData, requestCount, ignoreNavigation}: { + microMetrics?: {[key: string]: string}, + forceGc?: boolean, + captureFrames?: boolean, + receivedData?: boolean, + requestCount?: boolean, + ignoreNavigation?: boolean + } = {}): Metric { + commandLog = []; + if (!perfLogFeatures) { + perfLogFeatures = + new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); + } + if (!microMetrics) { + microMetrics = {}; + } + const providers: StaticProvider[] = [ + Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS, + {provide: Options.MICRO_METRICS, useValue: microMetrics}, { + provide: PerflogMetric.SET_TIMEOUT, + useValue: (fn: Function, millis: number) => { + commandLog.push(['setTimeout', millis]); + fn(); }, - { - provide: WebDriverExtension, - useValue: new MockDriverExtension(perfLogs, commandLog, perfLogFeatures) - } - ]; - if (forceGc != null) { - providers.push({provide: Options.FORCE_GC, useValue: forceGc}); - } - if (captureFrames != null) { - providers.push({provide: Options.CAPTURE_FRAMES, useValue: captureFrames}); - } - if (receivedData != null) { - providers.push({provide: Options.RECEIVED_DATA, useValue: receivedData}); + }, + { + provide: WebDriverExtension, + useValue: new MockDriverExtension(perfLogs, commandLog, perfLogFeatures) } - if (requestCount != null) { - providers.push({provide: Options.REQUEST_COUNT, useValue: requestCount}); - } - if (ignoreNavigation != null) { - providers.push({provide: PerflogMetric.IGNORE_NAVIGATION, useValue: ignoreNavigation}); - } - return Injector.create(providers).get(PerflogMetric); + ]; + if (forceGc != null) { + providers.push({provide: Options.FORCE_GC, useValue: forceGc}); } + if (captureFrames != null) { + providers.push({provide: Options.CAPTURE_FRAMES, useValue: captureFrames}); + } + if (receivedData != null) { + providers.push({provide: Options.RECEIVED_DATA, useValue: receivedData}); + } + if (requestCount != null) { + providers.push({provide: Options.REQUEST_COUNT, useValue: requestCount}); + } + if (ignoreNavigation != null) { + providers.push({provide: PerflogMetric.IGNORE_NAVIGATION, useValue: ignoreNavigation}); + } + return Injector.create(providers).get(PerflogMetric); +} - describe('perflog metric', () => { - - function sortedKeys(stringMap: {[key: string]: any}) { - const res: string[] = []; - res.push(...Object.keys(stringMap)); - res.sort(); - return res; - } - - it('should describe itself based on the perfLogFeatrues', () => { - expect(sortedKeys(createMetric([[]], new PerfLogFeatures()).describe())).toEqual([ - 'pureScriptTime', 'scriptTime' - ]); - - expect( - sortedKeys(createMetric([[]], new PerfLogFeatures({render: true, gc: false})).describe())) - .toEqual(['pureScriptTime', 'renderTime', 'scriptTime']); - - expect(sortedKeys(createMetric([[]], null !).describe())).toEqual([ - 'gcAmount', 'gcTime', 'majorGcTime', 'pureScriptTime', 'renderTime', 'scriptTime' - ]); - - expect(sortedKeys(createMetric([[]], new PerfLogFeatures({render: true, gc: true}), { - forceGc: true - }).describe())) - .toEqual([ - 'forcedGcAmount', 'forcedGcTime', 'gcAmount', 'gcTime', 'majorGcTime', 'pureScriptTime', - 'renderTime', 'scriptTime' - ]); - - - expect(sortedKeys(createMetric([[]], new PerfLogFeatures({userTiming: true}), { - receivedData: true, - requestCount: true - }).describe())) - .toEqual(['pureScriptTime', 'receivedData', 'requestCount', 'scriptTime']); - }); - - it('should describe itself based on micro metrics', () => { - const description = - createMetric([[]], null !, {microMetrics: {'myMicroMetric': 'someDesc'}}).describe(); - expect(description['myMicroMetric']).toEqual('someDesc'); - }); - - it('should describe itself if frame capture is requested and available', () => { - const description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), { - captureFrames: true - }).describe(); - expect(description['frameTime.mean']).not.toContain('WARNING'); - expect(description['frameTime.best']).not.toContain('WARNING'); - expect(description['frameTime.worst']).not.toContain('WARNING'); - expect(description['frameTime.smooth']).not.toContain('WARNING'); - }); +describe('perflog metric', () => { + function sortedKeys(stringMap: {[key: string]: any}) { + const res: string[] = []; + res.push(...Object.keys(stringMap)); + res.sort(); + return res; + } - it('should describe itself if frame capture is requested and not available', () => { - const description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), { - captureFrames: true - }).describe(); - expect(description['frameTime.mean']).toContain('WARNING'); - expect(description['frameTime.best']).toContain('WARNING'); - expect(description['frameTime.worst']).toContain('WARNING'); - expect(description['frameTime.smooth']).toContain('WARNING'); - }); + it('should describe itself based on the perfLogFeatrues', () => { + expect(sortedKeys(createMetric([[]], new PerfLogFeatures()).describe())).toEqual([ + 'pureScriptTime', 'scriptTime' + ]); + + expect( + sortedKeys(createMetric([[]], new PerfLogFeatures({render: true, gc: false})).describe())) + .toEqual(['pureScriptTime', 'renderTime', 'scriptTime']); + + expect(sortedKeys(createMetric([[]], null!).describe())).toEqual([ + 'gcAmount', 'gcTime', 'majorGcTime', 'pureScriptTime', 'renderTime', 'scriptTime' + ]); + + expect(sortedKeys(createMetric([[]], new PerfLogFeatures({render: true, gc: true}), { + forceGc: true + }).describe())) + .toEqual([ + 'forcedGcAmount', 'forcedGcTime', 'gcAmount', 'gcTime', 'majorGcTime', 'pureScriptTime', + 'renderTime', 'scriptTime' + ]); + + + expect(sortedKeys(createMetric([[]], new PerfLogFeatures({userTiming: true}), { + receivedData: true, + requestCount: true + }).describe())) + .toEqual(['pureScriptTime', 'receivedData', 'requestCount', 'scriptTime']); + }); - describe('beginMeasure', () => { + it('should describe itself based on micro metrics', () => { + const description = + createMetric([[]], null!, {microMetrics: {'myMicroMetric': 'someDesc'}}).describe(); + expect(description['myMicroMetric']).toEqual('someDesc'); + }); - it('should not force gc and mark the timeline', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric([[]], null !); - metric.beginMeasure().then((_) => { - expect(commandLog).toEqual([['timeBegin', 'benchpress0']]); + it('should describe itself if frame capture is requested and available', () => { + const description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), { + captureFrames: true + }).describe(); + expect(description['frameTime.mean']).not.toContain('WARNING'); + expect(description['frameTime.best']).not.toContain('WARNING'); + expect(description['frameTime.worst']).not.toContain('WARNING'); + expect(description['frameTime.smooth']).not.toContain('WARNING'); + }); - async.done(); - }); - })); + it('should describe itself if frame capture is requested and not available', () => { + const description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), { + captureFrames: true + }).describe(); + expect(description['frameTime.mean']).toContain('WARNING'); + expect(description['frameTime.best']).toContain('WARNING'); + expect(description['frameTime.worst']).toContain('WARNING'); + expect(description['frameTime.smooth']).toContain('WARNING'); + }); - it('should force gc and mark the timeline', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric([[]], null !, {forceGc: true}); - metric.beginMeasure().then((_) => { - expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]); + describe('beginMeasure', () => { + it('should not force gc and mark the timeline', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const metric = createMetric([[]], null!); + metric.beginMeasure().then((_) => { + expect(commandLog).toEqual([['timeBegin', 'benchpress0']]); + + async.done(); + }); + })); + + it('should force gc and mark the timeline', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const metric = createMetric([[]], null!, {forceGc: true}); + metric.beginMeasure().then((_) => { + expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]); + + async.done(); + }); + })); + }); - async.done(); - }); - })); + describe('endMeasure', () => { + it('should mark and aggregate events in between the marks', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const events = [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), + eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10) + ]]; + const metric = createMetric(events, null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(commandLog).toEqual([ + ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog' + ]); + expect(data['scriptTime']).toBe(2); + + async.done(); + }); + })); + + it('should mark and aggregate events since navigationStart', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const events = [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), + eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7), + eventFactory.start('script', 8), eventFactory.end('script', 9), + eventFactory.markEnd('benchpress0', 10) + ]]; + const metric = createMetric(events, null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(data['scriptTime']).toBe(1); + + async.done(); + }); + })); + + it('should ignore navigationStart if ignoreNavigation is set', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const events = [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), + eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7), + eventFactory.start('script', 8), eventFactory.end('script', 9), + eventFactory.markEnd('benchpress0', 10) + ]]; + const metric = createMetric(events, null!, {ignoreNavigation: true}); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(data['scriptTime']).toBe(3); + + async.done(); + }); + })); + + it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const events = [ + [ + eventFactory.markStart('benchpress0', 0), + eventFactory.markEnd('benchpress0', 1), + eventFactory.markStart('benchpress1', 2), + ], + [eventFactory.markEnd('benchpress1', 3)] + ]; + const metric = createMetric(events, null!); + metric.beginMeasure() + .then((_) => metric.endMeasure(true)) + .then((_) => metric.endMeasure(true)) + .then((_) => { + expect(commandLog).toEqual([ + ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], + 'readPerfLog', ['timeEnd', 'benchpress1', 'benchpress2'], 'readPerfLog' + ]); - }); + async.done(); + }); + })); + + it('should loop and aggregate until the end mark is present', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const events = [ + [eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)], + [eventFactory.end('script', 2)], + [ + eventFactory.start('script', 3), eventFactory.end('script', 5), + eventFactory.markEnd('benchpress0', 10) + ] + ]; + const metric = createMetric(events, null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(commandLog).toEqual([ + ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog', + ['setTimeout', 100], 'readPerfLog', ['setTimeout', 100], 'readPerfLog' + ]); + expect(data['scriptTime']).toBe(3); + + async.done(); + }); + })); + + it('should store events after the end mark for the next call', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const events = [ + [ + eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1), + eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1), + eventFactory.end('script', 2) + ], + [ + eventFactory.start('script', 3), eventFactory.end('script', 5), + eventFactory.markEnd('benchpress1', 6) + ] + ]; + const metric = createMetric(events, null!); + metric.beginMeasure() + .then((_) => metric.endMeasure(true)) + .then((data) => { + expect(data['scriptTime']).toBe(0); + return metric.endMeasure(true); + }) + .then((data) => { + expect(commandLog).toEqual([ + ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], + 'readPerfLog', ['timeEnd', 'benchpress1', 'benchpress2'], 'readPerfLog' + ]); + expect(data['scriptTime']).toBe(3); - describe('endMeasure', () => { + async.done(); + }); + })); + + describe('with forced gc', () => { + let events: PerfLogEvent[][]; + beforeEach(() => { + events = [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), + eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10), + eventFactory.markStart('benchpress1', 11), + eventFactory.start('gc', 12, {'usedHeapSize': 2500}), + eventFactory.end('gc', 15, {'usedHeapSize': 1000}), + eventFactory.markEnd('benchpress1', 20) + ]]; + }); - it('should mark and aggregate events in between the marks', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), - eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10) - ]]; - const metric = createMetric(events, null !); + it('should measure forced gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const metric = createMetric(events, null!, {forceGc: true}); metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { expect(commandLog).toEqual([ - ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog' + ['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], + 'readPerfLog', ['gc'], ['timeEnd', 'benchpress1', null], 'readPerfLog' ]); - expect(data['scriptTime']).toBe(2); + expect(data['forcedGcTime']).toBe(3); + expect(data['forcedGcAmount']).toBe(1.5); async.done(); }); })); - it('should mark and aggregate events since navigationStart', + it('should restart after the forced gc if needed', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), - eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7), - eventFactory.start('script', 8), eventFactory.end('script', 9), - eventFactory.markEnd('benchpress0', 10) - ]]; - const metric = createMetric(events, null !); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(data['scriptTime']).toBe(1); - - async.done(); - }); - })); - - it('should ignore navigationStart if ignoreNavigation is set', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), - eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7), - eventFactory.start('script', 8), eventFactory.end('script', 9), - eventFactory.markEnd('benchpress0', 10) - ]]; - const metric = createMetric(events, null !, {ignoreNavigation: true}); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(data['scriptTime']).toBe(3); + const metric = createMetric(events, null!, {forceGc: true}); + metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => { + expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']); async.done(); }); })); + }); + }); - it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [ - [ - eventFactory.markStart('benchpress0', 0), - eventFactory.markEnd('benchpress0', 1), - eventFactory.markStart('benchpress1', 2), - ], - [eventFactory.markEnd('benchpress1', 3)] - ]; - const metric = createMetric(events, null !); - metric.beginMeasure() - .then((_) => metric.endMeasure(true)) - .then((_) => metric.endMeasure(true)) - .then((_) => { - expect(commandLog).toEqual([ - ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], - 'readPerfLog', ['timeEnd', 'benchpress1', 'benchpress2'], 'readPerfLog' - ]); + describe('aggregation', () => { + function aggregate(events: any[], {microMetrics, captureFrames, receivedData, requestCount}: { + microMetrics?: {[key: string]: string}, + captureFrames?: boolean, + receivedData?: boolean, + requestCount?: boolean + } = {}) { + events.unshift(eventFactory.markStart('benchpress0', 0)); + events.push(eventFactory.markEnd('benchpress0', 10)); + const metric = createMetric([events], null!, { + microMetrics: microMetrics, + captureFrames: captureFrames, + receivedData: receivedData, + requestCount: requestCount + }); + return metric.beginMeasure().then((_) => metric.endMeasure(false)); + } + describe('frame metrics', () => { + it('should calculate mean frame time', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate( + [ + eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), + eventFactory.instant('frame', 3), eventFactory.instant('frame', 4), + eventFactory.markEnd('frameCapture', 5) + ], + {captureFrames: true}) + .then((data) => { + expect(data['frameTime.mean']).toBe(((3 - 1) + (4 - 3)) / 2); async.done(); }); })); - it('should loop and aggregate until the end mark is present', + it('should throw if no start event', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [ - [eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)], - [eventFactory.end('script', 2)], - [ - eventFactory.start('script', 3), eventFactory.end('script', 5), - eventFactory.markEnd('benchpress0', 10) - ] - ]; - const metric = createMetric(events, null !); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(commandLog).toEqual([ - ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog', - ['setTimeout', 100], 'readPerfLog', ['setTimeout', 100], 'readPerfLog' - ]); - expect(data['scriptTime']).toBe(3); - + aggregate([eventFactory.instant('frame', 4), eventFactory.markEnd('frameCapture', 5)], { + captureFrames: true + }).catch((err): any => { + expect(() => { + throw err; + }).toThrowError('missing start event for frame capture'); async.done(); }); })); - it('should store events after the end mark for the next call', + it('should throw if no end event', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [ - [ - eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1), - eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1), - eventFactory.end('script', 2) - ], - [ - eventFactory.start('script', 3), eventFactory.end('script', 5), - eventFactory.markEnd('benchpress1', 6) - ] - ]; - const metric = createMetric(events, null !); - metric.beginMeasure() - .then((_) => metric.endMeasure(true)) - .then((data) => { - expect(data['scriptTime']).toBe(0); - return metric.endMeasure(true); - }) - .then((data) => { - expect(commandLog).toEqual([ - ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], - 'readPerfLog', ['timeEnd', 'benchpress1', 'benchpress2'], 'readPerfLog' - ]); - expect(data['scriptTime']).toBe(3); - + aggregate( + [eventFactory.markStart('frameCapture', 3), eventFactory.instant('frame', 4)], + {captureFrames: true}) + .catch((err): any => { + expect(() => { + throw err; + }).toThrowError('missing end event for frame capture'); async.done(); }); })); - describe('with forced gc', () => { - let events: PerfLogEvent[][]; - beforeEach(() => { - events = [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), - eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10), - eventFactory.markStart('benchpress1', 11), - eventFactory.start('gc', 12, {'usedHeapSize': 2500}), - eventFactory.end('gc', 15, {'usedHeapSize': 1000}), - eventFactory.markEnd('benchpress1', 20) - ]]; - }); - - it('should measure forced gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric(events, null !, {forceGc: true}); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(commandLog).toEqual([ - ['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], - 'readPerfLog', ['gc'], ['timeEnd', 'benchpress1', null], 'readPerfLog' - ]); - expect(data['forcedGcTime']).toBe(3); - expect(data['forcedGcAmount']).toBe(1.5); - - async.done(); - }); - })); - - it('should restart after the forced gc if needed', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric(events, null !, {forceGc: true}); - metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => { - expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']); - - async.done(); - }); - })); - - }); - - }); - - describe('aggregation', () => { - - function aggregate(events: any[], {microMetrics, captureFrames, receivedData, requestCount}: { - microMetrics?: {[key: string]: string}, - captureFrames?: boolean, - receivedData?: boolean, - requestCount?: boolean - } = {}) { - events.unshift(eventFactory.markStart('benchpress0', 0)); - events.push(eventFactory.markEnd('benchpress0', 10)); - const metric = createMetric([events], null !, { - microMetrics: microMetrics, - captureFrames: captureFrames, - receivedData: receivedData, - requestCount: requestCount - }); - return metric.beginMeasure().then((_) => metric.endMeasure(false)); - } - - describe('frame metrics', () => { - it('should calculate mean frame time', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), - eventFactory.instant('frame', 3), eventFactory.instant('frame', 4), - eventFactory.markEnd('frameCapture', 5) - ], - {captureFrames: true}) - .then((data) => { - expect(data['frameTime.mean']).toBe(((3 - 1) + (4 - 3)) / 2); - async.done(); - }); - })); - - it('should throw if no start event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - - aggregate( - [eventFactory.instant('frame', 4), eventFactory.markEnd('frameCapture', 5)], - {captureFrames: true}) - .catch((err): any => { - expect(() => { - throw err; - }).toThrowError('missing start event for frame capture'); - async.done(); - }); - })); - - it('should throw if no end event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - - aggregate( - [eventFactory.markStart('frameCapture', 3), eventFactory.instant('frame', 4)], - {captureFrames: true}) - .catch((err): any => { - expect(() => { throw err; }).toThrowError('missing end event for frame capture'); - async.done(); - }); - })); - - it('should throw if trying to capture twice', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - - aggregate( - [ - eventFactory.markStart('frameCapture', 3), - eventFactory.markStart('frameCapture', 4) - ], - {captureFrames: true}) - .catch((err): any => { - expect(() => { - throw err; - }).toThrowError('can capture frames only once per benchmark run'); - async.done(); - }); - })); - - it('should throw if trying to capture when frame capture is disabled', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([eventFactory.markStart('frameCapture', 3)]).catch((err) => { - expect(() => { throw err; }) - .toThrowError( - 'found start event for frame capture, but frame capture was not requested in benchpress'); - async.done(); - return null; - }); - })); - - it('should throw if frame capture is enabled, but nothing is captured', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([], {captureFrames: true}).catch((err): any => { - expect(() => { throw err; }) - .toThrowError( - 'frame capture requested in benchpress, but no start event was found'); - async.done(); - }); - })); - - it('should calculate best and worst frame time', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), - eventFactory.instant('frame', 9), eventFactory.instant('frame', 15), - eventFactory.instant('frame', 18), eventFactory.instant('frame', 28), - eventFactory.instant('frame', 32), eventFactory.markEnd('frameCapture', 10) - ], - {captureFrames: true}) - .then((data) => { - expect(data['frameTime.worst']).toBe(10); - expect(data['frameTime.best']).toBe(3); - async.done(); - }); - })); - - it('should calculate percentage of smoothness to be good', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), - eventFactory.instant('frame', 2), eventFactory.instant('frame', 3), - eventFactory.markEnd('frameCapture', 4) - ], - {captureFrames: true}) - .then((data) => { - expect(data['frameTime.smooth']).toBe(1.0); - async.done(); - }); - })); - - it('should calculate percentage of smoothness to be bad', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), - eventFactory.instant('frame', 2), eventFactory.instant('frame', 22), - eventFactory.instant('frame', 23), eventFactory.instant('frame', 24), - eventFactory.markEnd('frameCapture', 4) - ], - {captureFrames: true}) - .then((data) => { - expect(data['frameTime.smooth']).toBe(0.75); - async.done(); - }); - })); - - }); - - it('should report a single interval', + it('should throw if trying to capture twice', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.end('script', 5) - ]).then((data) => { - expect(data['scriptTime']).toBe(5); - async.done(); - }); - })); - - it('should sum up multiple intervals', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.end('script', 5), - eventFactory.start('script', 10), eventFactory.end('script', 17) - ]).then((data) => { - expect(data['scriptTime']).toBe(12); - async.done(); - }); + aggregate( + [ + eventFactory.markStart('frameCapture', 3), + eventFactory.markStart('frameCapture', 4) + ], + {captureFrames: true}) + .catch((err): any => { + expect(() => { + throw err; + }).toThrowError('can capture frames only once per benchmark run'); + async.done(); + }); })); - it('should ignore not started intervals', + it('should throw if trying to capture when frame capture is disabled', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([eventFactory.end('script', 10)]).then((data) => { - expect(data['scriptTime']).toBe(0); + aggregate([eventFactory.markStart('frameCapture', 3)]).catch((err) => { + expect(() => { + throw err; + }) + .toThrowError( + 'found start event for frame capture, but frame capture was not requested in benchpress'); async.done(); + return null; }); })); - it('should ignore not ended intervals', + it('should throw if frame capture is enabled, but nothing is captured', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([eventFactory.start('script', 10)]).then((data) => { - expect(data['scriptTime']).toBe(0); + aggregate([], {captureFrames: true}).catch((err): any => { + expect(() => { + throw err; + }).toThrowError('frame capture requested in benchpress, but no start event was found'); async.done(); }); })); - it('should ignore nested intervals', + it('should calculate best and worst frame time', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.start('script', 5), - eventFactory.end('script', 10), eventFactory.end('script', 17) - ]).then((data) => { - expect(data['scriptTime']).toBe(17); - async.done(); - }); + aggregate( + [ + eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), + eventFactory.instant('frame', 9), eventFactory.instant('frame', 15), + eventFactory.instant('frame', 18), eventFactory.instant('frame', 28), + eventFactory.instant('frame', 32), eventFactory.markEnd('frameCapture', 10) + ], + {captureFrames: true}) + .then((data) => { + expect(data['frameTime.worst']).toBe(10); + expect(data['frameTime.best']).toBe(3); + async.done(); + }); })); - it('should ignore events from different processed as the start mark', + it('should calculate percentage of smoothness to be good', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1'); - const metric = createMetric( - [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null), - eventFactory.end('script', 5, null), - otherProcessEventFactory.start('script', 10, null), - otherProcessEventFactory.end('script', 17, null), - eventFactory.markEnd('benchpress0', 20) - ]], - null !); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(data['scriptTime']).toBe(5); - async.done(); - }); + aggregate( + [ + eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), + eventFactory.instant('frame', 2), eventFactory.instant('frame', 3), + eventFactory.markEnd('frameCapture', 4) + ], + {captureFrames: true}) + .then((data) => { + expect(data['frameTime.smooth']).toBe(1.0); + async.done(); + }); })); - it('should mark a run as invalid if the start and end marks are different', + it('should calculate percentage of smoothness to be bad', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1'); - const metric = createMetric( - [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null), - eventFactory.end('script', 5, null), - otherProcessEventFactory.start('script', 10, null), - otherProcessEventFactory.end('script', 17, null), - otherProcessEventFactory.markEnd('benchpress0', 20) - ]], - null !); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(data['invalid']).toBe(1); - async.done(); - }); + aggregate( + [ + eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), + eventFactory.instant('frame', 2), eventFactory.instant('frame', 22), + eventFactory.instant('frame', 23), eventFactory.instant('frame', 24), + eventFactory.markEnd('frameCapture', 4) + ], + {captureFrames: true}) + .then((data) => { + expect(data['frameTime.smooth']).toBe(0.75); + async.done(); + }); })); + }); - it('should support scriptTime metric', + it('should report a single interval', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([ + eventFactory.start('script', 0), eventFactory.end('script', 5) + ]).then((data) => { + expect(data['scriptTime']).toBe(5); + async.done(); + }); + })); + + it('should sum up multiple intervals', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([ + eventFactory.start('script', 0), eventFactory.end('script', 5), + eventFactory.start('script', 10), eventFactory.end('script', 17) + ]).then((data) => { + expect(data['scriptTime']).toBe(12); + async.done(); + }); + })); + + it('should ignore not started intervals', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([eventFactory.end('script', 10)]).then((data) => { + expect(data['scriptTime']).toBe(0); + async.done(); + }); + })); + + it('should ignore not ended intervals', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([eventFactory.start('script', 10)]).then((data) => { + expect(data['scriptTime']).toBe(0); + async.done(); + }); + })); + + it('should ignore nested intervals', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([ + eventFactory.start('script', 0), eventFactory.start('script', 5), + eventFactory.end('script', 10), eventFactory.end('script', 17) + ]).then((data) => { + expect(data['scriptTime']).toBe(17); + async.done(); + }); + })); + + it('should ignore events from different processed as the start mark', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1'); + const metric = createMetric( + [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null), + eventFactory.end('script', 5, null), + otherProcessEventFactory.start('script', 10, null), + otherProcessEventFactory.end('script', 17, null), + eventFactory.markEnd('benchpress0', 20) + ]], + null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(data['scriptTime']).toBe(5); + async.done(); + }); + })); + + it('should mark a run as invalid if the start and end marks are different', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1'); + const metric = createMetric( + [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null), + eventFactory.end('script', 5, null), + otherProcessEventFactory.start('script', 10, null), + otherProcessEventFactory.end('script', 17, null), + otherProcessEventFactory.markEnd('benchpress0', 20) + ]], + null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(data['invalid']).toBe(1); + async.done(); + }); + })); + + it('should support scriptTime metric', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([ + eventFactory.start('script', 0), eventFactory.end('script', 5) + ]).then((data) => { + expect(data['scriptTime']).toBe(5); + async.done(); + }); + })); + + it('should support renderTime metric', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([ + eventFactory.start('render', 0), eventFactory.end('render', 5) + ]).then((data) => { + expect(data['renderTime']).toBe(5); + async.done(); + }); + })); + + it('should support gcTime/gcAmount metric', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([ + eventFactory.start('gc', 0, {'usedHeapSize': 2500}), + eventFactory.end('gc', 5, {'usedHeapSize': 1000}) + ]).then((data) => { + expect(data['gcTime']).toBe(5); + expect(data['gcAmount']).toBe(1.5); + expect(data['majorGcTime']).toBe(0); + async.done(); + }); + })); + + it('should support majorGcTime metric', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([ + eventFactory.start('gc', 0, {'usedHeapSize': 2500}), + eventFactory.end('gc', 5, {'usedHeapSize': 1000, 'majorGc': true}) + ]).then((data) => { + expect(data['gcTime']).toBe(5); + expect(data['majorGcTime']).toBe(5); + async.done(); + }); + })); + + it('should support pureScriptTime = scriptTime-gcTime-renderTime', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + aggregate([ + eventFactory.start('script', 0), eventFactory.start('gc', 1, {'usedHeapSize': 1000}), + eventFactory.end('gc', 4, {'usedHeapSize': 0}), eventFactory.start('render', 4), + eventFactory.end('render', 5), eventFactory.end('script', 6) + ]).then((data) => { + expect(data['scriptTime']).toBe(6); + expect(data['pureScriptTime']).toBe(2); + async.done(); + }); + })); + + describe('receivedData', () => { + it('should report received data since last navigationStart', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.end('script', 5) - ]).then((data) => { - expect(data['scriptTime']).toBe(5); - async.done(); - }); + aggregate( + [ + eventFactory.instant('receivedData', 0, {'encodedDataLength': 1}), + eventFactory.instant('navigationStart', 1), + eventFactory.instant('receivedData', 2, {'encodedDataLength': 2}), + eventFactory.instant('navigationStart', 3), + eventFactory.instant('receivedData', 4, {'encodedDataLength': 4}), + eventFactory.instant('receivedData', 5, {'encodedDataLength': 8}) + ], + {receivedData: true}) + .then((data) => { + expect(data['receivedData']).toBe(12); + async.done(); + }); })); + }); - it('should support renderTime metric', + describe('requestCount', () => { + it('should report count of requests sent since last navigationStart', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('render', 0), eventFactory.end('render', 5) - ]).then((data) => { - expect(data['renderTime']).toBe(5); - async.done(); - }); + aggregate( + [ + eventFactory.instant('sendRequest', 0), eventFactory.instant('navigationStart', 1), + eventFactory.instant('sendRequest', 2), eventFactory.instant('navigationStart', 3), + eventFactory.instant('sendRequest', 4), eventFactory.instant('sendRequest', 5) + ], + {requestCount: true}) + .then((data) => { + expect(data['requestCount']).toBe(2); + async.done(); + }); })); + }); - it('should support gcTime/gcAmount metric', + describe('microMetrics', () => { + it('should report micro metrics', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('gc', 0, {'usedHeapSize': 2500}), - eventFactory.end('gc', 5, {'usedHeapSize': 1000}) - ]).then((data) => { - expect(data['gcTime']).toBe(5); - expect(data['gcAmount']).toBe(1.5); - expect(data['majorGcTime']).toBe(0); - async.done(); - }); + aggregate( + [ + eventFactory.markStart('mm1', 0), + eventFactory.markEnd('mm1', 5), + ], + {microMetrics: {'mm1': 'micro metric 1'}}) + .then((data) => { + expect(data['mm1']).toBe(5.0); + async.done(); + }); })); - it('should support majorGcTime metric', + it('should ignore micro metrics that were not specified', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { aggregate([ - eventFactory.start('gc', 0, {'usedHeapSize': 2500}), - eventFactory.end('gc', 5, {'usedHeapSize': 1000, 'majorGc': true}) + eventFactory.markStart('mm1', 0), + eventFactory.markEnd('mm1', 5), ]).then((data) => { - expect(data['gcTime']).toBe(5); - expect(data['majorGcTime']).toBe(5); + expect(data['mm1']).toBeFalsy(); async.done(); }); })); - it('should support pureScriptTime = scriptTime-gcTime-renderTime', + it('should report micro metric averages', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.start('gc', 1, {'usedHeapSize': 1000}), - eventFactory.end('gc', 4, {'usedHeapSize': 0}), eventFactory.start('render', 4), - eventFactory.end('render', 5), eventFactory.end('script', 6) - ]).then((data) => { - expect(data['scriptTime']).toBe(6); - expect(data['pureScriptTime']).toBe(2); - async.done(); - }); + aggregate( + [ + eventFactory.markStart('mm1*20', 0), + eventFactory.markEnd('mm1*20', 5), + ], + {microMetrics: {'mm1': 'micro metric 1'}}) + .then((data) => { + expect(data['mm1']).toBe(5 / 20); + async.done(); + }); })); - - describe('receivedData', () => { - it('should report received data since last navigationStart', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.instant('receivedData', 0, {'encodedDataLength': 1}), - eventFactory.instant('navigationStart', 1), - eventFactory.instant('receivedData', 2, {'encodedDataLength': 2}), - eventFactory.instant('navigationStart', 3), - eventFactory.instant('receivedData', 4, {'encodedDataLength': 4}), - eventFactory.instant('receivedData', 5, {'encodedDataLength': 8}) - ], - {receivedData: true}) - .then((data) => { - expect(data['receivedData']).toBe(12); - async.done(); - }); - })); - }); - - describe('requestCount', () => { - it('should report count of requests sent since last navigationStart', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.instant('sendRequest', 0), - eventFactory.instant('navigationStart', 1), - eventFactory.instant('sendRequest', 2), - eventFactory.instant('navigationStart', 3), - eventFactory.instant('sendRequest', 4), eventFactory.instant('sendRequest', 5) - ], - {requestCount: true}) - .then((data) => { - expect(data['requestCount']).toBe(2); - async.done(); - }); - })); - }); - - describe('microMetrics', () => { - - it('should report micro metrics', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('mm1', 0), - eventFactory.markEnd('mm1', 5), - ], - {microMetrics: {'mm1': 'micro metric 1'}}) - .then((data) => { - expect(data['mm1']).toBe(5.0); - async.done(); - }); - })); - - it('should ignore micro metrics that were not specified', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.markStart('mm1', 0), - eventFactory.markEnd('mm1', 5), - ]).then((data) => { - expect(data['mm1']).toBeFalsy(); - async.done(); - }); - })); - - it('should report micro metric averages', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('mm1*20', 0), - eventFactory.markEnd('mm1*20', 5), - ], - {microMetrics: {'mm1': 'micro metric 1'}}) - .then((data) => { - expect(data['mm1']).toBe(5 / 20); - async.done(); - }); - })); - - }); - }); - }); +}); })(); class MockDriverExtension extends WebDriverExtension { @@ -717,7 +703,9 @@ class MockDriverExtension extends WebDriverExtension { return Promise.resolve(null); } - perfLogFeatures(): PerfLogFeatures { return this._perfLogFeatures; } + perfLogFeatures(): PerfLogFeatures { + return this._perfLogFeatures; + } readPerfLog(): Promise<any> { this._commandLog.push('readPerfLog'); diff --git a/packages/benchpress/test/metric/user_metric_spec.ts b/packages/benchpress/test/metric/user_metric_spec.ts index 0225154a585f5..0f551c3184c2e 100644 --- a/packages/benchpress/test/metric/user_metric_spec.ts +++ b/packages/benchpress/test/metric/user_metric_spec.ts @@ -12,55 +12,55 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/te import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index'; (function() { - let wdAdapter: MockDriverAdapter; +let wdAdapter: MockDriverAdapter; - function createMetric( - perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures, - {userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric { - if (!perfLogFeatures) { - perfLogFeatures = - new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); - } - if (!userMetrics) { - userMetrics = {}; - } - wdAdapter = new MockDriverAdapter(); - const providers: StaticProvider[] = [ - Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS, - {provide: Options.USER_METRICS, useValue: userMetrics}, - {provide: WebDriverAdapter, useValue: wdAdapter} - ]; - return Injector.create(providers).get(UserMetric); +function createMetric( + perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures, + {userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric { + if (!perfLogFeatures) { + perfLogFeatures = + new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); } + if (!userMetrics) { + userMetrics = {}; + } + wdAdapter = new MockDriverAdapter(); + const providers: StaticProvider[] = [ + Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS, + {provide: Options.USER_METRICS, useValue: userMetrics}, + {provide: WebDriverAdapter, useValue: wdAdapter} + ]; + return Injector.create(providers).get(UserMetric); +} - describe('user metric', () => { - - it('should describe itself based on userMetrics', () => { - expect(createMetric([[]], new PerfLogFeatures(), { - userMetrics: {'loadTime': 'time to load'} - }).describe()) - .toEqual({'loadTime': 'time to load'}); - }); - - describe('endMeasure', () => { - it('should stop measuring when all properties have numeric values', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric( - [[]], new PerfLogFeatures(), - {userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}}); - metric.beginMeasure().then(() => metric.endMeasure(true)).then(values => { - expect(values['loadTime']).toBe(25); - expect(values['content']).toBe(250); - async.done(); - }); +describe('user metric', () => { + it('should describe itself based on userMetrics', () => { + expect(createMetric([[]], new PerfLogFeatures(), { + userMetrics: {'loadTime': 'time to load'} + }).describe()) + .toEqual({'loadTime': 'time to load'}); + }); - wdAdapter.data['loadTime'] = 25; - // Wait before setting 2nd property. - setTimeout(() => { wdAdapter.data['content'] = 250; }, 50); + describe('endMeasure', () => { + it('should stop measuring when all properties have numeric values', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const metric = createMetric( + [[]], new PerfLogFeatures(), + {userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}}); + metric.beginMeasure().then(() => metric.endMeasure(true)).then(values => { + expect(values['loadTime']).toBe(25); + expect(values['content']).toBe(250); + async.done(); + }); - }), 600); - }); + wdAdapter.data['loadTime'] = 25; + // Wait before setting 2nd property. + setTimeout(() => { + wdAdapter.data['content'] = 250; + }, 50); + }), 600); }); +}); })(); class MockDriverAdapter extends WebDriverAdapter { diff --git a/packages/benchpress/test/reporter/console_reporter_spec.ts b/packages/benchpress/test/reporter/console_reporter_spec.ts index 0181382b11f2d..ac1397a82caa1 100644 --- a/packages/benchpress/test/reporter/console_reporter_spec.ts +++ b/packages/benchpress/test/reporter/console_reporter_spec.ts @@ -18,10 +18,10 @@ import {ConsoleReporter, Injector, MeasureValues, SampleDescription} from '../.. function createReporter( {columnWidth = null, sampleId = null, descriptions = null, metrics = null}: { - columnWidth?: number | null, - sampleId?: string | null, - descriptions?: {[key: string]: any}[] | null, - metrics?: {[key: string]: any} | null + columnWidth?: number|null, + sampleId?: string|null, + descriptions?: {[key: string]: any}[]|null, + metrics?: {[key: string]: any}|null }) { log = []; if (!descriptions) { @@ -33,7 +33,7 @@ import {ConsoleReporter, Injector, MeasureValues, SampleDescription} from '../.. const providers: StaticProvider[] = [ ConsoleReporter.PROVIDERS, { provide: SampleDescription, - useValue: new SampleDescription(sampleId, descriptions, metrics !) + useValue: new SampleDescription(sampleId, descriptions, metrics!) }, {provide: ConsoleReporter.PRINT, useValue: (line: string) => log.push(line)} ]; @@ -84,7 +84,6 @@ import {ConsoleReporter, Injector, MeasureValues, SampleDescription} from '../.. reporter.reportSample([], [mv(0, 0, {'a': 3, 'b': 0}), mv(1, 1, {'a': 5, 'b': 0})]); expect(log).toEqual(['======== | ========', '4.00+-25% | 0.00']); }); - }); } diff --git a/packages/benchpress/test/reporter/json_file_reporter_spec.ts b/packages/benchpress/test/reporter/json_file_reporter_spec.ts index 2ca147f2379d4..3ade0b9a27f1d 100644 --- a/packages/benchpress/test/reporter/json_file_reporter_spec.ts +++ b/packages/benchpress/test/reporter/json_file_reporter_spec.ts @@ -62,16 +62,12 @@ import {Injector, JsonFileReporter, MeasureValues, Options, SampleDescription} f {'timeStamp': '1970-01-01T00:00:00.000Z', 'runIndex': 0, 'values': {'a': 3, 'b': 6}} ], 'validSample': [ - {'timeStamp': '1970-01-01T00:00:00.000Z', 'runIndex': 0, 'values': {'a': 3, 'b': 6}}, { - 'timeStamp': '1970-01-01T00:00:00.001Z', - 'runIndex': 1, - 'values': {'a': 5, 'b': 9} - } + {'timeStamp': '1970-01-01T00:00:00.000Z', 'runIndex': 0, 'values': {'a': 3, 'b': 6}}, + {'timeStamp': '1970-01-01T00:00:00.001Z', 'runIndex': 1, 'values': {'a': 5, 'b': 9}} ] }); async.done(); })); - }); } diff --git a/packages/benchpress/test/reporter/multi_reporter_spec.ts b/packages/benchpress/test/reporter/multi_reporter_spec.ts index 4bfeb5ff82282..96045b5a1f79c 100644 --- a/packages/benchpress/test/reporter/multi_reporter_spec.ts +++ b/packages/benchpress/test/reporter/multi_reporter_spec.ts @@ -11,50 +11,48 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/te import {Injector, MeasureValues, MultiReporter, Reporter} from '../../index'; (function() { - function createReporters(ids: any[]) { - const r = Injector - .create([ - ids.map(id => ({provide: id, useValue: new MockReporter(id)})), - MultiReporter.provideWith(ids) - ]) - .get<MultiReporter>(MultiReporter); - return Promise.resolve(r); - } - - describe('multi reporter', () => { - - it('should reportMeasureValues to all', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const mv = new MeasureValues(0, new Date(), {}); - createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => { - - expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]); - async.done(); - }); - })); - - it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const completeSample = - [new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})]; - const validSample = [completeSample[1]]; +function createReporters(ids: any[]) { + const r = Injector + .create([ + ids.map(id => ({provide: id, useValue: new MockReporter(id)})), + MultiReporter.provideWith(ids) + ]) + .get<MultiReporter>(MultiReporter); + return Promise.resolve(r); +} - createReporters(['m1', 'm2']) - .then((r) => r.reportSample(completeSample, validSample)) - .then((values) => { +describe('multi reporter', () => { + it('should reportMeasureValues to all', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const mv = new MeasureValues(0, new Date(), {}); + createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => { + expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]); + async.done(); + }); + })); - expect(values).toEqual([ - {'id': 'm1', 'completeSample': completeSample, 'validSample': validSample}, - {'id': 'm2', 'completeSample': completeSample, 'validSample': validSample} - ]); - async.done(); - }); - })); + it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const completeSample = + [new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})]; + const validSample = [completeSample[1]]; - }); + createReporters(['m1', 'm2']) + .then((r) => r.reportSample(completeSample, validSample)) + .then((values) => { + expect(values).toEqual([ + {'id': 'm1', 'completeSample': completeSample, 'validSample': validSample}, + {'id': 'm2', 'completeSample': completeSample, 'validSample': validSample} + ]); + async.done(); + }); + })); +}); })(); class MockReporter extends Reporter { - constructor(private _id: string) { super(); } + constructor(private _id: string) { + super(); + } reportMeasureValues(values: MeasureValues): Promise<{[key: string]: any}> { return Promise.resolve({'id': this._id, 'values': values}); diff --git a/packages/benchpress/test/runner_spec.ts b/packages/benchpress/test/runner_spec.ts index 3487b5988eb52..87cd2710c952f 100644 --- a/packages/benchpress/test/runner_spec.ts +++ b/packages/benchpress/test/runner_spec.ts @@ -8,7 +8,7 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; -import {Injector, Metric, Options, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index'; +import {Injector, Metric, Options, Runner, SampleDescription, Sampler, SampleState, Validator, WebDriverAdapter} from '../index'; { describe('runner', () => { @@ -68,7 +68,6 @@ import {Injector, Metric, Options, Runner, SampleDescription, SampleState, Sampl .sample({id: 'someId'}) .then((_) => injector.get(SampleDescription)) .then((desc) => { - expect(desc.metrics).toEqual({'m1': 'some metric'}); async.done(); }); @@ -109,32 +108,45 @@ import {Injector, Metric, Options, Runner, SampleDescription, SampleState, Sampl }) .then((_) => injector.get(SampleDescription)) .then((desc) => { - expect(desc.description['a']).toBe(2); async.done(); }); - })); - }); } class MockWebDriverAdapter extends WebDriverAdapter { - executeScript(script: string): Promise<string> { return Promise.resolve('someUserAgent'); } - capabilities(): Promise<Map<string, any>> { return null !; } + executeScript(script: string): Promise<string> { + return Promise.resolve('someUserAgent'); + } + capabilities(): Promise<Map<string, any>> { + return null!; + } } class MockValidator extends Validator { - constructor() { super(); } - describe() { return {'v': 11}; } + constructor() { + super(); + } + describe() { + return {'v': 11}; + } } class MockMetric extends Metric { - constructor() { super(); } - describe() { return {'m1': 'some metric'}; } + constructor() { + super(); + } + describe() { + return {'m1': 'some metric'}; + } } class MockSampler extends Sampler { - constructor() { super(null !, null !, null !, null !, null !, null !, null !); } - sample(): Promise<SampleState> { return Promise.resolve(new SampleState([], [])); } + constructor() { + super(null!, null!, null!, null!, null!, null!, null!); + } + sample(): Promise<SampleState> { + return Promise.resolve(new SampleState([], [])); + } } diff --git a/packages/benchpress/test/sampler_spec.ts b/packages/benchpress/test/sampler_spec.ts index 9b1b7864e38d6..ec416c8d0f468 100644 --- a/packages/benchpress/test/sampler_spec.ts +++ b/packages/benchpress/test/sampler_spec.ts @@ -67,7 +67,6 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, expect(log).toEqual([0, 1, 2, 3]); async.done(); }); - })); it('should call prepare, beginMeasure, execute, endMeasure for every iteration', @@ -77,8 +76,12 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, createSampler({ metric: createCountingMetric(log), validator: createCountingValidator(2), - prepare: () => { log.push(`p${workCount++}`); }, - execute: () => { log.push(`w${workCount++}`); } + prepare: () => { + log.push(`p${workCount++}`); + }, + execute: () => { + log.push(`w${workCount++}`); + } }); sampler.sample().then((_) => { expect(log).toEqual([ @@ -102,7 +105,9 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, createSampler({ metric: createCountingMetric(log), validator: createCountingValidator(2), - execute: () => { log.push(`w${workCount++}`); }, + execute: () => { + log.push(`w${workCount++}`); + }, prepare: null }); sampler.sample().then((_) => { @@ -130,7 +135,9 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, scriptTime = 0; return result; }), - prepare: () => { scriptTime = 1 * iterationCount; }, + prepare: () => { + scriptTime = 1 * iterationCount; + }, execute: () => { scriptTime = 10 * iterationCount; iterationCount++; @@ -147,7 +154,7 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, it('should call the validator for every execution and store the valid sample', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const log: any[] = []; - const validSample = [mv(null !, null !, {})]; + const validSample = [mv(null!, null!, {})]; createSampler({ metric: createCountingMetric(), @@ -174,7 +181,7 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, it('should report the metric values', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const log: any[] = []; - const validSample = [mv(null !, null !, {})]; + const validSample = [mv(null!, null!, {})]; createSampler({ validator: createCountingValidator(2, validSample), metric: createCountingMetric(), @@ -198,7 +205,6 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, async.done(); }); })); - }); } @@ -223,7 +229,9 @@ function createCountingMetric(log: any[] = []) { } class MockDriverAdapter extends WebDriverAdapter { - constructor(private _log: any[] = [], private _waitFor: Function|null = null) { super(); } + constructor(private _log: any[] = [], private _waitFor: Function|null = null) { + super(); + } waitFor(callback: Function): Promise<any> { if (this._waitFor != null) { return this._waitFor(callback); @@ -235,7 +243,9 @@ class MockDriverAdapter extends WebDriverAdapter { class MockValidator extends Validator { - constructor(private _log: any[] = [], private _validate: Function|null = null) { super(); } + constructor(private _log: any[] = [], private _validate: Function|null = null) { + super(); + } validate(completeSample: MeasureValues[]): MeasureValues[] { const stableSample = this._validate != null ? this._validate(completeSample) : completeSample; this._log.push(['validate', completeSample, stableSample]); @@ -244,7 +254,9 @@ class MockValidator extends Validator { } class MockMetric extends Metric { - constructor(private _log: any[] = [], private _endMeasure: Function|null = null) { super(); } + constructor(private _log: any[] = [], private _endMeasure: Function|null = null) { + super(); + } beginMeasure() { this._log.push(['beginMeasure']); return Promise.resolve(null); @@ -257,7 +269,9 @@ class MockMetric extends Metric { } class MockReporter extends Reporter { - constructor(private _log: any[] = []) { super(); } + constructor(private _log: any[] = []) { + super(); + } reportMeasureValues(values: MeasureValues): Promise<any> { this._log.push(['reportMeasureValues', values]); return Promise.resolve(null); diff --git a/packages/benchpress/test/statistic_spec.ts b/packages/benchpress/test/statistic_spec.ts index efbeaa8766723..9f8a90b5cadc0 100644 --- a/packages/benchpress/test/statistic_spec.ts +++ b/packages/benchpress/test/statistic_spec.ts @@ -11,7 +11,6 @@ import {Statistic} from '../src/statistic'; { describe('statistic', () => { - it('should calculate the mean', () => { expect(Statistic.calculateMean([])).toBeNaN(); expect(Statistic.calculateMean([1, 2, 3])).toBe(2.0); @@ -34,6 +33,5 @@ import {Statistic} from '../src/statistic'; expect(Statistic.calculateRegressionSlope([1], 1, [2], 2)).toBeNaN(); expect(Statistic.calculateRegressionSlope([1, 2], 1.5, [2, 4], 3)).toBe(2.0); }); - }); } diff --git a/packages/benchpress/test/trace_event_factory.ts b/packages/benchpress/test/trace_event_factory.ts index 11ce4c0b53151..e687f2b0a2092 100644 --- a/packages/benchpress/test/trace_event_factory.ts +++ b/packages/benchpress/test/trace_event_factory.ts @@ -20,13 +20,21 @@ export class TraceEventFactory { return res; } - markStart(name: string, time: number) { return this.create('B', name, time); } + markStart(name: string, time: number) { + return this.create('B', name, time); + } - markEnd(name: string, time: number) { return this.create('E', name, time); } + markEnd(name: string, time: number) { + return this.create('E', name, time); + } - start(name: string, time: number, args: any = null) { return this.create('B', name, time, args); } + start(name: string, time: number, args: any = null) { + return this.create('B', name, time, args); + } - end(name: string, time: number, args: any = null) { return this.create('E', name, time, args); } + end(name: string, time: number, args: any = null) { + return this.create('E', name, time, args); + } instant(name: string, time: number, args: any = null) { return this.create('I', name, time, args); diff --git a/packages/benchpress/test/validator/regression_slope_validator_spec.ts b/packages/benchpress/test/validator/regression_slope_validator_spec.ts index 07cc120cc906d..ceafcca4e3b22 100644 --- a/packages/benchpress/test/validator/regression_slope_validator_spec.ts +++ b/packages/benchpress/test/validator/regression_slope_validator_spec.ts @@ -53,7 +53,6 @@ import {Injector, MeasureValues, RegressionSlopeValidator} from '../../index'; expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2)); expect(validator.validate(sample)).toEqual(sample.slice(1, 3)); }); - }); } diff --git a/packages/benchpress/test/validator/size_validator_spec.ts b/packages/benchpress/test/validator/size_validator_spec.ts index 782b36ab1f250..aa809412bfe91 100644 --- a/packages/benchpress/test/validator/size_validator_spec.ts +++ b/packages/benchpress/test/validator/size_validator_spec.ts @@ -39,7 +39,6 @@ import {Injector, MeasureValues, SizeValidator} from '../../index'; expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2)); expect(validator.validate(sample)).toEqual(sample.slice(1, 3)); }); - }); } diff --git a/packages/benchpress/test/web_driver_extension_spec.ts b/packages/benchpress/test/web_driver_extension_spec.ts index 907c464eaad5c..8ec00b4355276 100644 --- a/packages/benchpress/test/web_driver_extension_spec.ts +++ b/packages/benchpress/test/web_driver_extension_spec.ts @@ -11,44 +11,45 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/te import {Injector, Options, WebDriverExtension} from '../index'; (function() { - function createExtension(ids: any[], caps: any) { - return new Promise<any>((res, rej) => { - try { - res(Injector - .create([ - ids.map((id) => ({provide: id, useValue: new MockExtension(id)})), - {provide: Options.CAPABILITIES, useValue: caps}, - WebDriverExtension.provideFirstSupported(ids) - ]) - .get(WebDriverExtension)); - } catch (e) { - rej(e); - } - }); - } - - describe('WebDriverExtension.provideFirstSupported', () => { - - it('should provide the extension that matches the capabilities', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension(['m1', 'm2', 'm3'], {'browser': 'm2'}).then((m) => { - expect(m.id).toEqual('m2'); - async.done(); - }); - })); - - it('should throw if there is no match', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension(['m1'], {'browser': 'm2'}).catch((err) => { - expect(err != null).toBe(true); - async.done(); - }); - })); +function createExtension(ids: any[], caps: any) { + return new Promise<any>((res, rej) => { + try { + res(Injector + .create([ + ids.map((id) => ({provide: id, useValue: new MockExtension(id)})), + {provide: Options.CAPABILITIES, useValue: caps}, + WebDriverExtension.provideFirstSupported(ids) + ]) + .get(WebDriverExtension)); + } catch (e) { + rej(e); + } }); +} + +describe('WebDriverExtension.provideFirstSupported', () => { + it('should provide the extension that matches the capabilities', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + createExtension(['m1', 'm2', 'm3'], {'browser': 'm2'}).then((m) => { + expect(m.id).toEqual('m2'); + async.done(); + }); + })); + + it('should throw if there is no match', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + createExtension(['m1'], {'browser': 'm2'}).catch((err) => { + expect(err != null).toBe(true); + async.done(); + }); + })); +}); })(); class MockExtension extends WebDriverExtension { - constructor(public id: string) { super(); } + constructor(public id: string) { + super(); + } supports(capabilities: {[key: string]: any}): boolean { return capabilities['browser'] === this.id; diff --git a/packages/benchpress/test/webdriver/chrome_driver_extension_spec.ts b/packages/benchpress/test/webdriver/chrome_driver_extension_spec.ts index 988352345cdd2..1e353a05b257e 100644 --- a/packages/benchpress/test/webdriver/chrome_driver_extension_spec.ts +++ b/packages/benchpress/test/webdriver/chrome_driver_extension_spec.ts @@ -32,7 +32,7 @@ import {TraceEventFactory} from '../trace_event_factory'; const normEvents = new TraceEventFactory('timeline', 'pid0'); function createExtension( - perfRecords: any[] | null = null, userAgent: string | null = null, + perfRecords: any[]|null = null, userAgent: string|null = null, messageMethod = 'Tracing.dataCollected'): WebDriverExtension { if (!perfRecords) { perfRecords = []; @@ -97,9 +97,9 @@ import {TraceEventFactory} from '../trace_event_factory'; it('should mark the timeline via performance.mark() with start and end of a test', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { createExtension().timeEnd('name1', 'name2').then((_) => { - expect(log).toEqual([[ - 'executeScript', `performance.mark('name1-bpend');performance.mark('name2-bpstart');` - ]]); + expect(log).toEqual([ + ['executeScript', `performance.mark('name1-bpend');performance.mark('name2-bpstart');`] + ]); async.done(); }); })); @@ -173,7 +173,8 @@ import {TraceEventFactory} from '../trace_event_factory'; [ chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}), chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}), - ], ) + ], + ) .readPerfLog() .then((events) => { expect(events.length).toEqual(2); @@ -192,7 +193,8 @@ import {TraceEventFactory} from '../trace_event_factory'; [ chrome45TimelineEvents.start(recordType, 1234), chrome45TimelineEvents.end(recordType, 2345) - ], ) + ], + ) .readPerfLog() .then((events) => { expect(events).toEqual([ @@ -210,7 +212,8 @@ import {TraceEventFactory} from '../trace_event_factory'; [ chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234), chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345) - ], ) + ], + ) .readPerfLog() .then((events) => { expect(events).toEqual([ @@ -254,8 +257,10 @@ import {TraceEventFactory} from '../trace_event_factory'; })); it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([chrome45TimelineEvents.instant( - 'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})], ) + createExtension( + [chrome45TimelineEvents.instant( + 'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})], + ) .readPerfLog() .then((events) => { expect(events).toEqual( @@ -265,9 +270,11 @@ import {TraceEventFactory} from '../trace_event_factory'; })); it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([chrome45TimelineEvents.instant( - 'ResourceSendRequest', 1234, - {'data': {'url': 'http://here', 'requestMethod': 'GET'}})], ) + createExtension( + [chrome45TimelineEvents.instant( + 'ResourceSendRequest', 1234, + {'data': {'url': 'http://here', 'requestMethod': 'GET'}})], + ) .readPerfLog() .then((events) => { expect(events).toEqual([normEvents.instant( @@ -277,7 +284,6 @@ import {TraceEventFactory} from '../trace_event_factory'; })); describe('readPerfLog (common)', () => { - it('should execute a dummy script before reading them', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { // TODO(tbosch): This seems to be a bug in ChromeDriver: @@ -296,7 +302,8 @@ import {TraceEventFactory} from '../trace_event_factory'; [ chromeTimelineEvents.start(recordType, 1234), chromeTimelineEvents.end(recordType, 2345) - ], ) + ], + ) .readPerfLog() .then((events) => { expect(events).toEqual([ @@ -337,7 +344,6 @@ import {TraceEventFactory} from '../trace_event_factory'; it('should throw when ImplThreadRenderingStats contains more than one frame', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([benchmarkEvents.instant( 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100, {'data': {'frame_count': 2}})]) @@ -349,7 +355,6 @@ import {TraceEventFactory} from '../trace_event_factory'; async.done(); }); })); - }); it('should report begin timestamps', @@ -374,7 +379,6 @@ import {TraceEventFactory} from '../trace_event_factory'; it('should throw an error on buffer overflow', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( [ chromeTimelineEvents.start('FunctionCall', 1234), @@ -394,9 +398,7 @@ import {TraceEventFactory} from '../trace_event_factory'; expect(createExtension().supports({'browserName': 'Chrome'})).toBe(true); }); - }); - }); } @@ -419,7 +421,7 @@ class MockDriverAdapter extends WebDriverAdapter { {'message': {'method': this._messageMethod, 'params': event}}, null, 2) }))); } else { - return null !; + return null!; } } } diff --git a/packages/benchpress/test/webdriver/ios_driver_extension_spec.ts b/packages/benchpress/test/webdriver/ios_driver_extension_spec.ts index f2edbc774015f..6e7e6de0289f1 100644 --- a/packages/benchpress/test/webdriver/ios_driver_extension_spec.ts +++ b/packages/benchpress/test/webdriver/ios_driver_extension_spec.ts @@ -8,7 +8,7 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; -import {IOsDriverExtension, Injector, WebDriverAdapter, WebDriverExtension} from '../../index'; +import {Injector, IOsDriverExtension, WebDriverAdapter, WebDriverExtension} from '../../index'; import {TraceEventFactory} from '../trace_event_factory'; { @@ -18,7 +18,7 @@ import {TraceEventFactory} from '../trace_event_factory'; const normEvents = new TraceEventFactory('timeline', 'pid0'); - function createExtension(perfRecords: any[] | null = null): WebDriverExtension { + function createExtension(perfRecords: any[]|null = null): WebDriverExtension { if (!perfRecords) { perfRecords = []; } @@ -63,7 +63,6 @@ import {TraceEventFactory} from '../trace_event_factory'; })); describe('readPerfLog', () => { - it('should execute a dummy script before reading them', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { // TODO(tbosch): This seems to be a bug in ChromeDriver: @@ -140,9 +139,7 @@ import {TraceEventFactory} from '../trace_event_factory'; expect(createExtension().supports({'browserName': 'Safari'})).toBe(true); }); - }); - }); } @@ -155,7 +152,7 @@ function timeEndRecord(name: string, time: number) { } function durationRecord( - type: string, startTime: number, endTime: number, children: any[] | null = null) { + type: string, startTime: number, endTime: number, children: any[]|null = null) { if (!children) { children = []; } @@ -172,7 +169,9 @@ function internalScriptRecord(startTime: number, endTime: number) { } class MockDriverAdapter extends WebDriverAdapter { - constructor(private _log: any[], private _perfRecords: any[]) { super(); } + constructor(private _log: any[], private _perfRecords: any[]) { + super(); + } executeScript(script: string) { this._log.push(['executeScript', script]); @@ -190,7 +189,7 @@ class MockDriverAdapter extends WebDriverAdapter { }; })); } else { - return null !; + return null!; } } } diff --git a/packages/circular-deps-test.conf.js b/packages/circular-deps-test.conf.js new file mode 100644 index 0000000000000..83f9a1d5f4872 --- /dev/null +++ b/packages/circular-deps-test.conf.js @@ -0,0 +1,31 @@ +/** + * @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 + */ + +const path = require('path'); + +module.exports = { + baseDir: '../', + goldenFile: '../goldens/packages-circular-deps.json', + // The test should not capture deprecated packages such as `http`, or the `webworker` platform. + glob: `./!(http|platform-webworker|platform-webworker-dynamic)/**/*.ts`, + // Command that will be displayed if the golden needs to be updated. + approveCommand: 'yarn ts-circular-deps:approve', + resolveModule: resolveModule +}; + +/** + * Custom module resolver that maps specifiers starting with `@angular/` to the + * local packages folder. This ensures that cross package/entry-point dependencies + * can be detected. + */ +function resolveModule(specifier) { + if (specifier.startsWith('@angular/')) { + return path.join(__dirname, specifier.substr('@angular/'.length)); + } + return null; +} diff --git a/packages/common/http/src/client.ts b/packages/common/http/src/client.ts index 275fc69a77508..6814034f8b46e 100644 --- a/packages/common/http/src/client.ts +++ b/packages/common/http/src/client.ts @@ -7,7 +7,7 @@ */ import {Injectable} from '@angular/core'; -import {Observable, of } from 'rxjs'; +import {Observable, of} from 'rxjs'; import {concatMap, filter, map} from 'rxjs/operators'; import {HttpHandler} from './backend'; @@ -29,14 +29,14 @@ import {HttpEvent, HttpResponse} from './response'; */ function addBody<T>( options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: HttpObserve, - params?: HttpParams | {[param: string]: string | string[]}, + params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, - responseType?: 'arraybuffer' | 'blob' | 'json' | 'text', + responseType?: 'arraybuffer'|'blob'|'json'|'text', withCredentials?: boolean, }, - body: T | null): any { + body: T|null): any { return { body, headers: options.headers, @@ -48,7 +48,7 @@ function addBody<T>( }; } -export type HttpObserve = 'body' | 'events' | 'response'; +export type HttpObserve = 'body'|'events'|'response'; /** * Performs HTTP requests. @@ -108,8 +108,8 @@ export class HttpClient { request<R>(req: HttpRequest<any>): Observable<HttpEvent<R>>; /** - * Constructs a request that interprets the body as an `ArrayBuffer` and returns the response in an - * `ArrayBuffer`. + * Constructs a request that interprets the body as an `ArrayBuffer` and returns the response in + * an `ArrayBuffer`. * * @param method The HTTP method. * @param url The endpoint URL. @@ -123,8 +123,8 @@ export class HttpClient { headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<ArrayBuffer>; /** @@ -142,8 +142,8 @@ export class HttpClient { headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<Blob>; /** @@ -161,8 +161,8 @@ export class HttpClient { headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<string>; /** @@ -173,15 +173,16 @@ export class HttpClient { * @param url The endpoint URL. * @param options The HTTP options to send with the request. * - * @return An `Observable` of the response, with the response body as an array of `HTTPEvents` for the + * @return An `Observable` of the response, with the response body as an array of `HTTPEvents` for + * the * request. */ request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, - params?: HttpParams|{[param: string]: string | string[]}, - observe: 'events', reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + params?: HttpParams|{[param: string]: string | string[]}, observe: 'events', + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpEvent<ArrayBuffer>>; /** @@ -197,15 +198,15 @@ export class HttpClient { */ request(method: string, url: string, options: { body?: any, - headers?: HttpHeaders|{[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpEvent<Blob>>; /** - * Constructs a request which interprets the body as a text string and returns the full event stream. + * Constructs a request which interprets the body as a text string and returns the full event + * stream. * * @param method The HTTP method. * @param url The endpoint URL. @@ -216,15 +217,15 @@ export class HttpClient { */ request(method: string, url: string, options: { body?: any, - headers?: HttpHeaders|{[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpEvent<string>>; /** - * Constructs a request which interprets the body as a JSON object and returns the full event stream. + * Constructs a request which interprets the body as a JSON object and returns the full event + * stream. * * @param method The HTTP method. * @param url The endpoint URL. @@ -236,15 +237,15 @@ export class HttpClient { request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, - reportProgress?: boolean, - observe: 'events', + reportProgress?: boolean, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, responseType?: 'json', withCredentials?: boolean, }): Observable<HttpEvent<any>>; /** - * Constructs a request which interprets the body as a JSON object and returns the full event stream. + * Constructs a request which interprets the body as a JSON object and returns the full event + * stream. * * @param method The HTTP method. * @param url The endpoint URL. @@ -256,8 +257,7 @@ export class HttpClient { request<R>(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, - reportProgress?: boolean, - observe: 'events', + reportProgress?: boolean, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, responseType?: 'json', withCredentials?: boolean, @@ -275,11 +275,10 @@ export class HttpClient { */ request(method: string, url: string, options: { body?: any, - headers?: HttpHeaders|{[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpResponse<ArrayBuffer>>; /** @@ -293,15 +292,15 @@ export class HttpClient { */ request(method: string, url: string, options: { body?: any, - headers?: HttpHeaders|{[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpResponse<Blob>>; /** - * Constructs a request which interprets the body as a text stream and returns the full `HTTPResponse`. + * Constructs a request which interprets the body as a text stream and returns the full + * `HTTPResponse`. * * @param method The HTTP method. * @param url The endpoint URL. @@ -311,15 +310,15 @@ export class HttpClient { */ request(method: string, url: string, options: { body?: any, - headers?: HttpHeaders|{[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpResponse<string>>; /** - * Constructs a request which interprets the body as a JSON object and returns the full `HTTPResponse`. + * Constructs a request which interprets the body as a JSON object and returns the full + * `HTTPResponse`. * * @param method The HTTP method. * @param url The endpoint URL. @@ -331,8 +330,7 @@ export class HttpClient { request(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, - reportProgress?: boolean, - observe: 'response', + reportProgress?: boolean, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, responseType?: 'json', withCredentials?: boolean, @@ -351,8 +349,7 @@ export class HttpClient { request<R>(method: string, url: string, options: { body?: any, headers?: HttpHeaders|{[header: string]: string | string[]}, - reportProgress?: boolean, - observe: 'response', + reportProgress?: boolean, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, responseType?: 'json', withCredentials?: boolean, @@ -477,12 +474,12 @@ export class HttpClient { if (options.params instanceof HttpParams) { params = options.params; } else { - params = new HttpParams({ fromObject: options.params } as HttpParamsOptions); + params = new HttpParams({fromObject: options.params} as HttpParamsOptions); } } // Construct the request. - req = new HttpRequest(first, url !, (options.body !== undefined ? options.body : null), { + req = new HttpRequest(first, url!, (options.body !== undefined ? options.body : null), { headers, params, reportProgress: options.reportProgress, @@ -497,7 +494,7 @@ export class HttpClient { // inside an Observable chain, which causes interceptors to be re-run on every // subscription (this also makes retries re-run the handler, including interceptors). const events$: Observable<HttpEvent<any>> = - of (req).pipe(concatMap((req: HttpRequest<any>) => this.handler.handle(req))); + of(req).pipe(concatMap((req: HttpRequest<any>) => this.handler.handle(req))); // If coming via the API signature which accepts a previously constructed HttpRequest, // the only option is to get the event stream. Otherwise, return the event stream if @@ -568,12 +565,12 @@ export class HttpClient { * * @return An `Observable` of the response body as an `ArrayBuffer`. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<ArrayBuffer>; @@ -586,12 +583,12 @@ export class HttpClient { * * @return An `Observable` of the response body as a `Blob`. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<Blob>; /** @@ -603,12 +600,12 @@ export class HttpClient { * * @return An `Observable` of the response, with the response body of type string. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<string>; /** @@ -621,12 +618,11 @@ export class HttpClient { * @return An `Observable` of all `HTTPEvents` for the request, * with response body as an `ArrayBuffer`. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpEvent<ArrayBuffer>>; /** @@ -639,12 +635,11 @@ export class HttpClient { * @return An `Observable` of all the `HTTPEvents` for the request, with the response body as a * `Blob`. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpEvent<Blob>>; /** @@ -657,12 +652,11 @@ export class HttpClient { * @return An `Observable` of all `HTTPEvents` for the request, with the response * body of type string. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpEvent<string>>; /** @@ -675,9 +669,8 @@ export class HttpClient { * @return An `Observable` of all `HTTPEvents` for the request, with response body of * type `Object`. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -695,8 +688,7 @@ export class HttpClient { * body in the requested type. */ delete<T>(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -712,12 +704,11 @@ export class HttpClient { * * @return An `Observable` of the full `HTTPResponse`, with the response body as an `ArrayBuffer`. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpResponse<ArrayBuffer>>; /** @@ -729,12 +720,11 @@ export class HttpClient { * * @return An `Observable` of the `HTTPResponse`, with the response body of type `Blob`. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpResponse<Blob>>; /** @@ -746,12 +736,11 @@ export class HttpClient { * * @return An `Observable` of the full `HTTPResponse`, with the response body of type string. */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpResponse<string>>; /** @@ -764,9 +753,8 @@ export class HttpClient { * @return An `Observable` of the `HTTPResponse`, with the response body of type `Object`. * */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -783,8 +771,7 @@ export class HttpClient { * @return An `Observable` of the `HTTPResponse`, with the response body of the requested type. */ delete<T>(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -800,8 +787,8 @@ export class HttpClient { * * @return An `Observable` of the response, with the response body of type `Object`. */ - delete (url: string, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + delete(url: string, options?: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -819,7 +806,7 @@ export class HttpClient { * @return An `Observable` of the `HTTPResponse`, with response body in the requested type. */ delete<T>(url: string, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -836,8 +823,8 @@ export class HttpClient { * @param options The HTTP options to send with the request. * */ - delete (url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + delete(url: string, options: { + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: HttpObserve, params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -849,8 +836,8 @@ export class HttpClient { /** - * Constructs a `GET` request that interprets the body as an `ArrayBuffer` and returns the response in - * an `ArrayBuffer`. + * Constructs a `GET` request that interprets the body as an `ArrayBuffer` and returns the + * response in an `ArrayBuffer`. * * @param url The endpoint URL. * @param options The HTTP options to send with the request. @@ -858,11 +845,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<ArrayBuffer>; /** @@ -875,11 +862,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a `Blob`. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<Blob>; /** @@ -892,11 +879,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body of type string. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<string>; /** @@ -910,11 +897,10 @@ export class HttpClient { * body as an `ArrayBuffer`. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpEvent<ArrayBuffer>>; /** @@ -927,11 +913,10 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a `Blob`. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpEvent<Blob>>; /** @@ -944,11 +929,10 @@ export class HttpClient { * @return An `Observable` of the response, with the response body of type string. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpEvent<string>>; /** @@ -961,8 +945,7 @@ export class HttpClient { * @return An `Observable` of the response, with the response body of type `Object`. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -970,7 +953,8 @@ export class HttpClient { }): Observable<HttpEvent<Object>>; /** - * Constructs a `GET` request that interprets the body as a JSON object and returns the full event stream. + * Constructs a `GET` request that interprets the body as a JSON object and returns the full event + * stream. * * @param url The endpoint URL. * @param options The HTTP options to send with the request. @@ -978,8 +962,7 @@ export class HttpClient { * @return An `Observable` of the response, with a response body in the requested type. */ get<T>(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -997,11 +980,10 @@ export class HttpClient { * with the response body as an `ArrayBuffer`. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpResponse<ArrayBuffer>>; /** @@ -1015,11 +997,10 @@ export class HttpClient { * with the response body as a `Blob`. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpResponse<Blob>>; /** @@ -1033,11 +1014,10 @@ export class HttpClient { * with the response body of type string. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpResponse<string>>; /** @@ -1051,8 +1031,7 @@ export class HttpClient { * with the response body of type `Object`. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1070,8 +1049,7 @@ export class HttpClient { * with a response body in the requested type. */ get<T>(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1089,7 +1067,7 @@ export class HttpClient { * @return An `Observable` of the response body as a JSON object. */ get(url: string, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1107,7 +1085,7 @@ export class HttpClient { * @return An `Observable` of the `HTTPResponse`, with a response body in the requested type. */ get<T>(url: string, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1121,7 +1099,7 @@ export class HttpClient { * details on the return type. */ get(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: HttpObserve, params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1142,11 +1120,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<ArrayBuffer>; /** @@ -1160,11 +1138,11 @@ export class HttpClient { */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<Blob>; /** @@ -1177,11 +1155,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body of type string. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<string>; /** @@ -1195,11 +1173,10 @@ export class HttpClient { * with the response body as an `ArrayBuffer`. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpEvent<ArrayBuffer>>; /** @@ -1213,11 +1190,10 @@ export class HttpClient { * with the response body as a `Blob`. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpEvent<Blob>>; /** @@ -1227,14 +1203,14 @@ export class HttpClient { * @param url The endpoint URL. * @param options The HTTP options to send with the request. * - * @return An `Observable` of all HttpEvents for the request, with the response body of type string. + * @return An `Observable` of all HttpEvents for the request, with the response body of type + * string. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpEvent<string>>; /** @@ -1248,8 +1224,7 @@ export class HttpClient { * type `Object`. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1267,8 +1242,7 @@ export class HttpClient { * @param options The HTTP options to send with the request. */ head<T>(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1286,11 +1260,10 @@ export class HttpClient { * with the response body as an `ArrayBuffer`. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpResponse<ArrayBuffer>>; /** @@ -1304,11 +1277,10 @@ export class HttpClient { * with the response body as a blob. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpResponse<Blob>>; /** @@ -1322,11 +1294,10 @@ export class HttpClient { * with the response body of type string. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpResponse<string>>; /** @@ -1340,8 +1311,7 @@ export class HttpClient { * with the response body of type `Object`. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1359,8 +1329,7 @@ export class HttpClient { * with a responmse body of the requested type. */ head<T>(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1377,7 +1346,7 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a JSON object. */ head(url: string, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1396,7 +1365,7 @@ export class HttpClient { * with a response body of the given type. */ head<T>(url: string, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1412,7 +1381,7 @@ export class HttpClient { * details on the return type. */ head(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: HttpObserve, params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1482,11 +1451,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<ArrayBuffer>; /** @@ -1499,11 +1468,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a `Blob`. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<Blob>; /** @@ -1516,11 +1485,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body of type string. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<string>; /** @@ -1534,11 +1503,10 @@ export class HttpClient { * with the response body as an `ArrayBuffer`. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpEvent<ArrayBuffer>>; /** @@ -1552,11 +1520,10 @@ export class HttpClient { * with the response body as a `Blob`. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpEvent<Blob>>; /** @@ -1570,11 +1537,10 @@ export class HttpClient { * with the response body of type string. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpEvent<string>>; /** @@ -1588,8 +1554,7 @@ export class HttpClient { * body of type `Object`. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1607,8 +1572,7 @@ export class HttpClient { * with a response body in the requested type. */ options<T>(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1626,11 +1590,10 @@ export class HttpClient { * with the response body as an `ArrayBuffer`. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpResponse<ArrayBuffer>>; /** @@ -1644,11 +1607,10 @@ export class HttpClient { * with the response body as a `Blob`. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpResponse<Blob>>; /** @@ -1662,11 +1624,10 @@ export class HttpClient { * with the response body of type string. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpResponse<string>>; /** @@ -1680,8 +1641,7 @@ export class HttpClient { * with the response body of type `Object`. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1699,8 +1659,7 @@ export class HttpClient { * with a response body in the requested type. */ options<T>(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1708,8 +1667,8 @@ export class HttpClient { }): Observable<HttpResponse<T>>; /** - * Constructs an `OPTIONS` request that interprets the body as a JSON object and returns the response - * body as a JSON object. + * Constructs an `OPTIONS` request that interprets the body as a JSON object and returns the + * response body as a JSON object. * * @param url The endpoint URL. * @param options HTTP options. @@ -1717,7 +1676,7 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a JSON object. */ options(url: string, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1726,8 +1685,8 @@ export class HttpClient { }): Observable<Object>; /** - * Constructs an `OPTIONS` request that interprets the body as a JSON object and returns the response - * in a given type. + * Constructs an `OPTIONS` request that interprets the body as a JSON object and returns the + * response in a given type. * * @param url The endpoint URL. * @param options HTTP options. @@ -1735,7 +1694,7 @@ export class HttpClient { * @return An `Observable` of the `HTTPResponse`, with a response body of the given type. */ options<T>(url: string, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1751,7 +1710,7 @@ export class HttpClient { * details on the return type. */ options(url: string, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: HttpObserve, params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -1772,11 +1731,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<ArrayBuffer>; /** @@ -1790,11 +1749,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a `Blob`. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<Blob>; /** @@ -1808,11 +1767,11 @@ export class HttpClient { * @return An `Observable` of the response, with a response body of type string. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<string>; /** @@ -1828,11 +1787,10 @@ export class HttpClient { */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpEvent<ArrayBuffer>>; /** @@ -1847,11 +1805,10 @@ export class HttpClient { * response body as `Blob`. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpEvent<Blob>>; /** @@ -1866,11 +1823,10 @@ export class HttpClient { * response body of type string. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpEvent<string>>; /** @@ -1885,8 +1841,7 @@ export class HttpClient { * with a response body of type `Object`. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1905,8 +1860,7 @@ export class HttpClient { * with a response body in the requested type. */ patch<T>(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -1925,11 +1879,10 @@ export class HttpClient { * with the response body as an `ArrayBuffer`. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpResponse<ArrayBuffer>>; /** @@ -1944,11 +1897,10 @@ export class HttpClient { * with the response body as a `Blob`. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpResponse<Blob>>; /** @@ -1963,11 +1915,10 @@ export class HttpClient { * with a response body of type string. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpResponse<string>>; /** @@ -1982,8 +1933,7 @@ export class HttpClient { * with a response body in the requested type. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2002,8 +1952,7 @@ export class HttpClient { * with a response body in the given type. */ patch<T>(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2021,7 +1970,7 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a JSON object. */ patch(url: string, body: any|null, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -2041,7 +1990,7 @@ export class HttpClient { * with a response body in the given type. */ patch<T>(url: string, body: any|null, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -2055,7 +2004,7 @@ export class HttpClient { * details on the return type. */ patch(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: HttpObserve, params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -2076,11 +2025,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<ArrayBuffer>; /** @@ -2094,11 +2043,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a `Blob`. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<Blob>; /** @@ -2112,11 +2061,11 @@ export class HttpClient { * @return An `Observable` of the response, with a response body of type string. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<string>; /** @@ -2131,11 +2080,10 @@ export class HttpClient { * with the response body as an `ArrayBuffer`. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpEvent<ArrayBuffer>>; /** @@ -2149,15 +2097,15 @@ export class HttpClient { * @return An `Observable` of all `HttpEvents` for the request, with the response body as `Blob`. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpEvent<Blob>>; /** - * Constructs a `POST` request that interprets the body as a text string and returns the full event stream. + * Constructs a `POST` request that interprets the body as a text string and returns the full + * event stream. * * @param url The endpoint URL. * @param body The content to replace with. @@ -2167,15 +2115,15 @@ export class HttpClient { * with a response body of type string. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpEvent<string>>; /** - * Constructs a POST request that interprets the body as a JSON object and returns the full event stream. + * Constructs a POST request that interprets the body as a JSON object and returns the full event + * stream. * * @param url The endpoint URL. * @param body The content to replace with. @@ -2185,8 +2133,7 @@ export class HttpClient { * with a response body of type `Object`. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2194,7 +2141,8 @@ export class HttpClient { }): Observable<HttpEvent<Object>>; /** - * Constructs a POST request that interprets the body as a JSON object and returns the full event stream. + * Constructs a POST request that interprets the body as a JSON object and returns the full event + * stream. * * @param url The endpoint URL. * @param body The content to replace with. @@ -2204,8 +2152,7 @@ export class HttpClient { * with a response body in the requested type. */ post<T>(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2220,14 +2167,14 @@ export class HttpClient { * @param body The content to replace with. * @param options HTTP options * - * @return An `Observable` of the `HTTPResponse` for the request, with the response body as an `ArrayBuffer`. + * @return An `Observable` of the `HTTPResponse` for the request, with the response body as an + * `ArrayBuffer`. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpResponse<ArrayBuffer>>; /** @@ -2242,11 +2189,10 @@ export class HttpClient { * with the response body as a `Blob`. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpResponse<Blob>>; /** @@ -2261,11 +2207,10 @@ export class HttpClient { * with a response body of type string. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpResponse<string>>; /** @@ -2280,8 +2225,7 @@ export class HttpClient { * `Object`. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2297,11 +2241,11 @@ export class HttpClient { * @param body The content to replace with. * @param options HTTP options * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the requested type. + * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the + * requested type. */ post<T>(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2319,7 +2263,7 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a JSON object. */ post(url: string, body: any|null, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -2335,10 +2279,11 @@ export class HttpClient { * @param body The content to replace with. * @param options HTTP options * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the requested type. + * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the + * requested type. */ post<T>(url: string, body: any|null, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -2353,7 +2298,7 @@ export class HttpClient { * details on the return type. */ post(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: HttpObserve, params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -2374,11 +2319,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as an `ArrayBuffer`. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<ArrayBuffer>; /** @@ -2392,11 +2337,11 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a `Blob`. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<Blob>; /** @@ -2410,11 +2355,11 @@ export class HttpClient { * @return An `Observable` of the response, with a response body of type string. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<string>; /** @@ -2429,15 +2374,15 @@ export class HttpClient { * with the response body as an `ArrayBuffer`. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpEvent<ArrayBuffer>>; /** - * Constructs a `PUT` request that interprets the body as a `Blob` and returns the full event stream. + * Constructs a `PUT` request that interprets the body as a `Blob` and returns the full event + * stream. * * @param url The endpoint URL. * @param body The resources to add/update. @@ -2447,15 +2392,15 @@ export class HttpClient { * with the response body as a `Blob`. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpEvent<Blob>>; /** - * Constructs a `PUT` request that interprets the body as a text string and returns the full event stream. + * Constructs a `PUT` request that interprets the body as a text string and returns the full event + * stream. * * @param url The endpoint URL. * @param body The resources to add/update. @@ -2465,15 +2410,15 @@ export class HttpClient { * of type string. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpEvent<string>>; /** - * Constructs a `PUT` request that interprets the body as a JSON object and returns the full event stream. + * Constructs a `PUT` request that interprets the body as a JSON object and returns the full event + * stream. * * @param url The endpoint URL. * @param body The resources to add/update. @@ -2483,8 +2428,7 @@ export class HttpClient { * type `Object`. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2503,8 +2447,9 @@ export class HttpClient { * with a response body in the requested type. */ put<T>(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'events', responseType?: 'json', withCredentials?: boolean, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events', + responseType?: 'json', + withCredentials?: boolean, }): Observable<HttpEvent<T>>; /** @@ -2515,14 +2460,14 @@ export class HttpClient { * @param body The resources to add/update. * @param options HTTP options * - * @return An `Observable` of the `HTTPResponse` for the request, with the response body as an `ArrayBuffer`. + * @return An `Observable` of the `HTTPResponse` for the request, with the response body as an + * `ArrayBuffer`. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'arraybuffer', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'arraybuffer', + withCredentials?: boolean, }): Observable<HttpResponse<ArrayBuffer>>; /** @@ -2537,11 +2482,10 @@ export class HttpClient { * with the response body as a `Blob`. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'blob', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'blob', + withCredentials?: boolean, }): Observable<HttpResponse<Blob>>; /** @@ -2552,18 +2496,19 @@ export class HttpClient { * @param body The resources to add/update. * @param options HTTP options * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body of type string. + * @return An `Observable` of the `HTTPResponse` for the request, with a response body of type + * string. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, - reportProgress?: boolean, - responseType: 'text', withCredentials?: boolean, + reportProgress?: boolean, responseType: 'text', + withCredentials?: boolean, }): Observable<HttpResponse<string>>; /** - * Constructs a `PUT` request that interprets the body as a JSON object and returns the full HTTP response. + * Constructs a `PUT` request that interprets the body as a JSON object and returns the full HTTP + * response. * * @param url The endpoint URL. * @param body The resources to add/update. @@ -2573,8 +2518,7 @@ export class HttpClient { * of type 'Object`. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2582,7 +2526,8 @@ export class HttpClient { }): Observable<HttpResponse<Object>>; /** - * Constructs a `PUT` request that interprets the body as a JSON object and returns the full HTTP response. + * Constructs a `PUT` request that interprets the body as a JSON object and returns the full HTTP + * response. * * @param url The endpoint URL. * @param body The resources to add/update. @@ -2592,8 +2537,7 @@ export class HttpClient { * with a response body in the requested type. */ put<T>(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, - observe: 'response', + headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'response', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, responseType?: 'json', @@ -2611,7 +2555,7 @@ export class HttpClient { * @return An `Observable` of the response, with the response body as a JSON object. */ put(url: string, body: any|null, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -2627,10 +2571,11 @@ export class HttpClient { * @param body The resources to add/update. * @param options HTTP options * - * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the requested type. + * @return An `Observable` of the `HTTPResponse` for the request, with a response body in the + * requested type. */ put<T>(url: string, body: any|null, options?: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: 'body', params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, @@ -2645,7 +2590,7 @@ export class HttpClient { * See the individual overloads for details on the return type. */ put(url: string, body: any|null, options: { - headers?: HttpHeaders | {[header: string]: string | string[]}, + headers?: HttpHeaders|{[header: string]: string | string[]}, observe?: HttpObserve, params?: HttpParams|{[param: string]: string | string[]}, reportProgress?: boolean, diff --git a/packages/common/http/src/headers.ts b/packages/common/http/src/headers.ts index a8a006d467e7b..6e187c3adabf7 100755 --- a/packages/common/http/src/headers.ts +++ b/packages/common/http/src/headers.ts @@ -24,7 +24,7 @@ export class HttpHeaders { * Internal map of lowercase header names to values. */ // TODO(issue/24571): remove '!'. - private headers !: Map<string, string[]>; + private headers!: Map<string, string[]>; /** @@ -36,7 +36,7 @@ export class HttpHeaders { /** * Complete the lazy initialization of this object (needed before reading). */ - private lazyInit !: HttpHeaders | Function | null; + private lazyInit!: HttpHeaders|Function|null; /** * Queued updates to be materialized the next initialization. @@ -59,7 +59,7 @@ export class HttpHeaders { const value = line.slice(index + 1).trim(); this.maybeSetNormalizedName(name, key); if (this.headers.has(key)) { - this.headers.get(key) !.push(value); + this.headers.get(key)!.push(value); } else { this.headers.set(key, [value]); } @@ -169,7 +169,7 @@ export class HttpHeaders { * * @returns A clone of the HTTP headers object with the given value deleted. */ - delete (name: string, value?: string|string[]): HttpHeaders { + delete(name: string, value?: string|string[]): HttpHeaders { return this.clone({name, value, op: 'd'}); } @@ -197,8 +197,8 @@ export class HttpHeaders { private copyFrom(other: HttpHeaders) { other.init(); Array.from(other.headers.keys()).forEach(key => { - this.headers.set(key, other.headers.get(key) !); - this.normalizedNames.set(key, other.normalizedNames.get(key) !); + this.headers.set(key, other.headers.get(key)!); + this.normalizedNames.set(key, other.normalizedNames.get(key)!); }); } @@ -215,7 +215,7 @@ export class HttpHeaders { switch (update.op) { case 'a': case 's': - let value = update.value !; + let value = update.value!; if (typeof value === 'string') { value = [value]; } @@ -255,6 +255,6 @@ export class HttpHeaders { forEach(fn: (name: string, values: string[]) => void) { this.init(); Array.from(this.normalizedNames.keys()) - .forEach(key => fn(this.normalizedNames.get(key) !, this.headers.get(key) !)); + .forEach(key => fn(this.normalizedNames.get(key)!, this.headers.get(key)!)); } } diff --git a/packages/common/http/src/interceptor.ts b/packages/common/http/src/interceptor.ts index 0f71d46eb44b2..5f83c1421230e 100644 --- a/packages/common/http/src/interceptor.ts +++ b/packages/common/http/src/interceptor.ts @@ -38,8 +38,8 @@ import {HttpEvent} from './response'; * To use the same instance of `HttpInterceptors` for the entire app, import the `HttpClientModule` * only in your `AppModule`, and add the interceptors to the root application injector . * If you import `HttpClientModule` multiple times across different modules (for example, in lazy - * loading modules), each import creates a new copy of the `HttpClientModule`, which overwrites the interceptors - * provided in the root module. + * loading modules), each import creates a new copy of the `HttpClientModule`, which overwrites the + * interceptors provided in the root module. * */ export interface HttpInterceptor { diff --git a/packages/common/http/src/jsonp.ts b/packages/common/http/src/jsonp.ts index b613434039a8e..7a6c9311e4775 100644 --- a/packages/common/http/src/jsonp.ts +++ b/packages/common/http/src/jsonp.ts @@ -36,7 +36,9 @@ export const JSONP_ERR_WRONG_RESPONSE_TYPE = 'JSONP requests must use Json respo * * */ -export abstract class JsonpCallbackContext { [key: string]: (data: any) => void; } +export abstract class JsonpCallbackContext { + [key: string]: (data: any) => void; +} /** * Processes an `HttpRequest` with the JSONP method, @@ -53,7 +55,9 @@ export class JsonpClientBackend implements HttpBackend { /** * Get the name of the next callback method, by incrementing the global `nextRequestId`. */ - private nextCallback(): string { return `ng_jsonp_callback_${nextRequestId++}`; } + private nextCallback(): string { + return `ng_jsonp_callback_${nextRequestId++}`; + } /** * Processes a JSONP request and returns an event stream of the results. @@ -157,7 +161,8 @@ export class JsonpClientBackend implements HttpBackend { observer.next(new HttpResponse({ body, status: 200, - statusText: 'OK', url, + statusText: 'OK', + url, })); // Complete the stream, the response is over. @@ -178,7 +183,8 @@ export class JsonpClientBackend implements HttpBackend { observer.error(new HttpErrorResponse({ error, status: 0, - statusText: 'JSONP Error', url, + statusText: 'JSONP Error', + url, })); }; diff --git a/packages/common/http/src/module.ts b/packages/common/http/src/module.ts index 9225814ac66ae..33460638ef5e8 100644 --- a/packages/common/http/src/module.ts +++ b/packages/common/http/src/module.ts @@ -52,7 +52,7 @@ export class HttpInterceptingHandler implements HttpHandler { * */ export function interceptingHandler( - backend: HttpBackend, interceptors: HttpInterceptor[] | null = []): HttpHandler { + backend: HttpBackend, interceptors: HttpInterceptor[]|null = []): HttpHandler { if (!interceptors) { return backend; } diff --git a/packages/common/http/src/params.ts b/packages/common/http/src/params.ts index 30caa33cdec79..1e0ab81e9f4bb 100755 --- a/packages/common/http/src/params.ts +++ b/packages/common/http/src/params.ts @@ -37,28 +37,36 @@ export class HttpUrlEncodingCodec implements HttpParameterCodec { * @param key The key name. * @returns The encoded key name. */ - encodeKey(key: string): string { return standardEncoding(key); } + encodeKey(key: string): string { + return standardEncoding(key); + } /** * Encodes the value of a URL parameter or query-string. * @param value The value. * @returns The encoded value. */ - encodeValue(value: string): string { return standardEncoding(value); } + encodeValue(value: string): string { + return standardEncoding(value); + } /** * Decodes an encoded URL parameter or query-string key. * @param key The encoded key name. * @returns The decoded key name. */ - decodeKey(key: string): string { return decodeURIComponent(key); } + decodeKey(key: string): string { + return decodeURIComponent(key); + } /** * Decodes an encoded URL parameter or query-string value. * @param value The encoded value. * @returns The decoded value. */ - decodeValue(value: string) { return decodeURIComponent(value); } + decodeValue(value: string) { + return decodeURIComponent(value); + } } @@ -97,7 +105,8 @@ interface Update { op: 'a'|'d'|'s'; } -/** Options used to construct an `HttpParams` instance. +/** + * Options used to construct an `HttpParams` instance. * * @publicApi */ @@ -109,7 +118,7 @@ export interface HttpParamsOptions { fromString?: string; /** Object map of the HTTP parameters. Mutually exclusive with `fromString`. */ - fromObject?: {[param: string]: string | ReadonlyArray<string>}; + fromObject?: {[param: string]: string|ReadonlyArray<string>}; /** Encoding codec used to parse and serialize the parameters. */ encoder?: HttpParameterCodec; @@ -140,7 +149,7 @@ export class HttpParams { this.map = new Map<string, string[]>(); Object.keys(options.fromObject).forEach(key => { const value = (options.fromObject as any)[key]; - this.map !.set(key, Array.isArray(value) ? value : [value]); + this.map!.set(key, Array.isArray(value) ? value : [value]); }); } else { this.map = null; @@ -155,7 +164,7 @@ export class HttpParams { */ has(param: string): boolean { this.init(); - return this.map !.has(param); + return this.map!.has(param); } /** @@ -166,7 +175,7 @@ export class HttpParams { */ get(param: string): string|null { this.init(); - const res = this.map !.get(param); + const res = this.map!.get(param); return !!res ? res[0] : null; } @@ -178,7 +187,7 @@ export class HttpParams { */ getAll(param: string): string[]|null { this.init(); - return this.map !.get(param) || null; + return this.map!.get(param) || null; } /** @@ -187,7 +196,7 @@ export class HttpParams { */ keys(): string[] { this.init(); - return Array.from(this.map !.keys()); + return Array.from(this.map!.keys()); } /** @@ -196,7 +205,9 @@ export class HttpParams { * @param value The new value to add. * @return A new body with the appended value. */ - append(param: string, value: string): HttpParams { return this.clone({param, value, op: 'a'}); } + append(param: string, value: string): HttpParams { + return this.clone({param, value, op: 'a'}); + } /** * Replaces the value for a parameter. @@ -204,7 +215,9 @@ export class HttpParams { * @param value The new value. * @return A new body with the new value. */ - set(param: string, value: string): HttpParams { return this.clone({param, value, op: 's'}); } + set(param: string, value: string): HttpParams { + return this.clone({param, value, op: 's'}); + } /** * Removes a given value or all values from a parameter. @@ -213,7 +226,9 @@ export class HttpParams { * @return A new body with the given value removed, or with all values * removed if no value is specified. */ - delete (param: string, value?: string): HttpParams { return this.clone({param, value, op: 'd'}); } + delete(param: string, value?: string): HttpParams { + return this.clone({param, value, op: 'd'}); + } /** * Serializes the body to an encoded string, where key-value pairs (separated by `=`) are @@ -227,7 +242,7 @@ export class HttpParams { // `a: ['1']` produces `'a=1'` // `b: []` produces `''` // `c: ['1', '2']` produces `'c=1&c=2'` - return this.map !.get(key) !.map(value => eKey + '=' + this.encoder.encodeValue(value)) + return this.map!.get(key)!.map(value => eKey + '=' + this.encoder.encodeValue(value)) .join('&'); }) // filter out empty values because `b: []` produces `''` @@ -237,7 +252,7 @@ export class HttpParams { } private clone(update: Update): HttpParams { - const clone = new HttpParams({ encoder: this.encoder } as HttpParamsOptions); + const clone = new HttpParams({encoder: this.encoder} as HttpParamsOptions); clone.cloneFrom = this.cloneFrom || this; clone.updates = (this.updates || []).concat([update]); return clone; @@ -249,29 +264,29 @@ export class HttpParams { } if (this.cloneFrom !== null) { this.cloneFrom.init(); - this.cloneFrom.keys().forEach(key => this.map !.set(key, this.cloneFrom !.map !.get(key) !)); - this.updates !.forEach(update => { + this.cloneFrom.keys().forEach(key => this.map!.set(key, this.cloneFrom!.map!.get(key)!)); + this.updates!.forEach(update => { switch (update.op) { case 'a': case 's': - const base = (update.op === 'a' ? this.map !.get(update.param) : undefined) || []; - base.push(update.value !); - this.map !.set(update.param, base); + const base = (update.op === 'a' ? this.map!.get(update.param) : undefined) || []; + base.push(update.value!); + this.map!.set(update.param, base); break; case 'd': if (update.value !== undefined) { - let base = this.map !.get(update.param) || []; + let base = this.map!.get(update.param) || []; const idx = base.indexOf(update.value); if (idx !== -1) { base.splice(idx, 1); } if (base.length > 0) { - this.map !.set(update.param, base); + this.map!.set(update.param, base); } else { - this.map !.delete(update.param); + this.map!.delete(update.param); } } else { - this.map !.delete(update.param); + this.map!.delete(update.param); break; } } diff --git a/packages/common/http/src/request.ts b/packages/common/http/src/request.ts index b641daad9b4a0..12409680ce357 100644 --- a/packages/common/http/src/request.ts +++ b/packages/common/http/src/request.ts @@ -89,7 +89,7 @@ export class HttpRequest<T> { * Outgoing headers for this request. */ // TODO(issue/24571): remove '!'. - readonly headers !: HttpHeaders; + readonly headers!: HttpHeaders; /** * Whether this request should be made in a way that exposes progress events. @@ -121,7 +121,7 @@ export class HttpRequest<T> { * Outgoing URL parameters. */ // TODO(issue/24571): remove '!'. - readonly params !: HttpParams; + readonly params!: HttpParams; /** * The outgoing URL with all URL parameters set. @@ -312,7 +312,7 @@ export class HttpRequest<T> { body?: T|null, method?: string, url?: string, - setHeaders?: {[name: string]: string | string[]}, + setHeaders?: {[name: string]: string|string[]}, setParams?: {[param: string]: string}, }): HttpRequest<T>; clone<V>(update: { @@ -324,7 +324,7 @@ export class HttpRequest<T> { body?: V|null, method?: string, url?: string, - setHeaders?: {[name: string]: string | string[]}, + setHeaders?: {[name: string]: string|string[]}, setParams?: {[param: string]: string}, }): HttpRequest<V>; clone(update: { @@ -336,7 +336,7 @@ export class HttpRequest<T> { body?: any|null, method?: string, url?: string, - setHeaders?: {[name: string]: string | string[]}, + setHeaders?: {[name: string]: string|string[]}, setParams?: {[param: string]: string}; } = {}): HttpRequest<any> { // For method, url, and responseType, take the current value unless @@ -368,20 +368,23 @@ export class HttpRequest<T> { // Set every requested header. headers = Object.keys(update.setHeaders) - .reduce((headers, name) => headers.set(name, update.setHeaders ![name]), headers); + .reduce((headers, name) => headers.set(name, update.setHeaders![name]), headers); } // Check whether the caller has asked to set params. if (update.setParams) { // Set every requested param. params = Object.keys(update.setParams) - .reduce((params, param) => params.set(param, update.setParams ![param]), params); + .reduce((params, param) => params.set(param, update.setParams![param]), params); } // Finally, construct the new HttpRequest using the pieces from above. - return new HttpRequest( - method, url, body, { - params, headers, reportProgress, responseType, withCredentials, - }); + return new HttpRequest(method, url, body, { + params, + headers, + reportProgress, + responseType, + withCredentials, + }); } } diff --git a/packages/common/http/src/response.ts b/packages/common/http/src/response.ts index 35af00813d524..d41d7aeef7a89 100644 --- a/packages/common/http/src/response.ts +++ b/packages/common/http/src/response.ts @@ -100,7 +100,9 @@ export interface HttpUploadProgressEvent extends HttpProgressEvent { * * @publicApi */ -export interface HttpSentEvent { type: HttpEventType.Sent; } +export interface HttpSentEvent { + type: HttpEventType.Sent; +} /** * A user-defined event. @@ -110,7 +112,9 @@ export interface HttpSentEvent { type: HttpEventType.Sent; } * * @publicApi */ -export interface HttpUserEvent<T> { type: HttpEventType.User; } +export interface HttpUserEvent<T> { + type: HttpEventType.User; +} /** * An error that represents a failed attempt to JSON.parse text coming back @@ -133,7 +137,7 @@ export interface HttpJsonParseError { * @publicApi */ export type HttpEvent<T> = - HttpSentEvent | HttpHeaderResponse | HttpResponse<T>| HttpProgressEvent | HttpUserEvent<T>; + HttpSentEvent|HttpHeaderResponse|HttpResponse<T>|HttpProgressEvent|HttpUserEvent<T>; /** * Base class for both `HttpResponse` and `HttpHeaderResponse`. @@ -172,7 +176,7 @@ export abstract class HttpResponseBase { * Type of the response, narrowed to either the full response or the header. */ // TODO(issue/24571): remove '!'. - readonly type !: HttpEventType.Response | HttpEventType.ResponseHeader; + readonly type!: HttpEventType.Response|HttpEventType.ResponseHeader; /** * Super-constructor for all responses. @@ -260,7 +264,11 @@ export class HttpResponse<T> extends HttpResponseBase { * Construct a new `HttpResponse`. */ constructor(init: { - body?: T | null, headers?: HttpHeaders; status?: number; statusText?: string; url?: string; + body?: T|null, + headers?: HttpHeaders; + status?: number; + statusText?: string; + url?: string; } = {}) { super(init); this.body = init.body !== undefined ? init.body : null; @@ -272,10 +280,18 @@ export class HttpResponse<T> extends HttpResponseBase { clone(update: {headers?: HttpHeaders; status?: number; statusText?: string; url?: string;}): HttpResponse<T>; clone<V>(update: { - body?: V | null, headers?: HttpHeaders; status?: number; statusText?: string; url?: string; + body?: V|null, + headers?: HttpHeaders; + status?: number; + statusText?: string; + url?: string; }): HttpResponse<V>; clone(update: { - body?: any | null; headers?: HttpHeaders; status?: number; statusText?: string; url?: string; + body?: any|null; + headers?: HttpHeaders; + status?: number; + statusText?: string; + url?: string; } = {}): HttpResponse<any> { return new HttpResponse<any>({ body: (update.body !== undefined) ? update.body : this.body, @@ -311,7 +327,11 @@ export class HttpErrorResponse extends HttpResponseBase implements Error { readonly ok = false; constructor(init: { - error?: any; headers?: HttpHeaders; status?: number; statusText?: string; url?: string; + error?: any; + headers?: HttpHeaders; + status?: number; + statusText?: string; + url?: string; }) { // Initialize with a default status of 0 / Unknown Error. super(init, 0, 'Unknown Error'); @@ -322,8 +342,8 @@ export class HttpErrorResponse extends HttpResponseBase implements Error { if (this.status >= 200 && this.status < 300) { this.message = `Http failure during parsing for ${init.url || '(unknown url)'}`; } else { - this.message = - `Http failure response for ${init.url || '(unknown url)'}: ${init.status} ${init.statusText}`; + this.message = `Http failure response for ${init.url || '(unknown url)'}: ${init.status} ${ + init.statusText}`; } this.error = init.error || null; } diff --git a/packages/common/http/src/xhr.ts b/packages/common/http/src/xhr.ts index b1c00e3249903..f0e4eb9a81860 100644 --- a/packages/common/http/src/xhr.ts +++ b/packages/common/http/src/xhr.ts @@ -35,7 +35,9 @@ function getResponseUrl(xhr: any): string|null { * * @publicApi */ -export abstract class XhrFactory { abstract build(): XMLHttpRequest; } +export abstract class XhrFactory { + abstract build(): XMLHttpRequest; +} /** * A factory for `HttpXhrBackend` that uses the `XMLHttpRequest` browser API. @@ -44,7 +46,9 @@ export abstract class XhrFactory { abstract build(): XMLHttpRequest; } @Injectable() export class BrowserXhr implements XhrFactory { constructor() {} - build(): any { return <any>(new XMLHttpRequest()); } + build(): any { + return <any>(new XMLHttpRequest()); + } } /** @@ -200,7 +204,7 @@ export class HttpXhrBackend implements HttpBackend { // Even though the response status was 2xx, this is still an error. ok = false; // The parse error contains the text of the body that failed to parse. - body = { error, text: body } as HttpJsonParseError; + body = {error, text: body} as HttpJsonParseError; } } } @@ -318,7 +322,7 @@ export class HttpXhrBackend implements HttpBackend { } // Fire the request, and notify the event stream that it was fired. - xhr.send(reqBody !); + xhr.send(reqBody!); observer.next({type: HttpEventType.Sent}); // This is the return from the Observable function, which is the diff --git a/packages/common/http/test/client_spec.ts b/packages/common/http/test/client_spec.ts index bed29df8eb351..d84925c2d0d55 100644 --- a/packages/common/http/test/client_spec.ts +++ b/packages/common/http/test/client_spec.ts @@ -14,13 +14,15 @@ import {toArray} from 'rxjs/operators'; { describe('HttpClient', () => { - let client: HttpClient = null !; - let backend: HttpClientTestingBackend = null !; + let client: HttpClient = null!; + let backend: HttpClientTestingBackend = null!; beforeEach(() => { backend = new HttpClientTestingBackend(); client = new HttpClient(backend); }); - afterEach(() => { backend.verify(); }); + afterEach(() => { + backend.verify(); + }); describe('makes a basic request', () => { it('for JSON data', done => { client.get('/test').subscribe(res => { diff --git a/packages/common/http/test/headers_spec.ts b/packages/common/http/test/headers_spec.ts index c7e23e2b2c4bc..49554a52015a4 100644 --- a/packages/common/http/test/headers_spec.ts +++ b/packages/common/http/test/headers_spec.ts @@ -10,7 +10,6 @@ import {HttpHeaders} from '@angular/common/http/src/headers'; { describe('HttpHeaders', () => { - describe('initialization', () => { it('should conform to spec', () => { const httpHeaders = { diff --git a/packages/common/http/test/jsonp_mock.ts b/packages/common/http/test/jsonp_mock.ts index 7b2345d7c3494..b05f25672dae7 100644 --- a/packages/common/http/test/jsonp_mock.ts +++ b/packages/common/http/test/jsonp_mock.ts @@ -18,19 +18,23 @@ export class MockScriptElement { this.listeners[event] = handler as any; } - removeEventListener(event: 'load'|'error'): void { delete this.listeners[event]; } + removeEventListener(event: 'load'|'error'): void { + delete this.listeners[event]; + } } export class MockDocument { // TODO(issue/24571): remove '!'. - mock !: MockScriptElement | null; + mock!: MockScriptElement|null; readonly body: any = this; createElement(tag: 'script'): HTMLScriptElement { return new MockScriptElement() as any as HTMLScriptElement; } - appendChild(node: any): void { this.mock = node; } + appendChild(node: any): void { + this.mock = node; + } removeNode(node: any): void { if (this.mock === node) { @@ -38,7 +42,11 @@ export class MockDocument { } } - mockLoad(): void { this.mock !.listeners.load !(null as any); } + mockLoad(): void { + this.mock!.listeners.load!(null as any); + } - mockError(err: Error) { this.mock !.listeners.error !(err); } + mockError(err: Error) { + this.mock!.listeners.error!(err); + } } diff --git a/packages/common/http/test/module_spec.ts b/packages/common/http/test/module_spec.ts index a6a66440f3cb2..4841de7b496f9 100644 --- a/packages/common/http/test/module_spec.ts +++ b/packages/common/http/test/module_spec.ts @@ -38,11 +38,15 @@ class TestInterceptor implements HttpInterceptor { } class InterceptorA extends TestInterceptor { - constructor() { super('A'); } + constructor() { + super('A'); + } } class InterceptorB extends TestInterceptor { - constructor() { super('B'); } + constructor() { + super('B'); + } } @Injectable() @@ -98,7 +102,9 @@ class ReentrantInterceptor implements HttpInterceptor { {provide: HTTP_INTERCEPTORS, useClass: ReentrantInterceptor, multi: true}, ], }); - injector.get(HttpClient).get('/test').subscribe(() => { done(); }); + injector.get(HttpClient).get('/test').subscribe(() => { + done(); + }); injector.get(HttpTestingController).expectOne('/test').flush('ok!'); }); }); diff --git a/packages/common/http/test/request_spec.ts b/packages/common/http/test/request_spec.ts index af17f0f7887e6..4c4f74d4a36df 100644 --- a/packages/common/http/test/request_spec.ts +++ b/packages/common/http/test/request_spec.ts @@ -80,16 +80,21 @@ const TEST_STRING = `I'm a body!`; expect(clone.headers).toBe(headers); expect(clone.headers.get('Test')).toBe('Test header'); }); - it('and updates the url', - () => { expect(req.clone({url: '/changed'}).url).toBe('/changed'); }); - it('and updates the method', - () => { expect(req.clone({method: 'PUT'}).method).toBe('PUT'); }); - it('and updates the body', - () => { expect(req.clone({body: 'changed body'}).body).toBe('changed body'); }); + it('and updates the url', () => { + expect(req.clone({url: '/changed'}).url).toBe('/changed'); + }); + it('and updates the method', () => { + expect(req.clone({method: 'PUT'}).method).toBe('PUT'); + }); + it('and updates the body', () => { + expect(req.clone({body: 'changed body'}).body).toBe('changed body'); + }); }); describe('content type detection', () => { const baseReq = new HttpRequest('POST', '/test', null); - it('handles a null body', () => { expect(baseReq.detectContentTypeHeader()).toBeNull(); }); + it('handles a null body', () => { + expect(baseReq.detectContentTypeHeader()).toBeNull(); + }); it('doesn\'t associate a content type with ArrayBuffers', () => { const req = baseReq.clone({body: new ArrayBuffer(4)}); expect(req.detectContentTypeHeader()).toBeNull(); @@ -113,7 +118,9 @@ const TEST_STRING = `I'm a body!`; }); describe('body serialization', () => { const baseReq = new HttpRequest('POST', '/test', null); - it('handles a null body', () => { expect(baseReq.serializeBody()).toBeNull(); }); + it('handles a null body', () => { + expect(baseReq.serializeBody()).toBeNull(); + }); it('passes ArrayBuffers through', () => { const body = new ArrayBuffer(4); expect(baseReq.clone({body}).serializeBody()).toBe(body); @@ -125,8 +132,9 @@ const TEST_STRING = `I'm a body!`; it('serializes arrays as json', () => { expect(baseReq.clone({body: ['a', 'b']}).serializeBody()).toBe('["a","b"]'); }); - it('handles numbers as json', - () => { expect(baseReq.clone({body: 314159}).serializeBody()).toBe('314159'); }); + it('handles numbers as json', () => { + expect(baseReq.clone({body: 314159}).serializeBody()).toBe('314159'); + }); it('handles objects as json', () => { const req = baseReq.clone({body: {data: 'test data'}}); expect(req.serializeBody()).toBe('{"data":"test data"}'); diff --git a/packages/common/http/test/xhr_mock.ts b/packages/common/http/test/xhr_mock.ts index bfac036991ba8..3634293726e6d 100644 --- a/packages/common/http/test/xhr_mock.ts +++ b/packages/common/http/test/xhr_mock.ts @@ -11,9 +11,11 @@ import {XhrFactory} from '@angular/common/http/src/xhr'; export class MockXhrFactory implements XhrFactory { // TODO(issue/24571): remove '!'. - mock !: MockXMLHttpRequest; + mock!: MockXMLHttpRequest; - build(): XMLHttpRequest { return (this.mock = new MockXMLHttpRequest()) as any; } + build(): XMLHttpRequest { + return (this.mock = new MockXMLHttpRequest()) as any; + } } export class MockXMLHttpRequestUpload { @@ -32,9 +34,9 @@ export class MockXMLHttpRequest { // Set by method calls. body: any; // TODO(issue/24571): remove '!'. - method !: string; + method!: string; // TODO(issue/24571): remove '!'. - url !: string; + url!: string; mockHeaders: {[key: string]: string} = {}; mockAborted: boolean = false; @@ -64,7 +66,9 @@ export class MockXMLHttpRequest { this.url = url; } - send(body: any): void { this.body = body; } + send(body: any): void { + this.body = body; + } addEventListener(event: 'error'|'load'|'progress'|'uploadProgress', handler: Function): void { this.listeners[event] = handler as any; @@ -74,9 +78,13 @@ export class MockXMLHttpRequest { delete this.listeners[event]; } - setRequestHeader(name: string, value: string): void { this.mockHeaders[name] = value; } + setRequestHeader(name: string, value: string): void { + this.mockHeaders[name] = value; + } - getAllResponseHeaders(): string { return this.mockResponseHeaders; } + getAllResponseHeaders(): string { + return this.mockResponseHeaders; + } getResponseHeader(header: string): string|null { return new HttpHeaders(this.mockResponseHeaders).get(header); @@ -95,14 +103,17 @@ export class MockXMLHttpRequest { mockDownloadProgressEvent(loaded: number, total?: number): void { if (this.listeners.progress) { - this.listeners.progress({ lengthComputable: total !== undefined, loaded, total } as any); + this.listeners.progress({lengthComputable: total !== undefined, loaded, total} as any); } } mockUploadProgressEvent(loaded: number, total?: number) { if (this.listeners.uploadProgress) { - this.listeners.uploadProgress( - { lengthComputable: total !== undefined, loaded, total, } as any); + this.listeners.uploadProgress({ + lengthComputable: total !== undefined, + loaded, + total, + } as any); } } @@ -118,5 +129,7 @@ export class MockXMLHttpRequest { } } - abort() { this.mockAborted = true; } + abort() { + this.mockAborted = true; + } } diff --git a/packages/common/http/test/xhr_spec.ts b/packages/common/http/test/xhr_spec.ts index 88e608d484b3f..d7b4147a2b796 100644 --- a/packages/common/http/test/xhr_spec.ts +++ b/packages/common/http/test/xhr_spec.ts @@ -29,8 +29,8 @@ const XSSI_PREFIX = ')]}\'\n'; { describe('XhrBackend', () => { - let factory: MockXhrFactory = null !; - let backend: HttpXhrBackend = null !; + let factory: MockXhrFactory = null!; + let backend: HttpXhrBackend = null!; beforeEach(() => { factory = new MockXhrFactory(); backend = new HttpXhrBackend(factory); @@ -92,7 +92,7 @@ const XSSI_PREFIX = ')]}\'\n'; factory.mock.mockFlush(200, 'OK', JSON.stringify({data: 'some data'})); expect(events.length).toBe(2); const res = events[1] as HttpResponse<{data: string}>; - expect(res.body !.data).toBe('some data'); + expect(res.body!.data).toBe('some data'); }); it('handles a blank json response', () => { const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); @@ -106,14 +106,14 @@ const XSSI_PREFIX = ')]}\'\n'; factory.mock.mockFlush(500, 'Error', JSON.stringify({data: 'some data'})); expect(events.length).toBe(2); const res = events[1] as any as HttpErrorResponse; - expect(res.error !.data).toBe('some data'); + expect(res.error!.data).toBe('some data'); }); it('handles a json error response with XSSI prefix', () => { const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); factory.mock.mockFlush(500, 'Error', XSSI_PREFIX + JSON.stringify({data: 'some data'})); expect(events.length).toBe(2); const res = events[1] as any as HttpErrorResponse; - expect(res.error !.data).toBe('some data'); + expect(res.error!.data).toBe('some data'); }); it('handles a json string response', () => { const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'}))); @@ -128,7 +128,7 @@ const XSSI_PREFIX = ')]}\'\n'; factory.mock.mockFlush(200, 'OK', XSSI_PREFIX + JSON.stringify({data: 'some data'})); expect(events.length).toBe(2); const res = events[1] as HttpResponse<{data: string}>; - expect(res.body !.data).toBe('some data'); + expect(res.body!.data).toBe('some data'); }); it('emits unsuccessful responses via the error path', done => { backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => { @@ -141,7 +141,7 @@ const XSSI_PREFIX = ')]}\'\n'; it('emits real errors via the error path', done => { backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => { expect(err instanceof HttpErrorResponse).toBe(true); - expect(err.error instanceof Error); + expect(err.error instanceof Error).toBeTrue(); expect(err.url).toBe('/test'); done(); }); diff --git a/packages/common/http/test/xsrf_spec.ts b/packages/common/http/test/xsrf_spec.ts index 252b0032b6fcc..bf588b2fab9cc 100644 --- a/packages/common/http/test/xsrf_spec.ts +++ b/packages/common/http/test/xsrf_spec.ts @@ -13,16 +13,22 @@ import {HttpXsrfCookieExtractor, HttpXsrfInterceptor, HttpXsrfTokenExtractor} fr import {HttpClientTestingBackend} from '@angular/common/http/testing/src/backend'; class SampleTokenExtractor extends HttpXsrfTokenExtractor { - constructor(private token: string|null) { super(); } + constructor(private token: string|null) { + super(); + } - getToken(): string|null { return this.token; } + getToken(): string|null { + return this.token; + } } { describe('HttpXsrfInterceptor', () => { let backend: HttpClientTestingBackend; const interceptor = new HttpXsrfInterceptor(new SampleTokenExtractor('test'), 'X-XSRF-TOKEN'); - beforeEach(() => { backend = new HttpClientTestingBackend(); }); + beforeEach(() => { + backend = new HttpClientTestingBackend(); + }); it('applies XSRF protection to outgoing requests', () => { interceptor.intercept(new HttpRequest('POST', '/test', {}), backend).subscribe(); const req = backend.expectOne('/test'); @@ -59,7 +65,9 @@ class SampleTokenExtractor extends HttpXsrfTokenExtractor { expect(req.request.headers.has('X-XSRF-TOKEN')).toEqual(false); req.flush({}); }); - afterEach(() => { backend.verify(); }); + afterEach(() => { + backend.verify(); + }); }); describe('HttpXsrfCookieExtractor', () => { let document: {[key: string]: string}; @@ -70,8 +78,9 @@ class SampleTokenExtractor extends HttpXsrfTokenExtractor { }; extractor = new HttpXsrfCookieExtractor(document, 'browser', 'XSRF-TOKEN'); }); - it('parses the cookie from document.cookie', - () => { expect(extractor.getToken()).toEqual('test'); }); + it('parses the cookie from document.cookie', () => { + expect(extractor.getToken()).toEqual('test'); + }); it('does not re-parse if document.cookie has not changed', () => { expect(extractor.getToken()).toEqual('test'); expect(extractor.getToken()).toEqual('test'); diff --git a/packages/common/http/testing/src/backend.ts b/packages/common/http/testing/src/backend.ts index 75a32389b8dff..b01dc0dc353c0 100644 --- a/packages/common/http/testing/src/backend.ts +++ b/packages/common/http/testing/src/backend.ts @@ -39,8 +39,10 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl return new Observable((observer: Observer<any>) => { const testReq = new TestRequest(req, observer); this.open.push(testReq); - observer.next({ type: HttpEventType.Sent } as HttpEvent<any>); - return () => { testReq._cancelled = true; }; + observer.next({type: HttpEventType.Sent} as HttpEvent<any>); + return () => { + testReq._cancelled = true; + }; }); } @@ -86,8 +88,8 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl description = description || this.descriptionFromMatcher(match); const matches = this.match(match); if (matches.length > 1) { - throw new Error( - `Expected one matching request for criteria "${description}", found ${matches.length} requests.`); + throw new Error(`Expected one matching request for criteria "${description}", found ${ + matches.length} requests.`); } if (matches.length === 0) { let message = `Expected one matching request for criteria "${description}", found none.`; @@ -116,8 +118,8 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl description = description || this.descriptionFromMatcher(match); const matches = this.match(match); if (matches.length > 0) { - throw new Error( - `Expected zero matching requests for criteria "${description}", found ${matches.length}.`); + throw new Error(`Expected zero matching requests for criteria "${description}", found ${ + matches.length}.`); } } diff --git a/packages/common/http/testing/src/request.ts b/packages/common/http/testing/src/request.ts index 022cf77e19be1..9f240be590785 100644 --- a/packages/common/http/testing/src/request.ts +++ b/packages/common/http/testing/src/request.ts @@ -21,7 +21,9 @@ export class TestRequest { /** * Whether the request was cancelled after it was sent. */ - get cancelled(): boolean { return this._cancelled; } + get cancelled(): boolean { + return this._cancelled; + } /** * @internal set by `HttpClientTestingBackend` @@ -39,7 +41,7 @@ export class TestRequest { * Both successful and unsuccessful responses can be delivered via `flush()`. */ flush(body: ArrayBuffer|Blob|string|number|Object|(string|number|Object|null)[]|null, opts: { - headers?: HttpHeaders | {[name: string]: string | string[]}, + headers?: HttpHeaders|{[name: string]: string | string[]}, status?: number, statusText?: string, } = {}): void { @@ -75,7 +77,7 @@ export class TestRequest { * Resolve the request by returning an `ErrorEvent` (e.g. simulating a network failure). */ error(error: ErrorEvent, opts: { - headers?: HttpHeaders | {[name: string]: string | string[]}, + headers?: HttpHeaders|{[name: string]: string | string[]}, status?: number, statusText?: string, } = {}): void { @@ -112,9 +114,8 @@ export class TestRequest { /** * Helper function to convert a response body to an ArrayBuffer. */ -function _toArrayBufferBody( - body: ArrayBuffer | Blob | string | number | Object | - (string | number | Object | null)[]): ArrayBuffer { +function _toArrayBufferBody(body: ArrayBuffer|Blob|string|number|Object| + (string | number | Object | null)[]): ArrayBuffer { if (typeof ArrayBuffer === 'undefined') { throw new Error('ArrayBuffer responses are not supported on this platform.'); } @@ -127,9 +128,8 @@ function _toArrayBufferBody( /** * Helper function to convert a response body to a Blob. */ -function _toBlob( - body: ArrayBuffer | Blob | string | number | Object | - (string | number | Object | null)[]): Blob { +function _toBlob(body: ArrayBuffer|Blob|string|number|Object| + (string | number | Object | null)[]): Blob { if (typeof Blob === 'undefined') { throw new Error('Blob responses are not supported on this platform.'); } @@ -146,7 +146,7 @@ function _toBlob( * Helper function to convert a response body to JSON data. */ function _toJsonBody( - body: ArrayBuffer | Blob | string | number | Object | (string | number | Object | null)[], + body: ArrayBuffer|Blob|string|number|Object|(string | number | Object | null)[], format: string = 'JSON'): Object|string|number|(Object | string | number)[] { if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer) { throw new Error(`Automatic conversion to ${format} is not supported for ArrayBuffers.`); @@ -164,9 +164,8 @@ function _toJsonBody( /** * Helper function to convert a response body to a string. */ -function _toTextBody( - body: ArrayBuffer | Blob | string | number | Object | - (string | number | Object | null)[]): string { +function _toTextBody(body: ArrayBuffer|Blob|string|number|Object| + (string | number | Object | null)[]): string { if (typeof body === 'string') { return body; } @@ -183,9 +182,9 @@ function _toTextBody( * Convert a response body to the requested type. */ function _maybeConvertBody( - responseType: string, body: ArrayBuffer | Blob | string | number | Object | - (string | number | Object | null)[] | null): ArrayBuffer|Blob|string|number|Object| - (string | number | Object | null)[]|null { + responseType: string, + body: ArrayBuffer|Blob|string|number|Object|(string | number | Object | null)[]| + null): ArrayBuffer|Blob|string|number|Object|(string | number | Object | null)[]|null { if (body === null) { return null; } diff --git a/packages/common/http/testing/test/request_spec.ts b/packages/common/http/testing/test/request_spec.ts index 92d78399d29d7..5c4092a52d2c3 100644 --- a/packages/common/http/testing/test/request_spec.ts +++ b/packages/common/http/testing/test/request_spec.ts @@ -15,7 +15,9 @@ describe('HttpClient TestRequest', () => { const client = new HttpClient(mock); let resp: any; - client.post('/some-url', {test: 'test'}).subscribe(body => { resp = body; }); + client.post('/some-url', {test: 'test'}).subscribe(body => { + resp = body; + }); const req = mock.expectOne('/some-url'); req.flush(null); @@ -28,7 +30,9 @@ describe('HttpClient TestRequest', () => { const client = new HttpClient(mock); let resp: any; - client.get('/some-other-url').subscribe(body => { resp = body; }); + client.get('/some-other-url').subscribe(body => { + resp = body; + }); try { // expect different URL @@ -48,7 +52,9 @@ describe('HttpClient TestRequest', () => { let resp: any; const params = {query: 'hello'}; - client.get('/some-url', {params}).subscribe(body => { resp = body; }); + client.get('/some-url', {params}).subscribe(body => { + resp = body; + }); try { // expect different query parameters @@ -67,8 +73,12 @@ describe('HttpClient TestRequest', () => { const client = new HttpClient(mock); let resp: any; - client.get('/some-other-url?query=world').subscribe(body => { resp = body; }); - client.post('/and-another-url', {}).subscribe(body => { resp = body; }); + client.get('/some-other-url?query=world').subscribe(body => { + resp = body; + }); + client.post('/and-another-url', {}).subscribe(body => { + resp = body; + }); try { // expect different URL diff --git a/packages/common/locales/global/af-NA.js b/packages/common/locales/global/af-NA.js index 471a48f7e9b90..d83b3a73e5124 100644 --- a/packages/common/locales/global/af-NA.js +++ b/packages/common/locales/global/af-NA.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/af.js b/packages/common/locales/global/af.js index ee0f963abbc6f..402ed13ea5dd1 100644 --- a/packages/common/locales/global/af.js +++ b/packages/common/locales/global/af.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/agq.js b/packages/common/locales/global/agq.js index bd7afe2bf6c8c..4d5aeb2e694e8 100644 --- a/packages/common/locales/global/agq.js +++ b/packages/common/locales/global/agq.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['agq'] = [ 'agq', diff --git a/packages/common/locales/global/ak.js b/packages/common/locales/global/ak.js index 60e5be2218c5e..fdecc3ee7913e 100644 --- a/packages/common/locales/global/ak.js +++ b/packages/common/locales/global/ak.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/am.js b/packages/common/locales/global/am.js index 0f597a4f75b75..9c0eaed2a5eae 100644 --- a/packages/common/locales/global/am.js +++ b/packages/common/locales/global/am.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ar-AE.js b/packages/common/locales/global/ar-AE.js index 37e655b58772c..515bbf54c7083 100644 --- a/packages/common/locales/global/ar-AE.js +++ b/packages/common/locales/global/ar-AE.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-BH.js b/packages/common/locales/global/ar-BH.js index e190d93b54c05..aa0136eba1692 100644 --- a/packages/common/locales/global/ar-BH.js +++ b/packages/common/locales/global/ar-BH.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-DJ.js b/packages/common/locales/global/ar-DJ.js index e8d43396a23f9..53614895a2c19 100644 --- a/packages/common/locales/global/ar-DJ.js +++ b/packages/common/locales/global/ar-DJ.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-DZ.js b/packages/common/locales/global/ar-DZ.js index 0177a246858a2..1eec6b1979576 100644 --- a/packages/common/locales/global/ar-DZ.js +++ b/packages/common/locales/global/ar-DZ.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-EG.js b/packages/common/locales/global/ar-EG.js index 974b5c95bc277..46859d63e3cd1 100644 --- a/packages/common/locales/global/ar-EG.js +++ b/packages/common/locales/global/ar-EG.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-EH.js b/packages/common/locales/global/ar-EH.js index fa2269483f6cd..daf650c6f01c7 100644 --- a/packages/common/locales/global/ar-EH.js +++ b/packages/common/locales/global/ar-EH.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-ER.js b/packages/common/locales/global/ar-ER.js index feacf13a10bb1..eab142ba56c19 100644 --- a/packages/common/locales/global/ar-ER.js +++ b/packages/common/locales/global/ar-ER.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-IL.js b/packages/common/locales/global/ar-IL.js index c8c839612eeda..b0d8526cf0b6e 100644 --- a/packages/common/locales/global/ar-IL.js +++ b/packages/common/locales/global/ar-IL.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-IQ.js b/packages/common/locales/global/ar-IQ.js index 9e8ce9047f9c5..5c93d987280f1 100644 --- a/packages/common/locales/global/ar-IQ.js +++ b/packages/common/locales/global/ar-IQ.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-JO.js b/packages/common/locales/global/ar-JO.js index df04d870be6d9..d5fd78159abb2 100644 --- a/packages/common/locales/global/ar-JO.js +++ b/packages/common/locales/global/ar-JO.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-KM.js b/packages/common/locales/global/ar-KM.js index 62150af6f4ebb..8d95e74be034d 100644 --- a/packages/common/locales/global/ar-KM.js +++ b/packages/common/locales/global/ar-KM.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-KW.js b/packages/common/locales/global/ar-KW.js index b0f491938656e..c781df26a1577 100644 --- a/packages/common/locales/global/ar-KW.js +++ b/packages/common/locales/global/ar-KW.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-LB.js b/packages/common/locales/global/ar-LB.js index 6d808580ff7b8..d3761299bf578 100644 --- a/packages/common/locales/global/ar-LB.js +++ b/packages/common/locales/global/ar-LB.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-LY.js b/packages/common/locales/global/ar-LY.js index 3493caa11bb24..e6970e4ce1aee 100644 --- a/packages/common/locales/global/ar-LY.js +++ b/packages/common/locales/global/ar-LY.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-MA.js b/packages/common/locales/global/ar-MA.js index 1147e34b1135c..0bceb9db7ece5 100644 --- a/packages/common/locales/global/ar-MA.js +++ b/packages/common/locales/global/ar-MA.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-MR.js b/packages/common/locales/global/ar-MR.js index 1aabaeb4a6c3c..be90d064767e3 100644 --- a/packages/common/locales/global/ar-MR.js +++ b/packages/common/locales/global/ar-MR.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-OM.js b/packages/common/locales/global/ar-OM.js index fae36199b8e88..1014d858d0710 100644 --- a/packages/common/locales/global/ar-OM.js +++ b/packages/common/locales/global/ar-OM.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-PS.js b/packages/common/locales/global/ar-PS.js index 4d26adce61102..ad28e1b41b2df 100644 --- a/packages/common/locales/global/ar-PS.js +++ b/packages/common/locales/global/ar-PS.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-QA.js b/packages/common/locales/global/ar-QA.js index d2eab7bbeadee..59f7075db98ac 100644 --- a/packages/common/locales/global/ar-QA.js +++ b/packages/common/locales/global/ar-QA.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-SA.js b/packages/common/locales/global/ar-SA.js index c1b189ca85237..f23111056fe9d 100644 --- a/packages/common/locales/global/ar-SA.js +++ b/packages/common/locales/global/ar-SA.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-SD.js b/packages/common/locales/global/ar-SD.js index 6b7060f6a71d9..920139b554ebf 100644 --- a/packages/common/locales/global/ar-SD.js +++ b/packages/common/locales/global/ar-SD.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-SO.js b/packages/common/locales/global/ar-SO.js index 489a94418f615..51a3ab99f418d 100644 --- a/packages/common/locales/global/ar-SO.js +++ b/packages/common/locales/global/ar-SO.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-SS.js b/packages/common/locales/global/ar-SS.js index 007be5fddb78e..2fd060efd6dc1 100644 --- a/packages/common/locales/global/ar-SS.js +++ b/packages/common/locales/global/ar-SS.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-SY.js b/packages/common/locales/global/ar-SY.js index d800ae164504e..1bf501363e95f 100644 --- a/packages/common/locales/global/ar-SY.js +++ b/packages/common/locales/global/ar-SY.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-TD.js b/packages/common/locales/global/ar-TD.js index 2ae8956f318b3..f21b155096988 100644 --- a/packages/common/locales/global/ar-TD.js +++ b/packages/common/locales/global/ar-TD.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-TN.js b/packages/common/locales/global/ar-TN.js index a139f1aa4861f..da5dae7613932 100644 --- a/packages/common/locales/global/ar-TN.js +++ b/packages/common/locales/global/ar-TN.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar-YE.js b/packages/common/locales/global/ar-YE.js index a424bfeec63d5..6c9b815486cf9 100644 --- a/packages/common/locales/global/ar-YE.js +++ b/packages/common/locales/global/ar-YE.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ar.js b/packages/common/locales/global/ar.js index a3b36fc52c1c8..b65f325b52645 100644 --- a/packages/common/locales/global/ar.js +++ b/packages/common/locales/global/ar.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/as.js b/packages/common/locales/global/as.js index 9ee490fd81d6a..dd8aa3d57048b 100644 --- a/packages/common/locales/global/as.js +++ b/packages/common/locales/global/as.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/asa.js b/packages/common/locales/global/asa.js index fbb1bfeb4eee3..21911ea9ab8a9 100644 --- a/packages/common/locales/global/asa.js +++ b/packages/common/locales/global/asa.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ast.js b/packages/common/locales/global/ast.js index 0a9c664f00490..3de7e90594274 100644 --- a/packages/common/locales/global/ast.js +++ b/packages/common/locales/global/ast.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/az-Cyrl.js b/packages/common/locales/global/az-Cyrl.js index b89692d60fedc..4ca3c74bcc4dc 100644 --- a/packages/common/locales/global/az-Cyrl.js +++ b/packages/common/locales/global/az-Cyrl.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['az-cyrl'] = [ 'az-Cyrl', diff --git a/packages/common/locales/global/az-Latn.js b/packages/common/locales/global/az-Latn.js index c001130b9e71f..e0ce8088de3f2 100644 --- a/packages/common/locales/global/az-Latn.js +++ b/packages/common/locales/global/az-Latn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/az.js b/packages/common/locales/global/az.js index 334cd9dd20cb9..1a12802797ae2 100644 --- a/packages/common/locales/global/az.js +++ b/packages/common/locales/global/az.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/bas.js b/packages/common/locales/global/bas.js index aceccdc63c73f..1c6ed80228cd3 100644 --- a/packages/common/locales/global/bas.js +++ b/packages/common/locales/global/bas.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['bas'] = [ 'bas', diff --git a/packages/common/locales/global/be.js b/packages/common/locales/global/be.js index 618da2dbffa66..40050693d93e7 100644 --- a/packages/common/locales/global/be.js +++ b/packages/common/locales/global/be.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n % 10 === 1 && !(n % 100 === 11)) return 1; if (n % 10 === Math.floor(n % 10) && n % 10 >= 2 && n % 10 <= 4 && diff --git a/packages/common/locales/global/bem.js b/packages/common/locales/global/bem.js index 55d87dbeef4c2..1de04e1f2c4fe 100644 --- a/packages/common/locales/global/bem.js +++ b/packages/common/locales/global/bem.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/bez.js b/packages/common/locales/global/bez.js index 10172145da912..edbaf6cce320e 100644 --- a/packages/common/locales/global/bez.js +++ b/packages/common/locales/global/bez.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/bg.js b/packages/common/locales/global/bg.js index e5bb8ce111b43..fab3b0d9f63fd 100644 --- a/packages/common/locales/global/bg.js +++ b/packages/common/locales/global/bg.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/bm.js b/packages/common/locales/global/bm.js index 72375841f7cfd..eadad1af0e676 100644 --- a/packages/common/locales/global/bm.js +++ b/packages/common/locales/global/bm.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['bm'] = [ 'bm', diff --git a/packages/common/locales/global/bn-IN.js b/packages/common/locales/global/bn-IN.js index b0acd076849d3..389895fe77311 100644 --- a/packages/common/locales/global/bn-IN.js +++ b/packages/common/locales/global/bn-IN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/bn.js b/packages/common/locales/global/bn.js index feecae00052bc..a5153fa4c0ee7 100644 --- a/packages/common/locales/global/bn.js +++ b/packages/common/locales/global/bn.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/bo-IN.js b/packages/common/locales/global/bo-IN.js index e72f00df2af2e..53bfabab61a3f 100644 --- a/packages/common/locales/global/bo-IN.js +++ b/packages/common/locales/global/bo-IN.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['bo-in'] = [ 'bo-IN', diff --git a/packages/common/locales/global/bo.js b/packages/common/locales/global/bo.js index 9eef9ff0c34f3..15084c7a55b14 100644 --- a/packages/common/locales/global/bo.js +++ b/packages/common/locales/global/bo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['bo'] = [ 'bo', diff --git a/packages/common/locales/global/br.js b/packages/common/locales/global/br.js index 42bc652c2e454..cb7a420b113b1 100644 --- a/packages/common/locales/global/br.js +++ b/packages/common/locales/global/br.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n % 10 === 1 && !(n % 100 === 11 || n % 100 === 71 || n % 100 === 91)) return 1; if (n % 10 === 2 && !(n % 100 === 12 || n % 100 === 72 || n % 100 === 92)) return 2; diff --git a/packages/common/locales/global/brx.js b/packages/common/locales/global/brx.js index 80445432a8120..9b13ab4219f39 100644 --- a/packages/common/locales/global/brx.js +++ b/packages/common/locales/global/brx.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/bs-Cyrl.js b/packages/common/locales/global/bs-Cyrl.js index 224c86112c393..095e60c3a6929 100644 --- a/packages/common/locales/global/bs-Cyrl.js +++ b/packages/common/locales/global/bs-Cyrl.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['bs-cyrl'] = [ 'bs-Cyrl', diff --git a/packages/common/locales/global/bs-Latn.js b/packages/common/locales/global/bs-Latn.js index d53e41a4e5ae4..7aba924f48929 100644 --- a/packages/common/locales/global/bs-Latn.js +++ b/packages/common/locales/global/bs-Latn.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; diff --git a/packages/common/locales/global/bs.js b/packages/common/locales/global/bs.js index fac88cea2feb9..c72ed50e2b99d 100644 --- a/packages/common/locales/global/bs.js +++ b/packages/common/locales/global/bs.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; diff --git a/packages/common/locales/global/ca-AD.js b/packages/common/locales/global/ca-AD.js index 2ba18a2495476..e2981f16c99fa 100644 --- a/packages/common/locales/global/ca-AD.js +++ b/packages/common/locales/global/ca-AD.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ca-ES-VALENCIA.js b/packages/common/locales/global/ca-ES-VALENCIA.js index 4a7def3856714..ce3a023e2a0f9 100644 --- a/packages/common/locales/global/ca-ES-VALENCIA.js +++ b/packages/common/locales/global/ca-ES-VALENCIA.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ca-FR.js b/packages/common/locales/global/ca-FR.js index c3a416eaec4f6..660abfab6b5c2 100644 --- a/packages/common/locales/global/ca-FR.js +++ b/packages/common/locales/global/ca-FR.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ca-IT.js b/packages/common/locales/global/ca-IT.js index 81c1a67557cfa..4dd3f9c2f2931 100644 --- a/packages/common/locales/global/ca-IT.js +++ b/packages/common/locales/global/ca-IT.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ca.js b/packages/common/locales/global/ca.js index 47a968af35cf9..6ed04c1a78983 100644 --- a/packages/common/locales/global/ca.js +++ b/packages/common/locales/global/ca.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ccp-IN.js b/packages/common/locales/global/ccp-IN.js index edc58143559bf..babc86723bd9b 100644 --- a/packages/common/locales/global/ccp-IN.js +++ b/packages/common/locales/global/ccp-IN.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ccp-in'] = [ 'ccp-IN', diff --git a/packages/common/locales/global/ccp.js b/packages/common/locales/global/ccp.js index 658bae826e89a..5bd0feb50f90e 100644 --- a/packages/common/locales/global/ccp.js +++ b/packages/common/locales/global/ccp.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ccp'] = [ 'ccp', diff --git a/packages/common/locales/global/ce.js b/packages/common/locales/global/ce.js index cffb72067302b..6e3b8962c6195 100644 --- a/packages/common/locales/global/ce.js +++ b/packages/common/locales/global/ce.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ceb.js b/packages/common/locales/global/ceb.js index 22a96dbfee848..737eea647a728 100644 --- a/packages/common/locales/global/ceb.js +++ b/packages/common/locales/global/ceb.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ceb'] = [ 'ceb', diff --git a/packages/common/locales/global/cgg.js b/packages/common/locales/global/cgg.js index 4eb7401e232f7..2471a2ea5b2c4 100644 --- a/packages/common/locales/global/cgg.js +++ b/packages/common/locales/global/cgg.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/chr.js b/packages/common/locales/global/chr.js index 761f06bfc4944..f81e889428396 100644 --- a/packages/common/locales/global/chr.js +++ b/packages/common/locales/global/chr.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ckb-IR.js b/packages/common/locales/global/ckb-IR.js index d565245c9f9a1..2860f055e56f3 100644 --- a/packages/common/locales/global/ckb-IR.js +++ b/packages/common/locales/global/ckb-IR.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ckb.js b/packages/common/locales/global/ckb.js index a185b9ecfa042..c232db19a3005 100644 --- a/packages/common/locales/global/ckb.js +++ b/packages/common/locales/global/ckb.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/cs.js b/packages/common/locales/global/cs.js index aa9946508cf53..a09dfa2e3036a 100644 --- a/packages/common/locales/global/cs.js +++ b/packages/common/locales/global/cs.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; if (i === Math.floor(i) && i >= 2 && i <= 4 && v === 0) return 3; if (!(v === 0)) return 4; diff --git a/packages/common/locales/global/cu.js b/packages/common/locales/global/cu.js index 0e413ac445f82..359ecb9fffa59 100644 --- a/packages/common/locales/global/cu.js +++ b/packages/common/locales/global/cu.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['cu'] = [ 'cu', diff --git a/packages/common/locales/global/cy.js b/packages/common/locales/global/cy.js index 0f502e5b9b883..532bac65eaf42 100644 --- a/packages/common/locales/global/cy.js +++ b/packages/common/locales/global/cy.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/da-GL.js b/packages/common/locales/global/da-GL.js index cdf3c8d2731cc..93f35e6ea2245 100644 --- a/packages/common/locales/global/da-GL.js +++ b/packages/common/locales/global/da-GL.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), + var i = Math.floor(Math.abs(n)), t = parseInt(n.toString().replace(/^[^.]*\.?|0+$/g, ''), 10) || 0; if (n === 1 || !(t === 0) && (i === 0 || i === 1)) return 1; return 5; diff --git a/packages/common/locales/global/da.js b/packages/common/locales/global/da.js index 0046ebb7670b0..1dd4851be9583 100644 --- a/packages/common/locales/global/da.js +++ b/packages/common/locales/global/da.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), + var i = Math.floor(Math.abs(n)), t = parseInt(n.toString().replace(/^[^.]*\.?|0+$/g, ''), 10) || 0; if (n === 1 || !(t === 0) && (i === 0 || i === 1)) return 1; return 5; diff --git a/packages/common/locales/global/dav.js b/packages/common/locales/global/dav.js index 8be697e5bdd06..9aec360d245aa 100644 --- a/packages/common/locales/global/dav.js +++ b/packages/common/locales/global/dav.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['dav'] = [ 'dav', diff --git a/packages/common/locales/global/de-AT.js b/packages/common/locales/global/de-AT.js index bf064c7b5cc92..8e87c852c4d05 100644 --- a/packages/common/locales/global/de-AT.js +++ b/packages/common/locales/global/de-AT.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/de-BE.js b/packages/common/locales/global/de-BE.js index b6b6cde58a6ed..65facda5220c9 100644 --- a/packages/common/locales/global/de-BE.js +++ b/packages/common/locales/global/de-BE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/de-CH.js b/packages/common/locales/global/de-CH.js index 7d73241a07c39..1f16eebf6ec2d 100644 --- a/packages/common/locales/global/de-CH.js +++ b/packages/common/locales/global/de-CH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/de-IT.js b/packages/common/locales/global/de-IT.js index b90769e392ce5..d7439e75f4875 100644 --- a/packages/common/locales/global/de-IT.js +++ b/packages/common/locales/global/de-IT.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/de-LI.js b/packages/common/locales/global/de-LI.js index 7d82d5a35d502..b65dc65e6c315 100644 --- a/packages/common/locales/global/de-LI.js +++ b/packages/common/locales/global/de-LI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/de-LU.js b/packages/common/locales/global/de-LU.js index a9e844a0108d5..70c110441e532 100644 --- a/packages/common/locales/global/de-LU.js +++ b/packages/common/locales/global/de-LU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/de.js b/packages/common/locales/global/de.js index 1379bbaacfdc6..f814dda8f512d 100644 --- a/packages/common/locales/global/de.js +++ b/packages/common/locales/global/de.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/dje.js b/packages/common/locales/global/dje.js index 82a7c7ff403a3..b7d5dd6161b7a 100644 --- a/packages/common/locales/global/dje.js +++ b/packages/common/locales/global/dje.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['dje'] = [ 'dje', diff --git a/packages/common/locales/global/dsb.js b/packages/common/locales/global/dsb.js index 736887461d20d..c2706f54f7186 100644 --- a/packages/common/locales/global/dsb.js +++ b/packages/common/locales/global/dsb.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 100 === 1 || f % 100 === 1) return 1; if (v === 0 && i % 100 === 2 || f % 100 === 2) return 2; diff --git a/packages/common/locales/global/dua.js b/packages/common/locales/global/dua.js index b145567edea91..c4bc66472dc9b 100644 --- a/packages/common/locales/global/dua.js +++ b/packages/common/locales/global/dua.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['dua'] = [ 'dua', diff --git a/packages/common/locales/global/dyo.js b/packages/common/locales/global/dyo.js index 80c4e69119dcc..e85f82dd7f485 100644 --- a/packages/common/locales/global/dyo.js +++ b/packages/common/locales/global/dyo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['dyo'] = [ 'dyo', diff --git a/packages/common/locales/global/dz.js b/packages/common/locales/global/dz.js index a7383c10aa97c..9caf02222f0df 100644 --- a/packages/common/locales/global/dz.js +++ b/packages/common/locales/global/dz.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['dz'] = [ 'dz', diff --git a/packages/common/locales/global/ebu.js b/packages/common/locales/global/ebu.js index 3b93c21ef848e..7313d8e4d8790 100644 --- a/packages/common/locales/global/ebu.js +++ b/packages/common/locales/global/ebu.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ebu'] = [ 'ebu', diff --git a/packages/common/locales/global/ee-TG.js b/packages/common/locales/global/ee-TG.js index 29176223bf9a7..e87277ac9c502 100644 --- a/packages/common/locales/global/ee-TG.js +++ b/packages/common/locales/global/ee-TG.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ee.js b/packages/common/locales/global/ee.js index 57c5d0a879e02..1abd568f96ae6 100644 --- a/packages/common/locales/global/ee.js +++ b/packages/common/locales/global/ee.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/el-CY.js b/packages/common/locales/global/el-CY.js index 37f97c384dae0..17d7e4e5d726b 100644 --- a/packages/common/locales/global/el-CY.js +++ b/packages/common/locales/global/el-CY.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/el.js b/packages/common/locales/global/el.js index 1ab2d32cdb835..25489cb6006c9 100644 --- a/packages/common/locales/global/el.js +++ b/packages/common/locales/global/el.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/en-001.js b/packages/common/locales/global/en-001.js index 5b6c65bb389ec..8fd3949e9734d 100644 --- a/packages/common/locales/global/en-001.js +++ b/packages/common/locales/global/en-001.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-150.js b/packages/common/locales/global/en-150.js index 3e9dc483fa300..867145db9d2a7 100644 --- a/packages/common/locales/global/en-150.js +++ b/packages/common/locales/global/en-150.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-AE.js b/packages/common/locales/global/en-AE.js index e6c14527c844b..787d49eafba2c 100644 --- a/packages/common/locales/global/en-AE.js +++ b/packages/common/locales/global/en-AE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-AG.js b/packages/common/locales/global/en-AG.js index 078f168fe542d..6ea845f9d08b2 100644 --- a/packages/common/locales/global/en-AG.js +++ b/packages/common/locales/global/en-AG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-AI.js b/packages/common/locales/global/en-AI.js index 8e1057e1bdcc5..d6e812bf58e56 100644 --- a/packages/common/locales/global/en-AI.js +++ b/packages/common/locales/global/en-AI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-AS.js b/packages/common/locales/global/en-AS.js index 6b288707dd0b7..48244d48c3b2c 100644 --- a/packages/common/locales/global/en-AS.js +++ b/packages/common/locales/global/en-AS.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-AT.js b/packages/common/locales/global/en-AT.js index 765b638621afe..be6c364c5625d 100644 --- a/packages/common/locales/global/en-AT.js +++ b/packages/common/locales/global/en-AT.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-AU.js b/packages/common/locales/global/en-AU.js index 5cc7523db9c3a..8267849b66d7c 100644 --- a/packages/common/locales/global/en-AU.js +++ b/packages/common/locales/global/en-AU.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-BB.js b/packages/common/locales/global/en-BB.js index 3476054b26109..3065020a7b280 100644 --- a/packages/common/locales/global/en-BB.js +++ b/packages/common/locales/global/en-BB.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-BE.js b/packages/common/locales/global/en-BE.js index 0e5e65193d6f7..4390985a6f68d 100644 --- a/packages/common/locales/global/en-BE.js +++ b/packages/common/locales/global/en-BE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-BI.js b/packages/common/locales/global/en-BI.js index 8de64318bb805..90f3e5703b031 100644 --- a/packages/common/locales/global/en-BI.js +++ b/packages/common/locales/global/en-BI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-BM.js b/packages/common/locales/global/en-BM.js index 5b322c8867ea2..5f3f7faca0b79 100644 --- a/packages/common/locales/global/en-BM.js +++ b/packages/common/locales/global/en-BM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-BS.js b/packages/common/locales/global/en-BS.js index 60618313a353d..97b3180b95774 100644 --- a/packages/common/locales/global/en-BS.js +++ b/packages/common/locales/global/en-BS.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-BW.js b/packages/common/locales/global/en-BW.js index 4f4c5a99ab62b..7818fb4b691ab 100644 --- a/packages/common/locales/global/en-BW.js +++ b/packages/common/locales/global/en-BW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-BZ.js b/packages/common/locales/global/en-BZ.js index 64e8e8f066d4c..772d4cb68bcf7 100644 --- a/packages/common/locales/global/en-BZ.js +++ b/packages/common/locales/global/en-BZ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-CA.js b/packages/common/locales/global/en-CA.js index 3c31c19e20d00..6916729dff62e 100644 --- a/packages/common/locales/global/en-CA.js +++ b/packages/common/locales/global/en-CA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-CC.js b/packages/common/locales/global/en-CC.js index 17ee2e3fd80dc..eefcb89a80fce 100644 --- a/packages/common/locales/global/en-CC.js +++ b/packages/common/locales/global/en-CC.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-CH.js b/packages/common/locales/global/en-CH.js index d04abb05fdbc2..bf1a12853edf0 100644 --- a/packages/common/locales/global/en-CH.js +++ b/packages/common/locales/global/en-CH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-CK.js b/packages/common/locales/global/en-CK.js index 9d93825802438..f62a60d017961 100644 --- a/packages/common/locales/global/en-CK.js +++ b/packages/common/locales/global/en-CK.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-CM.js b/packages/common/locales/global/en-CM.js index efcafac9f1cf3..5316414ea92ce 100644 --- a/packages/common/locales/global/en-CM.js +++ b/packages/common/locales/global/en-CM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-CX.js b/packages/common/locales/global/en-CX.js index f62312d48c5d4..e14db9b2118be 100644 --- a/packages/common/locales/global/en-CX.js +++ b/packages/common/locales/global/en-CX.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-CY.js b/packages/common/locales/global/en-CY.js index f2bc8bef4c9d5..d6fcdaec44fa6 100644 --- a/packages/common/locales/global/en-CY.js +++ b/packages/common/locales/global/en-CY.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-DE.js b/packages/common/locales/global/en-DE.js index f80a3d7347c31..e9e4a8dc4f611 100644 --- a/packages/common/locales/global/en-DE.js +++ b/packages/common/locales/global/en-DE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-DG.js b/packages/common/locales/global/en-DG.js index 34256c3fe0ba3..4ba8a399d1868 100644 --- a/packages/common/locales/global/en-DG.js +++ b/packages/common/locales/global/en-DG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-DK.js b/packages/common/locales/global/en-DK.js index 78fa0c61463f0..d961d1850a582 100644 --- a/packages/common/locales/global/en-DK.js +++ b/packages/common/locales/global/en-DK.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-DM.js b/packages/common/locales/global/en-DM.js index c850ef2dc44b2..65db91f4b101f 100644 --- a/packages/common/locales/global/en-DM.js +++ b/packages/common/locales/global/en-DM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-ER.js b/packages/common/locales/global/en-ER.js index 993c85bc39c9b..f409d4a2e1b08 100644 --- a/packages/common/locales/global/en-ER.js +++ b/packages/common/locales/global/en-ER.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-FI.js b/packages/common/locales/global/en-FI.js index ff6c12041b245..c3a167b8c8ab1 100644 --- a/packages/common/locales/global/en-FI.js +++ b/packages/common/locales/global/en-FI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-FJ.js b/packages/common/locales/global/en-FJ.js index a7c0b92fd3268..fa308c4e58dfa 100644 --- a/packages/common/locales/global/en-FJ.js +++ b/packages/common/locales/global/en-FJ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-FK.js b/packages/common/locales/global/en-FK.js index 4af4727e15e12..ba2a20423493d 100644 --- a/packages/common/locales/global/en-FK.js +++ b/packages/common/locales/global/en-FK.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-FM.js b/packages/common/locales/global/en-FM.js index 97c0f85e604e9..8eedaf8013fb0 100644 --- a/packages/common/locales/global/en-FM.js +++ b/packages/common/locales/global/en-FM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-GB.js b/packages/common/locales/global/en-GB.js index c174fd35d4141..0ea0f2ba0e648 100644 --- a/packages/common/locales/global/en-GB.js +++ b/packages/common/locales/global/en-GB.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-GD.js b/packages/common/locales/global/en-GD.js index 4eb58a1b49bee..bfe1243d68dd9 100644 --- a/packages/common/locales/global/en-GD.js +++ b/packages/common/locales/global/en-GD.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-GG.js b/packages/common/locales/global/en-GG.js index 4ad6f137f5552..10ccd23bad5b9 100644 --- a/packages/common/locales/global/en-GG.js +++ b/packages/common/locales/global/en-GG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-GH.js b/packages/common/locales/global/en-GH.js index e7e30f5ad72a6..b5616663b7d3a 100644 --- a/packages/common/locales/global/en-GH.js +++ b/packages/common/locales/global/en-GH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-GI.js b/packages/common/locales/global/en-GI.js index f37963fb4cce6..869406aeb4d97 100644 --- a/packages/common/locales/global/en-GI.js +++ b/packages/common/locales/global/en-GI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-GM.js b/packages/common/locales/global/en-GM.js index eb6316c2d5048..7c95f7b983586 100644 --- a/packages/common/locales/global/en-GM.js +++ b/packages/common/locales/global/en-GM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-GU.js b/packages/common/locales/global/en-GU.js index 6a58f2de19f37..df95318b37364 100644 --- a/packages/common/locales/global/en-GU.js +++ b/packages/common/locales/global/en-GU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-GY.js b/packages/common/locales/global/en-GY.js index 9293f01435e30..3f3ace051246e 100644 --- a/packages/common/locales/global/en-GY.js +++ b/packages/common/locales/global/en-GY.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-HK.js b/packages/common/locales/global/en-HK.js index bb3c267a7ff33..40c633851a6ca 100644 --- a/packages/common/locales/global/en-HK.js +++ b/packages/common/locales/global/en-HK.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-IE.js b/packages/common/locales/global/en-IE.js index ebbfdbabbbe9b..635ea996ada0e 100644 --- a/packages/common/locales/global/en-IE.js +++ b/packages/common/locales/global/en-IE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-IL.js b/packages/common/locales/global/en-IL.js index 08f452f6d2a8a..a209031d17282 100644 --- a/packages/common/locales/global/en-IL.js +++ b/packages/common/locales/global/en-IL.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-IM.js b/packages/common/locales/global/en-IM.js index f7eb39104033f..bd8451132ee8b 100644 --- a/packages/common/locales/global/en-IM.js +++ b/packages/common/locales/global/en-IM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-IN.js b/packages/common/locales/global/en-IN.js index 931d9864492ce..6a17e174c66e3 100644 --- a/packages/common/locales/global/en-IN.js +++ b/packages/common/locales/global/en-IN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-IO.js b/packages/common/locales/global/en-IO.js index 7be7cb49c55fe..b10813580626d 100644 --- a/packages/common/locales/global/en-IO.js +++ b/packages/common/locales/global/en-IO.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-JE.js b/packages/common/locales/global/en-JE.js index 93c24c5dea30d..3d65847087aa5 100644 --- a/packages/common/locales/global/en-JE.js +++ b/packages/common/locales/global/en-JE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-JM.js b/packages/common/locales/global/en-JM.js index 408a1dd8420f1..ab67b90b091d1 100644 --- a/packages/common/locales/global/en-JM.js +++ b/packages/common/locales/global/en-JM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-KE.js b/packages/common/locales/global/en-KE.js index b050ba9242a75..97b2e0bf02246 100644 --- a/packages/common/locales/global/en-KE.js +++ b/packages/common/locales/global/en-KE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-KI.js b/packages/common/locales/global/en-KI.js index c0033bddba8be..2123207f48349 100644 --- a/packages/common/locales/global/en-KI.js +++ b/packages/common/locales/global/en-KI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-KN.js b/packages/common/locales/global/en-KN.js index 39da662be4b98..8d35e569d5272 100644 --- a/packages/common/locales/global/en-KN.js +++ b/packages/common/locales/global/en-KN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-KY.js b/packages/common/locales/global/en-KY.js index 74499ae2804b2..353fd0ab3ff69 100644 --- a/packages/common/locales/global/en-KY.js +++ b/packages/common/locales/global/en-KY.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-LC.js b/packages/common/locales/global/en-LC.js index fe549aff2cdba..8e47aedcd3e99 100644 --- a/packages/common/locales/global/en-LC.js +++ b/packages/common/locales/global/en-LC.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-LR.js b/packages/common/locales/global/en-LR.js index 3890a885cac82..c449b257b0f67 100644 --- a/packages/common/locales/global/en-LR.js +++ b/packages/common/locales/global/en-LR.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-LS.js b/packages/common/locales/global/en-LS.js index 4e28db09a9b8e..9b7e006b98dfe 100644 --- a/packages/common/locales/global/en-LS.js +++ b/packages/common/locales/global/en-LS.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MG.js b/packages/common/locales/global/en-MG.js index ca6ce4e4d6cfd..5fe68b25420cc 100644 --- a/packages/common/locales/global/en-MG.js +++ b/packages/common/locales/global/en-MG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MH.js b/packages/common/locales/global/en-MH.js index 499e8a6bd5cb0..26a41b4cb90d4 100644 --- a/packages/common/locales/global/en-MH.js +++ b/packages/common/locales/global/en-MH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MO.js b/packages/common/locales/global/en-MO.js index 4a7457d478859..6145393a5c62e 100644 --- a/packages/common/locales/global/en-MO.js +++ b/packages/common/locales/global/en-MO.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MP.js b/packages/common/locales/global/en-MP.js index 8afe11c18e7ea..74fe1fb9f6de5 100644 --- a/packages/common/locales/global/en-MP.js +++ b/packages/common/locales/global/en-MP.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MS.js b/packages/common/locales/global/en-MS.js index eefe032f5ab2b..ab0b23eb3178e 100644 --- a/packages/common/locales/global/en-MS.js +++ b/packages/common/locales/global/en-MS.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MT.js b/packages/common/locales/global/en-MT.js index 4e88841ac26c3..cc5092755a476 100644 --- a/packages/common/locales/global/en-MT.js +++ b/packages/common/locales/global/en-MT.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MU.js b/packages/common/locales/global/en-MU.js index 0c5f51a0a0a10..dab8c42b541b5 100644 --- a/packages/common/locales/global/en-MU.js +++ b/packages/common/locales/global/en-MU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MW.js b/packages/common/locales/global/en-MW.js index d936b75bf63e8..c2862d90922ea 100644 --- a/packages/common/locales/global/en-MW.js +++ b/packages/common/locales/global/en-MW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-MY.js b/packages/common/locales/global/en-MY.js index 3102a86043af3..d47adfb7bf329 100644 --- a/packages/common/locales/global/en-MY.js +++ b/packages/common/locales/global/en-MY.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-NA.js b/packages/common/locales/global/en-NA.js index dee551b0c88cd..48d735c09dd31 100644 --- a/packages/common/locales/global/en-NA.js +++ b/packages/common/locales/global/en-NA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-NF.js b/packages/common/locales/global/en-NF.js index 1cce502afa797..065515038b946 100644 --- a/packages/common/locales/global/en-NF.js +++ b/packages/common/locales/global/en-NF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-NG.js b/packages/common/locales/global/en-NG.js index 2d339ef24bb11..bef60e4bcd346 100644 --- a/packages/common/locales/global/en-NG.js +++ b/packages/common/locales/global/en-NG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-NL.js b/packages/common/locales/global/en-NL.js index fcde4a243c3ad..e7f73520b87d0 100644 --- a/packages/common/locales/global/en-NL.js +++ b/packages/common/locales/global/en-NL.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-NR.js b/packages/common/locales/global/en-NR.js index b6f9732968181..c86c1b095cb43 100644 --- a/packages/common/locales/global/en-NR.js +++ b/packages/common/locales/global/en-NR.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-NU.js b/packages/common/locales/global/en-NU.js index 466ef028a79a6..bf03bc8508838 100644 --- a/packages/common/locales/global/en-NU.js +++ b/packages/common/locales/global/en-NU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-NZ.js b/packages/common/locales/global/en-NZ.js index 15229a37a5fbf..218a2846ee610 100644 --- a/packages/common/locales/global/en-NZ.js +++ b/packages/common/locales/global/en-NZ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-PG.js b/packages/common/locales/global/en-PG.js index ff6edcad39135..414f957be97db 100644 --- a/packages/common/locales/global/en-PG.js +++ b/packages/common/locales/global/en-PG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-PH.js b/packages/common/locales/global/en-PH.js index 38c481b481b88..4a04e833dbee4 100644 --- a/packages/common/locales/global/en-PH.js +++ b/packages/common/locales/global/en-PH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-PK.js b/packages/common/locales/global/en-PK.js index a1526e78b51ce..1e79df18af4a9 100644 --- a/packages/common/locales/global/en-PK.js +++ b/packages/common/locales/global/en-PK.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-PN.js b/packages/common/locales/global/en-PN.js index c8fa0348dd530..cc3d71c036b26 100644 --- a/packages/common/locales/global/en-PN.js +++ b/packages/common/locales/global/en-PN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-PR.js b/packages/common/locales/global/en-PR.js index ca573c7075cc3..2113a349db707 100644 --- a/packages/common/locales/global/en-PR.js +++ b/packages/common/locales/global/en-PR.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-PW.js b/packages/common/locales/global/en-PW.js index 0de86f3573489..4e1f90d25d387 100644 --- a/packages/common/locales/global/en-PW.js +++ b/packages/common/locales/global/en-PW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-RW.js b/packages/common/locales/global/en-RW.js index 12a60eb6cc301..c33b44b50212e 100644 --- a/packages/common/locales/global/en-RW.js +++ b/packages/common/locales/global/en-RW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SB.js b/packages/common/locales/global/en-SB.js index d4a5f27874edd..412a512364c4b 100644 --- a/packages/common/locales/global/en-SB.js +++ b/packages/common/locales/global/en-SB.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SC.js b/packages/common/locales/global/en-SC.js index 6b25fd1bbd480..b2cbc120e612b 100644 --- a/packages/common/locales/global/en-SC.js +++ b/packages/common/locales/global/en-SC.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SD.js b/packages/common/locales/global/en-SD.js index ae72a66cecda4..69c22cb2ac49e 100644 --- a/packages/common/locales/global/en-SD.js +++ b/packages/common/locales/global/en-SD.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SE.js b/packages/common/locales/global/en-SE.js index eddb4e1578511..a764302c3f480 100644 --- a/packages/common/locales/global/en-SE.js +++ b/packages/common/locales/global/en-SE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SG.js b/packages/common/locales/global/en-SG.js index 8e9de3572bdce..2b37772595a57 100644 --- a/packages/common/locales/global/en-SG.js +++ b/packages/common/locales/global/en-SG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SH.js b/packages/common/locales/global/en-SH.js index c66dbb29a97d7..479bbf4ff9fdb 100644 --- a/packages/common/locales/global/en-SH.js +++ b/packages/common/locales/global/en-SH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SI.js b/packages/common/locales/global/en-SI.js index 4aad71c3f9002..f73435935b980 100644 --- a/packages/common/locales/global/en-SI.js +++ b/packages/common/locales/global/en-SI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SL.js b/packages/common/locales/global/en-SL.js index 2ee48417ad5a4..118586b56652b 100644 --- a/packages/common/locales/global/en-SL.js +++ b/packages/common/locales/global/en-SL.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SS.js b/packages/common/locales/global/en-SS.js index 12202ac75a072..136bb1372406d 100644 --- a/packages/common/locales/global/en-SS.js +++ b/packages/common/locales/global/en-SS.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SX.js b/packages/common/locales/global/en-SX.js index 25b0237a2f365..c58b46b98b89e 100644 --- a/packages/common/locales/global/en-SX.js +++ b/packages/common/locales/global/en-SX.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-SZ.js b/packages/common/locales/global/en-SZ.js index 755d49edad513..dfbe74fd8dd22 100644 --- a/packages/common/locales/global/en-SZ.js +++ b/packages/common/locales/global/en-SZ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-TC.js b/packages/common/locales/global/en-TC.js index 4615c0ee70148..26cebc4138e57 100644 --- a/packages/common/locales/global/en-TC.js +++ b/packages/common/locales/global/en-TC.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-TK.js b/packages/common/locales/global/en-TK.js index 4e02f43a3de15..fe64bad456b92 100644 --- a/packages/common/locales/global/en-TK.js +++ b/packages/common/locales/global/en-TK.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-TO.js b/packages/common/locales/global/en-TO.js index 4bc3f20af9dbc..2af3d9f0c208f 100644 --- a/packages/common/locales/global/en-TO.js +++ b/packages/common/locales/global/en-TO.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-TT.js b/packages/common/locales/global/en-TT.js index 7197069cd3ebe..991d9427c5792 100644 --- a/packages/common/locales/global/en-TT.js +++ b/packages/common/locales/global/en-TT.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-TV.js b/packages/common/locales/global/en-TV.js index cb6ce847ec980..ef8d73190d9d8 100644 --- a/packages/common/locales/global/en-TV.js +++ b/packages/common/locales/global/en-TV.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-TZ.js b/packages/common/locales/global/en-TZ.js index 67094aecbe760..f5bc5c77dd727 100644 --- a/packages/common/locales/global/en-TZ.js +++ b/packages/common/locales/global/en-TZ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-UG.js b/packages/common/locales/global/en-UG.js index ca7f59b1c003a..2d2e3dfa379a5 100644 --- a/packages/common/locales/global/en-UG.js +++ b/packages/common/locales/global/en-UG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-UM.js b/packages/common/locales/global/en-UM.js index 382278a1663f7..535af1eef1bb5 100644 --- a/packages/common/locales/global/en-UM.js +++ b/packages/common/locales/global/en-UM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-US-POSIX.js b/packages/common/locales/global/en-US-POSIX.js index 228879be926af..7688ba7b621bf 100644 --- a/packages/common/locales/global/en-US-POSIX.js +++ b/packages/common/locales/global/en-US-POSIX.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-VC.js b/packages/common/locales/global/en-VC.js index a436135deb71c..2b18cf2b41137 100644 --- a/packages/common/locales/global/en-VC.js +++ b/packages/common/locales/global/en-VC.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-VG.js b/packages/common/locales/global/en-VG.js index 3b3ddb8d2bb5f..f174fff3b5f03 100644 --- a/packages/common/locales/global/en-VG.js +++ b/packages/common/locales/global/en-VG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-VI.js b/packages/common/locales/global/en-VI.js index 84f9c115e18cf..dc8e37ef7a14c 100644 --- a/packages/common/locales/global/en-VI.js +++ b/packages/common/locales/global/en-VI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-VU.js b/packages/common/locales/global/en-VU.js index 65232c1f5b09b..ce62bd1589613 100644 --- a/packages/common/locales/global/en-VU.js +++ b/packages/common/locales/global/en-VU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-WS.js b/packages/common/locales/global/en-WS.js index 3b0d4be8d2f6f..27ae7e44f43a1 100644 --- a/packages/common/locales/global/en-WS.js +++ b/packages/common/locales/global/en-WS.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-ZA.js b/packages/common/locales/global/en-ZA.js index f9e330d69af77..fe1a34f07f036 100644 --- a/packages/common/locales/global/en-ZA.js +++ b/packages/common/locales/global/en-ZA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-ZM.js b/packages/common/locales/global/en-ZM.js index 75ecaa50ed329..a9582c2e38a6a 100644 --- a/packages/common/locales/global/en-ZM.js +++ b/packages/common/locales/global/en-ZM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en-ZW.js b/packages/common/locales/global/en-ZW.js index f408a964041cc..66b6f0456e4de 100644 --- a/packages/common/locales/global/en-ZW.js +++ b/packages/common/locales/global/en-ZW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/en.js b/packages/common/locales/global/en.js index e4f3e8830a63a..544e78f017cf5 100644 --- a/packages/common/locales/global/en.js +++ b/packages/common/locales/global/en.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/eo.js b/packages/common/locales/global/eo.js index 62e1a1d1eabdf..3031c75833699 100644 --- a/packages/common/locales/global/eo.js +++ b/packages/common/locales/global/eo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-419.js b/packages/common/locales/global/es-419.js index 89716bbf05f03..f2a5ee7db2510 100644 --- a/packages/common/locales/global/es-419.js +++ b/packages/common/locales/global/es-419.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-AR.js b/packages/common/locales/global/es-AR.js index ba96bc05d2efb..45961e3ce78d1 100644 --- a/packages/common/locales/global/es-AR.js +++ b/packages/common/locales/global/es-AR.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-BO.js b/packages/common/locales/global/es-BO.js index 86a30a2c68290..7f39ddf1cc041 100644 --- a/packages/common/locales/global/es-BO.js +++ b/packages/common/locales/global/es-BO.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-BR.js b/packages/common/locales/global/es-BR.js index 65dc00aba85f0..8391fc6e1d408 100644 --- a/packages/common/locales/global/es-BR.js +++ b/packages/common/locales/global/es-BR.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-BZ.js b/packages/common/locales/global/es-BZ.js index ff53cc45c4f68..ac1fa76933e77 100644 --- a/packages/common/locales/global/es-BZ.js +++ b/packages/common/locales/global/es-BZ.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-CL.js b/packages/common/locales/global/es-CL.js index 7ddf2309d3fa2..023dfddc63066 100644 --- a/packages/common/locales/global/es-CL.js +++ b/packages/common/locales/global/es-CL.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-CO.js b/packages/common/locales/global/es-CO.js index c4cc34c392e57..3f4194c632796 100644 --- a/packages/common/locales/global/es-CO.js +++ b/packages/common/locales/global/es-CO.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-CR.js b/packages/common/locales/global/es-CR.js index d16216b8fbd7c..d981452c0bc13 100644 --- a/packages/common/locales/global/es-CR.js +++ b/packages/common/locales/global/es-CR.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-CU.js b/packages/common/locales/global/es-CU.js index c4c4d1dbb96da..6fb30a3dd6acb 100644 --- a/packages/common/locales/global/es-CU.js +++ b/packages/common/locales/global/es-CU.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-DO.js b/packages/common/locales/global/es-DO.js index 88dbf09caf49f..9b3f267f384db 100644 --- a/packages/common/locales/global/es-DO.js +++ b/packages/common/locales/global/es-DO.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-EA.js b/packages/common/locales/global/es-EA.js index ad22653f6db7b..52b35b3921cb8 100644 --- a/packages/common/locales/global/es-EA.js +++ b/packages/common/locales/global/es-EA.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-EC.js b/packages/common/locales/global/es-EC.js index 5f9f691db3ce0..d9353e7447199 100644 --- a/packages/common/locales/global/es-EC.js +++ b/packages/common/locales/global/es-EC.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-GQ.js b/packages/common/locales/global/es-GQ.js index 65d5cfdbfe867..803a813ff7a0e 100644 --- a/packages/common/locales/global/es-GQ.js +++ b/packages/common/locales/global/es-GQ.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-GT.js b/packages/common/locales/global/es-GT.js index 7e3d07e20f38c..b83a21c541f6e 100644 --- a/packages/common/locales/global/es-GT.js +++ b/packages/common/locales/global/es-GT.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-HN.js b/packages/common/locales/global/es-HN.js index 4ad92812efb19..d1b8e3457ac92 100644 --- a/packages/common/locales/global/es-HN.js +++ b/packages/common/locales/global/es-HN.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-IC.js b/packages/common/locales/global/es-IC.js index b8c513415f4f2..5535f02a00753 100644 --- a/packages/common/locales/global/es-IC.js +++ b/packages/common/locales/global/es-IC.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-MX.js b/packages/common/locales/global/es-MX.js index 7f17449cba01e..8dcb6e7787889 100644 --- a/packages/common/locales/global/es-MX.js +++ b/packages/common/locales/global/es-MX.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-NI.js b/packages/common/locales/global/es-NI.js index 392716545700b..c216dfb3ea6c6 100644 --- a/packages/common/locales/global/es-NI.js +++ b/packages/common/locales/global/es-NI.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-PA.js b/packages/common/locales/global/es-PA.js index 15b395b39a7f9..bed4212d9f6a9 100644 --- a/packages/common/locales/global/es-PA.js +++ b/packages/common/locales/global/es-PA.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-PE.js b/packages/common/locales/global/es-PE.js index 1370da88694b7..cfb7331841f2c 100644 --- a/packages/common/locales/global/es-PE.js +++ b/packages/common/locales/global/es-PE.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-PH.js b/packages/common/locales/global/es-PH.js index 2565a82b135b5..8427dc80b4d2a 100644 --- a/packages/common/locales/global/es-PH.js +++ b/packages/common/locales/global/es-PH.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-PR.js b/packages/common/locales/global/es-PR.js index 50bd9b60e35dd..70a0d70847636 100644 --- a/packages/common/locales/global/es-PR.js +++ b/packages/common/locales/global/es-PR.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-PY.js b/packages/common/locales/global/es-PY.js index 1f035aacf1520..b671b83733caa 100644 --- a/packages/common/locales/global/es-PY.js +++ b/packages/common/locales/global/es-PY.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-SV.js b/packages/common/locales/global/es-SV.js index 8cfda5a099d53..82f63a368b9f7 100644 --- a/packages/common/locales/global/es-SV.js +++ b/packages/common/locales/global/es-SV.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-US.js b/packages/common/locales/global/es-US.js index 62048686b5f9e..40372f8a82760 100644 --- a/packages/common/locales/global/es-US.js +++ b/packages/common/locales/global/es-US.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-UY.js b/packages/common/locales/global/es-UY.js index 82e44cfd0dc5c..33f7acfccbe09 100644 --- a/packages/common/locales/global/es-UY.js +++ b/packages/common/locales/global/es-UY.js @@ -14,7 +14,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es-VE.js b/packages/common/locales/global/es-VE.js index 4e247a1ec525b..dedf77c07dbcf 100644 --- a/packages/common/locales/global/es-VE.js +++ b/packages/common/locales/global/es-VE.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/es.js b/packages/common/locales/global/es.js index 403be2819b2e4..559aab8805491 100644 --- a/packages/common/locales/global/es.js +++ b/packages/common/locales/global/es.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/et.js b/packages/common/locales/global/et.js index df789d76d2756..ca8487034bdb8 100644 --- a/packages/common/locales/global/et.js +++ b/packages/common/locales/global/et.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/eu.js b/packages/common/locales/global/eu.js index 9605e1e58fb33..9e0364b86ad6e 100644 --- a/packages/common/locales/global/eu.js +++ b/packages/common/locales/global/eu.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ewo.js b/packages/common/locales/global/ewo.js index e7d6ce2be8940..be06cd5c0ff94 100644 --- a/packages/common/locales/global/ewo.js +++ b/packages/common/locales/global/ewo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ewo'] = [ 'ewo', diff --git a/packages/common/locales/global/fa-AF.js b/packages/common/locales/global/fa-AF.js index d39cdecac6472..da0a228e43d3a 100644 --- a/packages/common/locales/global/fa-AF.js +++ b/packages/common/locales/global/fa-AF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fa.js b/packages/common/locales/global/fa.js index da637bf95295e..021c376a490d4 100644 --- a/packages/common/locales/global/fa.js +++ b/packages/common/locales/global/fa.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-BF.js b/packages/common/locales/global/ff-Latn-BF.js index b2450efe235ba..b723d5890b7a3 100644 --- a/packages/common/locales/global/ff-Latn-BF.js +++ b/packages/common/locales/global/ff-Latn-BF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-CM.js b/packages/common/locales/global/ff-Latn-CM.js index cde16b99e0d0e..7f86c1dfc9b36 100644 --- a/packages/common/locales/global/ff-Latn-CM.js +++ b/packages/common/locales/global/ff-Latn-CM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-GH.js b/packages/common/locales/global/ff-Latn-GH.js index 040449270f837..db526ab7d3018 100644 --- a/packages/common/locales/global/ff-Latn-GH.js +++ b/packages/common/locales/global/ff-Latn-GH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-GM.js b/packages/common/locales/global/ff-Latn-GM.js index 7a7c14b48c144..1118933960a14 100644 --- a/packages/common/locales/global/ff-Latn-GM.js +++ b/packages/common/locales/global/ff-Latn-GM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-GN.js b/packages/common/locales/global/ff-Latn-GN.js index e79d8f8fce7ec..dc4621c6e238f 100644 --- a/packages/common/locales/global/ff-Latn-GN.js +++ b/packages/common/locales/global/ff-Latn-GN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-GW.js b/packages/common/locales/global/ff-Latn-GW.js index 143eb1e0ebb24..90553e5b682fe 100644 --- a/packages/common/locales/global/ff-Latn-GW.js +++ b/packages/common/locales/global/ff-Latn-GW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-LR.js b/packages/common/locales/global/ff-Latn-LR.js index 2f7a9abe55474..e3d7b52b15087 100644 --- a/packages/common/locales/global/ff-Latn-LR.js +++ b/packages/common/locales/global/ff-Latn-LR.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-MR.js b/packages/common/locales/global/ff-Latn-MR.js index 0eeddd3ca70d2..366e09d9bfc0b 100644 --- a/packages/common/locales/global/ff-Latn-MR.js +++ b/packages/common/locales/global/ff-Latn-MR.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-NE.js b/packages/common/locales/global/ff-Latn-NE.js index fdd359832568a..0ed91f71640fc 100644 --- a/packages/common/locales/global/ff-Latn-NE.js +++ b/packages/common/locales/global/ff-Latn-NE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-NG.js b/packages/common/locales/global/ff-Latn-NG.js index b3379cd49fced..f39ea71d02048 100644 --- a/packages/common/locales/global/ff-Latn-NG.js +++ b/packages/common/locales/global/ff-Latn-NG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn-SL.js b/packages/common/locales/global/ff-Latn-SL.js index 09635ebc384e7..4c322e5b6641f 100644 --- a/packages/common/locales/global/ff-Latn-SL.js +++ b/packages/common/locales/global/ff-Latn-SL.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff-Latn.js b/packages/common/locales/global/ff-Latn.js index 47a9da1709774..9805c6c963d81 100644 --- a/packages/common/locales/global/ff-Latn.js +++ b/packages/common/locales/global/ff-Latn.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ff.js b/packages/common/locales/global/ff.js index 8255730de33ab..fbd7e9efd63fa 100644 --- a/packages/common/locales/global/ff.js +++ b/packages/common/locales/global/ff.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fi.js b/packages/common/locales/global/fi.js index e6296457d2f24..e4a414dec1bfe 100644 --- a/packages/common/locales/global/fi.js +++ b/packages/common/locales/global/fi.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/fil.js b/packages/common/locales/global/fil.js index cb59ad85c3e70..9b8dc667ef615 100644 --- a/packages/common/locales/global/fil.js +++ b/packages/common/locales/global/fil.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && (i === 1 || i === 2 || i === 3) || v === 0 && !(i % 10 === 4 || i % 10 === 6 || i % 10 === 9) || diff --git a/packages/common/locales/global/fo-DK.js b/packages/common/locales/global/fo-DK.js index c0f93453f9f8a..f6373174fdae7 100644 --- a/packages/common/locales/global/fo-DK.js +++ b/packages/common/locales/global/fo-DK.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/fo.js b/packages/common/locales/global/fo.js index 809712a01c169..e8902eed84174 100644 --- a/packages/common/locales/global/fo.js +++ b/packages/common/locales/global/fo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/fr-BE.js b/packages/common/locales/global/fr-BE.js index 391249f601a2f..c0f3254eb044d 100644 --- a/packages/common/locales/global/fr-BE.js +++ b/packages/common/locales/global/fr-BE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-BF.js b/packages/common/locales/global/fr-BF.js index 103371a94a161..b0609a3b528bd 100644 --- a/packages/common/locales/global/fr-BF.js +++ b/packages/common/locales/global/fr-BF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-BI.js b/packages/common/locales/global/fr-BI.js index f4311189e2c23..3751f226bda79 100644 --- a/packages/common/locales/global/fr-BI.js +++ b/packages/common/locales/global/fr-BI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-BJ.js b/packages/common/locales/global/fr-BJ.js index 114f6d3bd79ef..e25ccd8fb1b16 100644 --- a/packages/common/locales/global/fr-BJ.js +++ b/packages/common/locales/global/fr-BJ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-BL.js b/packages/common/locales/global/fr-BL.js index a95b8a8190c72..ded4107230e6a 100644 --- a/packages/common/locales/global/fr-BL.js +++ b/packages/common/locales/global/fr-BL.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-CA.js b/packages/common/locales/global/fr-CA.js index 4f1b1e8950b93..f1b5b55af551c 100644 --- a/packages/common/locales/global/fr-CA.js +++ b/packages/common/locales/global/fr-CA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-CD.js b/packages/common/locales/global/fr-CD.js index 3c40538fd666e..40638cef5210f 100644 --- a/packages/common/locales/global/fr-CD.js +++ b/packages/common/locales/global/fr-CD.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-CF.js b/packages/common/locales/global/fr-CF.js index e71ad662378ae..9f703e6c8125b 100644 --- a/packages/common/locales/global/fr-CF.js +++ b/packages/common/locales/global/fr-CF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-CG.js b/packages/common/locales/global/fr-CG.js index f3711b2bde897..44e51b790b994 100644 --- a/packages/common/locales/global/fr-CG.js +++ b/packages/common/locales/global/fr-CG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-CH.js b/packages/common/locales/global/fr-CH.js index 36728bb5a65ca..f96967ab5a4c2 100644 --- a/packages/common/locales/global/fr-CH.js +++ b/packages/common/locales/global/fr-CH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-CI.js b/packages/common/locales/global/fr-CI.js index ac0362af1d825..10aeb8f7055ae 100644 --- a/packages/common/locales/global/fr-CI.js +++ b/packages/common/locales/global/fr-CI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-CM.js b/packages/common/locales/global/fr-CM.js index 8b8372f2cae06..65d849945999c 100644 --- a/packages/common/locales/global/fr-CM.js +++ b/packages/common/locales/global/fr-CM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-DJ.js b/packages/common/locales/global/fr-DJ.js index 09098da1db6c3..222a90992e897 100644 --- a/packages/common/locales/global/fr-DJ.js +++ b/packages/common/locales/global/fr-DJ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-DZ.js b/packages/common/locales/global/fr-DZ.js index 2fdf877eaa850..c8628ab966c3d 100644 --- a/packages/common/locales/global/fr-DZ.js +++ b/packages/common/locales/global/fr-DZ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-GA.js b/packages/common/locales/global/fr-GA.js index 3b3fcd8c28bec..6f0e16f1dc533 100644 --- a/packages/common/locales/global/fr-GA.js +++ b/packages/common/locales/global/fr-GA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-GF.js b/packages/common/locales/global/fr-GF.js index 89284fc2c1c1a..26ad4a6794a26 100644 --- a/packages/common/locales/global/fr-GF.js +++ b/packages/common/locales/global/fr-GF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-GN.js b/packages/common/locales/global/fr-GN.js index 75a480d7a76d6..013bd0546f39f 100644 --- a/packages/common/locales/global/fr-GN.js +++ b/packages/common/locales/global/fr-GN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-GP.js b/packages/common/locales/global/fr-GP.js index e75c673580d43..7066ff121346b 100644 --- a/packages/common/locales/global/fr-GP.js +++ b/packages/common/locales/global/fr-GP.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-GQ.js b/packages/common/locales/global/fr-GQ.js index 11deee8d51e35..05b3ab3e45d64 100644 --- a/packages/common/locales/global/fr-GQ.js +++ b/packages/common/locales/global/fr-GQ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-HT.js b/packages/common/locales/global/fr-HT.js index 1b436b12bd515..ffde1040112ec 100644 --- a/packages/common/locales/global/fr-HT.js +++ b/packages/common/locales/global/fr-HT.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-KM.js b/packages/common/locales/global/fr-KM.js index 4954b27880629..0dce2cadc2eb3 100644 --- a/packages/common/locales/global/fr-KM.js +++ b/packages/common/locales/global/fr-KM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-LU.js b/packages/common/locales/global/fr-LU.js index 4edd776e0524c..1a54b654ac121 100644 --- a/packages/common/locales/global/fr-LU.js +++ b/packages/common/locales/global/fr-LU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-MA.js b/packages/common/locales/global/fr-MA.js index a93d9821fb551..e5a6845cd9436 100644 --- a/packages/common/locales/global/fr-MA.js +++ b/packages/common/locales/global/fr-MA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-MC.js b/packages/common/locales/global/fr-MC.js index ae9bde399d005..8020f51a5e14c 100644 --- a/packages/common/locales/global/fr-MC.js +++ b/packages/common/locales/global/fr-MC.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-MF.js b/packages/common/locales/global/fr-MF.js index df342fd9a64d8..a25c821682ef5 100644 --- a/packages/common/locales/global/fr-MF.js +++ b/packages/common/locales/global/fr-MF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-MG.js b/packages/common/locales/global/fr-MG.js index 72891a999b885..2872552f8279c 100644 --- a/packages/common/locales/global/fr-MG.js +++ b/packages/common/locales/global/fr-MG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-ML.js b/packages/common/locales/global/fr-ML.js index 3eff899b9d514..4ece63c30375a 100644 --- a/packages/common/locales/global/fr-ML.js +++ b/packages/common/locales/global/fr-ML.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-MQ.js b/packages/common/locales/global/fr-MQ.js index 256902f232aba..d5562f91249fc 100644 --- a/packages/common/locales/global/fr-MQ.js +++ b/packages/common/locales/global/fr-MQ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-MR.js b/packages/common/locales/global/fr-MR.js index c90be32699566..715bc7c29f09d 100644 --- a/packages/common/locales/global/fr-MR.js +++ b/packages/common/locales/global/fr-MR.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-MU.js b/packages/common/locales/global/fr-MU.js index 437271ae5136c..38b98b60a33a8 100644 --- a/packages/common/locales/global/fr-MU.js +++ b/packages/common/locales/global/fr-MU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-NC.js b/packages/common/locales/global/fr-NC.js index 622525f99acd0..6287da8063b19 100644 --- a/packages/common/locales/global/fr-NC.js +++ b/packages/common/locales/global/fr-NC.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-NE.js b/packages/common/locales/global/fr-NE.js index d97c8c510c8aa..9a095717eacbd 100644 --- a/packages/common/locales/global/fr-NE.js +++ b/packages/common/locales/global/fr-NE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-PF.js b/packages/common/locales/global/fr-PF.js index 8e18925250f1e..512ddcc0ef036 100644 --- a/packages/common/locales/global/fr-PF.js +++ b/packages/common/locales/global/fr-PF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-PM.js b/packages/common/locales/global/fr-PM.js index 200be437d5d81..e2420c6544a4e 100644 --- a/packages/common/locales/global/fr-PM.js +++ b/packages/common/locales/global/fr-PM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-RE.js b/packages/common/locales/global/fr-RE.js index aac33e3a0d478..5f112c1604e10 100644 --- a/packages/common/locales/global/fr-RE.js +++ b/packages/common/locales/global/fr-RE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-RW.js b/packages/common/locales/global/fr-RW.js index be31eef96fa1e..430bf137acc4e 100644 --- a/packages/common/locales/global/fr-RW.js +++ b/packages/common/locales/global/fr-RW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-SC.js b/packages/common/locales/global/fr-SC.js index 66d1b20a6c215..0fd0b73eb2e5a 100644 --- a/packages/common/locales/global/fr-SC.js +++ b/packages/common/locales/global/fr-SC.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-SN.js b/packages/common/locales/global/fr-SN.js index 7f84ea9704d26..950a4ad89f97d 100644 --- a/packages/common/locales/global/fr-SN.js +++ b/packages/common/locales/global/fr-SN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-SY.js b/packages/common/locales/global/fr-SY.js index 5404df5ce07ca..7dcb9fe2f094e 100644 --- a/packages/common/locales/global/fr-SY.js +++ b/packages/common/locales/global/fr-SY.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-TD.js b/packages/common/locales/global/fr-TD.js index 4c379d8741978..878b33c6d5afd 100644 --- a/packages/common/locales/global/fr-TD.js +++ b/packages/common/locales/global/fr-TD.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-TG.js b/packages/common/locales/global/fr-TG.js index cb3a8a965832f..9fd3865665f73 100644 --- a/packages/common/locales/global/fr-TG.js +++ b/packages/common/locales/global/fr-TG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-TN.js b/packages/common/locales/global/fr-TN.js index 8e58e32880a8a..accfefa05ead0 100644 --- a/packages/common/locales/global/fr-TN.js +++ b/packages/common/locales/global/fr-TN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-VU.js b/packages/common/locales/global/fr-VU.js index c5a6e715b0bdd..1a3c68ec61a7d 100644 --- a/packages/common/locales/global/fr-VU.js +++ b/packages/common/locales/global/fr-VU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-WF.js b/packages/common/locales/global/fr-WF.js index effd70cb60398..37ae6b86750de 100644 --- a/packages/common/locales/global/fr-WF.js +++ b/packages/common/locales/global/fr-WF.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr-YT.js b/packages/common/locales/global/fr-YT.js index 4842c78851699..3e89351346ab9 100644 --- a/packages/common/locales/global/fr-YT.js +++ b/packages/common/locales/global/fr-YT.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fr.js b/packages/common/locales/global/fr.js index 41852a1be6d5c..c7e8b2c8dc1b0 100644 --- a/packages/common/locales/global/fr.js +++ b/packages/common/locales/global/fr.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/fur.js b/packages/common/locales/global/fur.js index 2d2590bb1a6c6..d52a19b2f13b9 100644 --- a/packages/common/locales/global/fur.js +++ b/packages/common/locales/global/fur.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/fy.js b/packages/common/locales/global/fy.js index 0ba917deefcc3..d6d127bcd78a0 100644 --- a/packages/common/locales/global/fy.js +++ b/packages/common/locales/global/fy.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ga-GB.js b/packages/common/locales/global/ga-GB.js index 0deb9569419da..a8d279c30b19b 100644 --- a/packages/common/locales/global/ga-GB.js +++ b/packages/common/locales/global/ga-GB.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 2) return 2; diff --git a/packages/common/locales/global/ga.js b/packages/common/locales/global/ga.js index 222542f3d7e4e..1e87f526d92ac 100644 --- a/packages/common/locales/global/ga.js +++ b/packages/common/locales/global/ga.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 2) return 2; diff --git a/packages/common/locales/global/gd.js b/packages/common/locales/global/gd.js index c809a0e86bec6..467226eec5b69 100644 --- a/packages/common/locales/global/gd.js +++ b/packages/common/locales/global/gd.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1 || n === 11) return 1; if (n === 2 || n === 12) return 2; diff --git a/packages/common/locales/global/gl.js b/packages/common/locales/global/gl.js index befcf63be65f4..7c3687f3874e8 100644 --- a/packages/common/locales/global/gl.js +++ b/packages/common/locales/global/gl.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/gsw-FR.js b/packages/common/locales/global/gsw-FR.js index 51288964b1fe9..ef742f2e078df 100644 --- a/packages/common/locales/global/gsw-FR.js +++ b/packages/common/locales/global/gsw-FR.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/gsw-LI.js b/packages/common/locales/global/gsw-LI.js index 3a1f56fae106a..b2b09e01073ec 100644 --- a/packages/common/locales/global/gsw-LI.js +++ b/packages/common/locales/global/gsw-LI.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/gsw.js b/packages/common/locales/global/gsw.js index 35616f29c0b98..e03051b10b863 100644 --- a/packages/common/locales/global/gsw.js +++ b/packages/common/locales/global/gsw.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/gu.js b/packages/common/locales/global/gu.js index df4d1bacf15b5..ca3a420d4fe30 100644 --- a/packages/common/locales/global/gu.js +++ b/packages/common/locales/global/gu.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/guz.js b/packages/common/locales/global/guz.js index 514c3a6ab6d46..24b7136df207f 100644 --- a/packages/common/locales/global/guz.js +++ b/packages/common/locales/global/guz.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['guz'] = [ 'guz', diff --git a/packages/common/locales/global/gv.js b/packages/common/locales/global/gv.js index ef367547bbcdc..510de7c3522d8 100644 --- a/packages/common/locales/global/gv.js +++ b/packages/common/locales/global/gv.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 10 === 1) return 1; if (v === 0 && i % 10 === 2) return 2; if (v === 0 && diff --git a/packages/common/locales/global/ha-GH.js b/packages/common/locales/global/ha-GH.js index a6a745d9b2081..590f8fa09cb37 100644 --- a/packages/common/locales/global/ha-GH.js +++ b/packages/common/locales/global/ha-GH.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ha-NE.js b/packages/common/locales/global/ha-NE.js index 4c9fb10ae4ce0..5dda225cfa24e 100644 --- a/packages/common/locales/global/ha-NE.js +++ b/packages/common/locales/global/ha-NE.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ha.js b/packages/common/locales/global/ha.js index 4200a66969324..32b7c07d82a6d 100644 --- a/packages/common/locales/global/ha.js +++ b/packages/common/locales/global/ha.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/haw.js b/packages/common/locales/global/haw.js index 22f10d6bd54ff..c7f72dc04053c 100644 --- a/packages/common/locales/global/haw.js +++ b/packages/common/locales/global/haw.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/he.js b/packages/common/locales/global/he.js index d6eee2ce34e41..6b793c8f04750 100644 --- a/packages/common/locales/global/he.js +++ b/packages/common/locales/global/he.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; if (i === 2 && v === 0) return 2; if (v === 0 && !(n >= 0 && n <= 10) && n % 10 === 0) return 4; diff --git a/packages/common/locales/global/hi.js b/packages/common/locales/global/hi.js index ef335a399a020..da5351351f63c 100644 --- a/packages/common/locales/global/hi.js +++ b/packages/common/locales/global/hi.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/hr-BA.js b/packages/common/locales/global/hr-BA.js index 9777b0fe95984..2392c17e60d18 100644 --- a/packages/common/locales/global/hr-BA.js +++ b/packages/common/locales/global/hr-BA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && diff --git a/packages/common/locales/global/hr.js b/packages/common/locales/global/hr.js index 51ca643469efe..54244d0645903 100644 --- a/packages/common/locales/global/hr.js +++ b/packages/common/locales/global/hr.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && diff --git a/packages/common/locales/global/hsb.js b/packages/common/locales/global/hsb.js index 90c97a1ee7aa7..3781817b24b3e 100644 --- a/packages/common/locales/global/hsb.js +++ b/packages/common/locales/global/hsb.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 100 === 1 || f % 100 === 1) return 1; if (v === 0 && i % 100 === 2 || f % 100 === 2) return 2; diff --git a/packages/common/locales/global/hu.js b/packages/common/locales/global/hu.js index 15df0a9c181a2..52a851b421136 100644 --- a/packages/common/locales/global/hu.js +++ b/packages/common/locales/global/hu.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/hy.js b/packages/common/locales/global/hy.js index fac06183abe07..607cf165cad9f 100644 --- a/packages/common/locales/global/hy.js +++ b/packages/common/locales/global/hy.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ia.js b/packages/common/locales/global/ia.js index 101aef6dd2a8b..370a429c3c1f2 100644 --- a/packages/common/locales/global/ia.js +++ b/packages/common/locales/global/ia.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ia'] = [ 'ia', diff --git a/packages/common/locales/global/id.js b/packages/common/locales/global/id.js index 6e380a6de55da..7e8aa13eb0bf8 100644 --- a/packages/common/locales/global/id.js +++ b/packages/common/locales/global/id.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['id'] = [ 'id', diff --git a/packages/common/locales/global/ig.js b/packages/common/locales/global/ig.js index 572c257013860..ceb2690542f66 100644 --- a/packages/common/locales/global/ig.js +++ b/packages/common/locales/global/ig.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ig'] = [ 'ig', diff --git a/packages/common/locales/global/ii.js b/packages/common/locales/global/ii.js index d20f7048862cb..9d396ff693269 100644 --- a/packages/common/locales/global/ii.js +++ b/packages/common/locales/global/ii.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ii'] = [ 'ii', diff --git a/packages/common/locales/global/is.js b/packages/common/locales/global/is.js index 10b8f3f605566..408f85584b83a 100644 --- a/packages/common/locales/global/is.js +++ b/packages/common/locales/global/is.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), + var i = Math.floor(Math.abs(n)), t = parseInt(n.toString().replace(/^[^.]*\.?|0+$/g, ''), 10) || 0; if (t === 0 && i % 10 === 1 && !(i % 100 === 11) || !(t === 0)) return 1; return 5; diff --git a/packages/common/locales/global/it-CH.js b/packages/common/locales/global/it-CH.js index 52d74c13dfdef..bc03f610d6f99 100644 --- a/packages/common/locales/global/it-CH.js +++ b/packages/common/locales/global/it-CH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/it-SM.js b/packages/common/locales/global/it-SM.js index 0e462cce36200..45c4a4d5d4c3d 100644 --- a/packages/common/locales/global/it-SM.js +++ b/packages/common/locales/global/it-SM.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/it-VA.js b/packages/common/locales/global/it-VA.js index 76d23d169fe4f..23c045c40dbb3 100644 --- a/packages/common/locales/global/it-VA.js +++ b/packages/common/locales/global/it-VA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/it.js b/packages/common/locales/global/it.js index 4f0bdb452185d..2ab57e6f59084 100644 --- a/packages/common/locales/global/it.js +++ b/packages/common/locales/global/it.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ja.js b/packages/common/locales/global/ja.js index 94d6f90814a47..a743f680f7273 100644 --- a/packages/common/locales/global/ja.js +++ b/packages/common/locales/global/ja.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ja'] = [ 'ja', diff --git a/packages/common/locales/global/jgo.js b/packages/common/locales/global/jgo.js index c742e0cb5d5c7..c811bc4446375 100644 --- a/packages/common/locales/global/jgo.js +++ b/packages/common/locales/global/jgo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/jmc.js b/packages/common/locales/global/jmc.js index 504f88a41b452..5aba3e2f3cb91 100644 --- a/packages/common/locales/global/jmc.js +++ b/packages/common/locales/global/jmc.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/jv.js b/packages/common/locales/global/jv.js index dd1c37fa1aa5b..b9554151745bc 100644 --- a/packages/common/locales/global/jv.js +++ b/packages/common/locales/global/jv.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['jv'] = [ 'jv', diff --git a/packages/common/locales/global/ka.js b/packages/common/locales/global/ka.js index d3e1892a25625..80744fa4aba48 100644 --- a/packages/common/locales/global/ka.js +++ b/packages/common/locales/global/ka.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/kab.js b/packages/common/locales/global/kab.js index 787cb64ea4f4d..7cfb9c125313f 100644 --- a/packages/common/locales/global/kab.js +++ b/packages/common/locales/global/kab.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || i === 1) return 1; return 5; } diff --git a/packages/common/locales/global/kam.js b/packages/common/locales/global/kam.js index 010a0cad91af8..3c4f32277451b 100644 --- a/packages/common/locales/global/kam.js +++ b/packages/common/locales/global/kam.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['kam'] = [ 'kam', diff --git a/packages/common/locales/global/kde.js b/packages/common/locales/global/kde.js index 6bea04324b0de..4a592adbdd99a 100644 --- a/packages/common/locales/global/kde.js +++ b/packages/common/locales/global/kde.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['kde'] = [ 'kde', diff --git a/packages/common/locales/global/kea.js b/packages/common/locales/global/kea.js index 47e7efa8985fd..ff7f8ce72b601 100644 --- a/packages/common/locales/global/kea.js +++ b/packages/common/locales/global/kea.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['kea'] = [ 'kea', diff --git a/packages/common/locales/global/khq.js b/packages/common/locales/global/khq.js index f4507dce5b444..48c1d6f6a9937 100644 --- a/packages/common/locales/global/khq.js +++ b/packages/common/locales/global/khq.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['khq'] = [ 'khq', diff --git a/packages/common/locales/global/ki.js b/packages/common/locales/global/ki.js index dae343a7667ac..11113135f5447 100644 --- a/packages/common/locales/global/ki.js +++ b/packages/common/locales/global/ki.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ki'] = [ 'ki', diff --git a/packages/common/locales/global/kk.js b/packages/common/locales/global/kk.js index ecdb24f2d6916..77986a9f11e20 100644 --- a/packages/common/locales/global/kk.js +++ b/packages/common/locales/global/kk.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/kkj.js b/packages/common/locales/global/kkj.js index a7e22d62d580a..5ca5c43e1a668 100644 --- a/packages/common/locales/global/kkj.js +++ b/packages/common/locales/global/kkj.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/kl.js b/packages/common/locales/global/kl.js index c0df8b1c780f8..c722b41e3e349 100644 --- a/packages/common/locales/global/kl.js +++ b/packages/common/locales/global/kl.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/kln.js b/packages/common/locales/global/kln.js index 7a6580d8a2726..b0105c29f0f8f 100644 --- a/packages/common/locales/global/kln.js +++ b/packages/common/locales/global/kln.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['kln'] = [ 'kln', diff --git a/packages/common/locales/global/km.js b/packages/common/locales/global/km.js index a5242dd4e25a3..f76e5e68ecb9d 100644 --- a/packages/common/locales/global/km.js +++ b/packages/common/locales/global/km.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['km'] = [ 'km', diff --git a/packages/common/locales/global/kn.js b/packages/common/locales/global/kn.js index aed3529b37eb4..f38e47f728856 100644 --- a/packages/common/locales/global/kn.js +++ b/packages/common/locales/global/kn.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ko-KP.js b/packages/common/locales/global/ko-KP.js index 3dffa782bb1a7..ca80712149500 100644 --- a/packages/common/locales/global/ko-KP.js +++ b/packages/common/locales/global/ko-KP.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ko-kp'] = [ 'ko-KP', diff --git a/packages/common/locales/global/ko.js b/packages/common/locales/global/ko.js index 1283b78118699..a35a38b109d70 100644 --- a/packages/common/locales/global/ko.js +++ b/packages/common/locales/global/ko.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ko'] = [ 'ko', diff --git a/packages/common/locales/global/kok.js b/packages/common/locales/global/kok.js index 1a9f317ebf7cb..81aface29f0b6 100644 --- a/packages/common/locales/global/kok.js +++ b/packages/common/locales/global/kok.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['kok'] = [ 'kok', diff --git a/packages/common/locales/global/ks.js b/packages/common/locales/global/ks.js index b92b49fc944bd..ccbf3bb543f59 100644 --- a/packages/common/locales/global/ks.js +++ b/packages/common/locales/global/ks.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ksb.js b/packages/common/locales/global/ksb.js index dfaaa3b3a50c6..e4c2ae41d46a3 100644 --- a/packages/common/locales/global/ksb.js +++ b/packages/common/locales/global/ksb.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ksf.js b/packages/common/locales/global/ksf.js index 2248835c00f39..c67c4c0a10b3c 100644 --- a/packages/common/locales/global/ksf.js +++ b/packages/common/locales/global/ksf.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ksf'] = [ 'ksf', diff --git a/packages/common/locales/global/ksh.js b/packages/common/locales/global/ksh.js index 755939b7a9d8c..1c587755dfa38 100644 --- a/packages/common/locales/global/ksh.js +++ b/packages/common/locales/global/ksh.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 0) return 0; if (n === 1) return 1; diff --git a/packages/common/locales/global/ku.js b/packages/common/locales/global/ku.js index 2fc30a4848208..9428e846f4abb 100644 --- a/packages/common/locales/global/ku.js +++ b/packages/common/locales/global/ku.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/kw.js b/packages/common/locales/global/kw.js index b55445a906542..f16c1272eb092 100644 --- a/packages/common/locales/global/kw.js +++ b/packages/common/locales/global/kw.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 2) return 2; diff --git a/packages/common/locales/global/ky.js b/packages/common/locales/global/ky.js index fa62c3f68ab6e..25bd6c66310d0 100644 --- a/packages/common/locales/global/ky.js +++ b/packages/common/locales/global/ky.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/lag.js b/packages/common/locales/global/lag.js index f81cdf7edd35f..b32490f0c6643 100644 --- a/packages/common/locales/global/lag.js +++ b/packages/common/locales/global/lag.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (n === 0) return 0; if ((i === 0 || i === 1) && !(n === 0)) return 1; return 5; diff --git a/packages/common/locales/global/lb.js b/packages/common/locales/global/lb.js index 59fdf08faebe0..c543a108abd64 100644 --- a/packages/common/locales/global/lb.js +++ b/packages/common/locales/global/lb.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/lg.js b/packages/common/locales/global/lg.js index e7ac188d99bd6..2ac9a3884f30f 100644 --- a/packages/common/locales/global/lg.js +++ b/packages/common/locales/global/lg.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/lkt.js b/packages/common/locales/global/lkt.js index 52163af6dbc96..34f0884e80f8f 100644 --- a/packages/common/locales/global/lkt.js +++ b/packages/common/locales/global/lkt.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['lkt'] = [ 'lkt', diff --git a/packages/common/locales/global/ln-AO.js b/packages/common/locales/global/ln-AO.js index 09732287e5ee4..4a7a4301ebe87 100644 --- a/packages/common/locales/global/ln-AO.js +++ b/packages/common/locales/global/ln-AO.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/ln-CF.js b/packages/common/locales/global/ln-CF.js index bcead109e5aa9..e6aacf67b36c4 100644 --- a/packages/common/locales/global/ln-CF.js +++ b/packages/common/locales/global/ln-CF.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/ln-CG.js b/packages/common/locales/global/ln-CG.js index 4b8b33eb55854..c8425432a03b4 100644 --- a/packages/common/locales/global/ln-CG.js +++ b/packages/common/locales/global/ln-CG.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/ln.js b/packages/common/locales/global/ln.js index 9fc4e74df0e66..5c036d6b54043 100644 --- a/packages/common/locales/global/ln.js +++ b/packages/common/locales/global/ln.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/lo.js b/packages/common/locales/global/lo.js index ed97e65fe8436..889d6a59b311f 100644 --- a/packages/common/locales/global/lo.js +++ b/packages/common/locales/global/lo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['lo'] = [ 'lo', diff --git a/packages/common/locales/global/lrc-IQ.js b/packages/common/locales/global/lrc-IQ.js index 359477d9e0c7f..30d7898c6f95d 100644 --- a/packages/common/locales/global/lrc-IQ.js +++ b/packages/common/locales/global/lrc-IQ.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['lrc-iq'] = [ 'lrc-IQ', diff --git a/packages/common/locales/global/lrc.js b/packages/common/locales/global/lrc.js index 091746c2ead5e..94a15d761d5d0 100644 --- a/packages/common/locales/global/lrc.js +++ b/packages/common/locales/global/lrc.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['lrc'] = [ 'lrc', diff --git a/packages/common/locales/global/lt.js b/packages/common/locales/global/lt.js index ac683f5183198..d7435afd81dbb 100644 --- a/packages/common/locales/global/lt.js +++ b/packages/common/locales/global/lt.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; + var f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (n % 10 === 1 && !(n % 100 >= 11 && n % 100 <= 19)) return 1; if (n % 10 === Math.floor(n % 10) && n % 10 >= 2 && n % 10 <= 9 && !(n % 100 >= 11 && n % 100 <= 19)) diff --git a/packages/common/locales/global/lu.js b/packages/common/locales/global/lu.js index cb390fc2f6476..cadf639177da4 100644 --- a/packages/common/locales/global/lu.js +++ b/packages/common/locales/global/lu.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['lu'] = [ 'lu', diff --git a/packages/common/locales/global/luo.js b/packages/common/locales/global/luo.js index 920718cede0db..68b96295742ec 100644 --- a/packages/common/locales/global/luo.js +++ b/packages/common/locales/global/luo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['luo'] = [ 'luo', diff --git a/packages/common/locales/global/luy.js b/packages/common/locales/global/luy.js index 2b0eabb48dbe2..ef91edf16083d 100644 --- a/packages/common/locales/global/luy.js +++ b/packages/common/locales/global/luy.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['luy'] = [ 'luy', diff --git a/packages/common/locales/global/lv.js b/packages/common/locales/global/lv.js index d7828a1705907..8e5b418e53326 100644 --- a/packages/common/locales/global/lv.js +++ b/packages/common/locales/global/lv.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let v = n.toString().replace(/^[^.]*\.?/, '').length, + var v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (n % 10 === 0 || n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 19 || v === 2 && f % 100 === Math.floor(f % 100) && f % 100 >= 11 && f % 100 <= 19) diff --git a/packages/common/locales/global/mas-TZ.js b/packages/common/locales/global/mas-TZ.js index 7b75972a5f9ca..fb49169af10df 100644 --- a/packages/common/locales/global/mas-TZ.js +++ b/packages/common/locales/global/mas-TZ.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/mas.js b/packages/common/locales/global/mas.js index ae15b235c500b..d4a55660baf74 100644 --- a/packages/common/locales/global/mas.js +++ b/packages/common/locales/global/mas.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/mer.js b/packages/common/locales/global/mer.js index 315db7e742fe7..8e93b43fd75ab 100644 --- a/packages/common/locales/global/mer.js +++ b/packages/common/locales/global/mer.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['mer'] = [ 'mer', diff --git a/packages/common/locales/global/mfe.js b/packages/common/locales/global/mfe.js index 7e4d5d267cfe9..af0f79416dd49 100644 --- a/packages/common/locales/global/mfe.js +++ b/packages/common/locales/global/mfe.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['mfe'] = [ 'mfe', diff --git a/packages/common/locales/global/mg.js b/packages/common/locales/global/mg.js index 97a35f3152be8..76cfebde3bab8 100644 --- a/packages/common/locales/global/mg.js +++ b/packages/common/locales/global/mg.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/mgh.js b/packages/common/locales/global/mgh.js index 305c34958920c..8b5ef09c8d91e 100644 --- a/packages/common/locales/global/mgh.js +++ b/packages/common/locales/global/mgh.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['mgh'] = [ 'mgh', diff --git a/packages/common/locales/global/mgo.js b/packages/common/locales/global/mgo.js index 01d1a6b6edad5..d8d8d5eb18bbd 100644 --- a/packages/common/locales/global/mgo.js +++ b/packages/common/locales/global/mgo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/mi.js b/packages/common/locales/global/mi.js index 429dfc3e17fee..d91f4ac463652 100644 --- a/packages/common/locales/global/mi.js +++ b/packages/common/locales/global/mi.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['mi'] = [ 'mi', diff --git a/packages/common/locales/global/mk.js b/packages/common/locales/global/mk.js index 4c2a48b1d6a61..1468035ce5004 100644 --- a/packages/common/locales/global/mk.js +++ b/packages/common/locales/global/mk.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; return 5; diff --git a/packages/common/locales/global/ml.js b/packages/common/locales/global/ml.js index a8062bf890da6..6fd9cf5db8e84 100644 --- a/packages/common/locales/global/ml.js +++ b/packages/common/locales/global/ml.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/mn.js b/packages/common/locales/global/mn.js index 392d0ff90d80e..8d9814cc11b99 100644 --- a/packages/common/locales/global/mn.js +++ b/packages/common/locales/global/mn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/mr.js b/packages/common/locales/global/mr.js index 918bc65de8c76..baef20ea52d6d 100644 --- a/packages/common/locales/global/mr.js +++ b/packages/common/locales/global/mr.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/locales/global/ms-BN.js b/packages/common/locales/global/ms-BN.js index eda717c9d5ddd..e5de76a2eb56b 100644 --- a/packages/common/locales/global/ms-BN.js +++ b/packages/common/locales/global/ms-BN.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ms-bn'] = [ 'ms-BN', diff --git a/packages/common/locales/global/ms-SG.js b/packages/common/locales/global/ms-SG.js index 6e47535da499d..749eca2d62f63 100644 --- a/packages/common/locales/global/ms-SG.js +++ b/packages/common/locales/global/ms-SG.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ms-sg'] = [ 'ms-SG', diff --git a/packages/common/locales/global/ms.js b/packages/common/locales/global/ms.js index 9a22871e8dd5c..887e6efd11a33 100644 --- a/packages/common/locales/global/ms.js +++ b/packages/common/locales/global/ms.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ms'] = [ 'ms', diff --git a/packages/common/locales/global/mt.js b/packages/common/locales/global/mt.js index de28fb3c60462..9a1af96c5f747 100644 --- a/packages/common/locales/global/mt.js +++ b/packages/common/locales/global/mt.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 0 || n % 100 === Math.floor(n % 100) && n % 100 >= 2 && n % 100 <= 10) return 3; diff --git a/packages/common/locales/global/mua.js b/packages/common/locales/global/mua.js index afa6520a6958f..dccc53caeaa3f 100644 --- a/packages/common/locales/global/mua.js +++ b/packages/common/locales/global/mua.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['mua'] = [ 'mua', diff --git a/packages/common/locales/global/my.js b/packages/common/locales/global/my.js index 357fe87ae7305..f5e5a361e1971 100644 --- a/packages/common/locales/global/my.js +++ b/packages/common/locales/global/my.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['my'] = [ 'my', diff --git a/packages/common/locales/global/mzn.js b/packages/common/locales/global/mzn.js index bb09a4e8af644..2d7da9552d722 100644 --- a/packages/common/locales/global/mzn.js +++ b/packages/common/locales/global/mzn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['mzn'] = [ 'mzn', diff --git a/packages/common/locales/global/naq.js b/packages/common/locales/global/naq.js index 75aed02896e05..af3fc813d9dba 100644 --- a/packages/common/locales/global/naq.js +++ b/packages/common/locales/global/naq.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 2) return 2; diff --git a/packages/common/locales/global/nb-SJ.js b/packages/common/locales/global/nb-SJ.js index 8318ae5a42bbb..070de2368c525 100644 --- a/packages/common/locales/global/nb-SJ.js +++ b/packages/common/locales/global/nb-SJ.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/nb.js b/packages/common/locales/global/nb.js index 0acc2d7b066f5..32992448a2ff8 100644 --- a/packages/common/locales/global/nb.js +++ b/packages/common/locales/global/nb.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/nd.js b/packages/common/locales/global/nd.js index 29506c7e6ac75..46cbb61a7b0af 100644 --- a/packages/common/locales/global/nd.js +++ b/packages/common/locales/global/nd.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/nds-NL.js b/packages/common/locales/global/nds-NL.js index 90cb3d38fb290..750748b3de245 100644 --- a/packages/common/locales/global/nds-NL.js +++ b/packages/common/locales/global/nds-NL.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['nds-nl'] = [ 'nds-NL', diff --git a/packages/common/locales/global/nds.js b/packages/common/locales/global/nds.js index 79c4b1f13449a..ccd1690daa3e9 100644 --- a/packages/common/locales/global/nds.js +++ b/packages/common/locales/global/nds.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['nds'] = [ 'nds', diff --git a/packages/common/locales/global/ne-IN.js b/packages/common/locales/global/ne-IN.js index 505bdd3360555..78d62b243655a 100644 --- a/packages/common/locales/global/ne-IN.js +++ b/packages/common/locales/global/ne-IN.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ne.js b/packages/common/locales/global/ne.js index 59ea879fd00c6..d31ac419a1f6d 100644 --- a/packages/common/locales/global/ne.js +++ b/packages/common/locales/global/ne.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/nl-AW.js b/packages/common/locales/global/nl-AW.js index 16097d539b53d..c7885eb90c49f 100644 --- a/packages/common/locales/global/nl-AW.js +++ b/packages/common/locales/global/nl-AW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/nl-BE.js b/packages/common/locales/global/nl-BE.js index 20588592b3fb9..8861caa38ec92 100644 --- a/packages/common/locales/global/nl-BE.js +++ b/packages/common/locales/global/nl-BE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/nl-BQ.js b/packages/common/locales/global/nl-BQ.js index 241e1e53d8c9b..77c12f71ca8b6 100644 --- a/packages/common/locales/global/nl-BQ.js +++ b/packages/common/locales/global/nl-BQ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/nl-CW.js b/packages/common/locales/global/nl-CW.js index d11ae5dc3a734..d7816b6761fd8 100644 --- a/packages/common/locales/global/nl-CW.js +++ b/packages/common/locales/global/nl-CW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/nl-SR.js b/packages/common/locales/global/nl-SR.js index 8d5b154012fee..92728e33d5773 100644 --- a/packages/common/locales/global/nl-SR.js +++ b/packages/common/locales/global/nl-SR.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/nl-SX.js b/packages/common/locales/global/nl-SX.js index e8c4c77e83386..eae12e5fd7ca9 100644 --- a/packages/common/locales/global/nl-SX.js +++ b/packages/common/locales/global/nl-SX.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/nl.js b/packages/common/locales/global/nl.js index c4556ce50c096..604ba904caee8 100644 --- a/packages/common/locales/global/nl.js +++ b/packages/common/locales/global/nl.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/nmg.js b/packages/common/locales/global/nmg.js index c0144eb2b8cb6..a2338929e1aad 100644 --- a/packages/common/locales/global/nmg.js +++ b/packages/common/locales/global/nmg.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['nmg'] = [ 'nmg', diff --git a/packages/common/locales/global/nn.js b/packages/common/locales/global/nn.js index 8e8c3b8846ca6..80dd2897f3b14 100644 --- a/packages/common/locales/global/nn.js +++ b/packages/common/locales/global/nn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/nnh.js b/packages/common/locales/global/nnh.js index 3c160fb8992e2..d756bda4754eb 100644 --- a/packages/common/locales/global/nnh.js +++ b/packages/common/locales/global/nnh.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/nus.js b/packages/common/locales/global/nus.js index 4efa858c3a059..83e9f98224908 100644 --- a/packages/common/locales/global/nus.js +++ b/packages/common/locales/global/nus.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['nus'] = [ 'nus', diff --git a/packages/common/locales/global/nyn.js b/packages/common/locales/global/nyn.js index 1d216526a4dba..f88d0f19133f4 100644 --- a/packages/common/locales/global/nyn.js +++ b/packages/common/locales/global/nyn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/om-KE.js b/packages/common/locales/global/om-KE.js index ab9ca6dba3678..0e73e19504ec7 100644 --- a/packages/common/locales/global/om-KE.js +++ b/packages/common/locales/global/om-KE.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/om.js b/packages/common/locales/global/om.js index 3e86164662bce..8745efb4e22e9 100644 --- a/packages/common/locales/global/om.js +++ b/packages/common/locales/global/om.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/or.js b/packages/common/locales/global/or.js index 5c4bda9b8fe78..c2754a9329d97 100644 --- a/packages/common/locales/global/or.js +++ b/packages/common/locales/global/or.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/os-RU.js b/packages/common/locales/global/os-RU.js index fdcf71000e509..4015617984954 100644 --- a/packages/common/locales/global/os-RU.js +++ b/packages/common/locales/global/os-RU.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/os.js b/packages/common/locales/global/os.js index 33e4b2a616bfa..710e3d48f2480 100644 --- a/packages/common/locales/global/os.js +++ b/packages/common/locales/global/os.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/pa-Arab.js b/packages/common/locales/global/pa-Arab.js index b8c51f6edef31..01905ce14c335 100644 --- a/packages/common/locales/global/pa-Arab.js +++ b/packages/common/locales/global/pa-Arab.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['pa-arab'] = [ 'pa-Arab', diff --git a/packages/common/locales/global/pa-Guru.js b/packages/common/locales/global/pa-Guru.js index 134b60e9540a6..ff8e0064a3d04 100644 --- a/packages/common/locales/global/pa-Guru.js +++ b/packages/common/locales/global/pa-Guru.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/pa.js b/packages/common/locales/global/pa.js index c4ef9eb6b31fb..69db152fe2d71 100644 --- a/packages/common/locales/global/pa.js +++ b/packages/common/locales/global/pa.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/pl.js b/packages/common/locales/global/pl.js index 1d8172ceb3da5..acb09c7053774 100644 --- a/packages/common/locales/global/pl.js +++ b/packages/common/locales/global/pl.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && !(i % 100 >= 12 && i % 100 <= 14)) diff --git a/packages/common/locales/global/prg.js b/packages/common/locales/global/prg.js index 484561bb4cd1c..ae10b438d4edc 100644 --- a/packages/common/locales/global/prg.js +++ b/packages/common/locales/global/prg.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let v = n.toString().replace(/^[^.]*\.?/, '').length, + var v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (n % 10 === 0 || n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 19 || v === 2 && f % 100 === Math.floor(f % 100) && f % 100 >= 11 && f % 100 <= 19) diff --git a/packages/common/locales/global/ps-PK.js b/packages/common/locales/global/ps-PK.js index a3761cb53f26a..d049ca367d244 100644 --- a/packages/common/locales/global/ps-PK.js +++ b/packages/common/locales/global/ps-PK.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ps.js b/packages/common/locales/global/ps.js index 7d39173443964..60c37ead86dd4 100644 --- a/packages/common/locales/global/ps.js +++ b/packages/common/locales/global/ps.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/pt-AO.js b/packages/common/locales/global/pt-AO.js index 6ee0d7c8f54fe..85c9ab962be1d 100644 --- a/packages/common/locales/global/pt-AO.js +++ b/packages/common/locales/global/pt-AO.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-CH.js b/packages/common/locales/global/pt-CH.js index 67b22c5970787..e140320450d06 100644 --- a/packages/common/locales/global/pt-CH.js +++ b/packages/common/locales/global/pt-CH.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-CV.js b/packages/common/locales/global/pt-CV.js index 798bf7001618a..4ae0125e406fb 100644 --- a/packages/common/locales/global/pt-CV.js +++ b/packages/common/locales/global/pt-CV.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-GQ.js b/packages/common/locales/global/pt-GQ.js index f17a8fbae7e5c..9a3df826a984c 100644 --- a/packages/common/locales/global/pt-GQ.js +++ b/packages/common/locales/global/pt-GQ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-GW.js b/packages/common/locales/global/pt-GW.js index e9d90527d57d0..0c792ca14fd35 100644 --- a/packages/common/locales/global/pt-GW.js +++ b/packages/common/locales/global/pt-GW.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-LU.js b/packages/common/locales/global/pt-LU.js index 49163460cb666..7b021dcbb643d 100644 --- a/packages/common/locales/global/pt-LU.js +++ b/packages/common/locales/global/pt-LU.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-MO.js b/packages/common/locales/global/pt-MO.js index c9a83a9e2d651..484f7088ed2e5 100644 --- a/packages/common/locales/global/pt-MO.js +++ b/packages/common/locales/global/pt-MO.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-MZ.js b/packages/common/locales/global/pt-MZ.js index a4c6e3d2533c7..27b589c957d31 100644 --- a/packages/common/locales/global/pt-MZ.js +++ b/packages/common/locales/global/pt-MZ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-PT.js b/packages/common/locales/global/pt-PT.js index 554a67d39dae6..57e20ba3d79fa 100644 --- a/packages/common/locales/global/pt-PT.js +++ b/packages/common/locales/global/pt-PT.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-ST.js b/packages/common/locales/global/pt-ST.js index f8ede4438c523..971e5463f5326 100644 --- a/packages/common/locales/global/pt-ST.js +++ b/packages/common/locales/global/pt-ST.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt-TL.js b/packages/common/locales/global/pt-TL.js index 039e8e1ba4a63..cc7800f1ca7da 100644 --- a/packages/common/locales/global/pt-TL.js +++ b/packages/common/locales/global/pt-TL.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/pt.js b/packages/common/locales/global/pt.js index 97e269601a29d..438dd6b880473 100644 --- a/packages/common/locales/global/pt.js +++ b/packages/common/locales/global/pt.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === Math.floor(i) && i >= 0 && i <= 1) return 1; return 5; } diff --git a/packages/common/locales/global/qu-BO.js b/packages/common/locales/global/qu-BO.js index 27f69478c12da..694ba844b00e9 100644 --- a/packages/common/locales/global/qu-BO.js +++ b/packages/common/locales/global/qu-BO.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['qu-bo'] = [ 'qu-BO', diff --git a/packages/common/locales/global/qu-EC.js b/packages/common/locales/global/qu-EC.js index b1de706c62018..6d40e49098265 100644 --- a/packages/common/locales/global/qu-EC.js +++ b/packages/common/locales/global/qu-EC.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['qu-ec'] = [ 'qu-EC', diff --git a/packages/common/locales/global/qu.js b/packages/common/locales/global/qu.js index ac4f263fd2891..c925692f10c5c 100644 --- a/packages/common/locales/global/qu.js +++ b/packages/common/locales/global/qu.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['qu'] = [ 'qu', diff --git a/packages/common/locales/global/rm.js b/packages/common/locales/global/rm.js index d51cf6f8ee059..7cb7d0dbf6ad7 100644 --- a/packages/common/locales/global/rm.js +++ b/packages/common/locales/global/rm.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/rn.js b/packages/common/locales/global/rn.js index 52ff5e00758e3..dd9215791f02c 100644 --- a/packages/common/locales/global/rn.js +++ b/packages/common/locales/global/rn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['rn'] = [ 'rn', diff --git a/packages/common/locales/global/ro-MD.js b/packages/common/locales/global/ro-MD.js index 69d5f85fdde02..6e9cef1d5a263 100644 --- a/packages/common/locales/global/ro-MD.js +++ b/packages/common/locales/global/ro-MD.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; if (!(v === 0) || n === 0 || !(n === 1) && n % 100 === Math.floor(n % 100) && n % 100 >= 1 && n % 100 <= 19) diff --git a/packages/common/locales/global/ro.js b/packages/common/locales/global/ro.js index e23796db5adef..ad31168f8c061 100644 --- a/packages/common/locales/global/ro.js +++ b/packages/common/locales/global/ro.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; if (!(v === 0) || n === 0 || !(n === 1) && n % 100 === Math.floor(n % 100) && n % 100 >= 1 && n % 100 <= 19) diff --git a/packages/common/locales/global/rof.js b/packages/common/locales/global/rof.js index f37a0d1b792dd..ebfa465a97f68 100644 --- a/packages/common/locales/global/rof.js +++ b/packages/common/locales/global/rof.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/root.js b/packages/common/locales/global/root.js index 64788dbc0e439..dac95ce130d63 100644 --- a/packages/common/locales/global/root.js +++ b/packages/common/locales/global/root.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['root'] = [ 'root', diff --git a/packages/common/locales/global/ru-BY.js b/packages/common/locales/global/ru-BY.js index 9b5adef61ac63..c579803898cab 100644 --- a/packages/common/locales/global/ru-BY.js +++ b/packages/common/locales/global/ru-BY.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && !(i % 100 >= 12 && i % 100 <= 14)) diff --git a/packages/common/locales/global/ru-KG.js b/packages/common/locales/global/ru-KG.js index 241ad8734e193..aa385a53fb03d 100644 --- a/packages/common/locales/global/ru-KG.js +++ b/packages/common/locales/global/ru-KG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && !(i % 100 >= 12 && i % 100 <= 14)) diff --git a/packages/common/locales/global/ru-KZ.js b/packages/common/locales/global/ru-KZ.js index 0660885b26be6..f0bf29c806dae 100644 --- a/packages/common/locales/global/ru-KZ.js +++ b/packages/common/locales/global/ru-KZ.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && !(i % 100 >= 12 && i % 100 <= 14)) diff --git a/packages/common/locales/global/ru-MD.js b/packages/common/locales/global/ru-MD.js index e90b2b770d030..665592c479c1c 100644 --- a/packages/common/locales/global/ru-MD.js +++ b/packages/common/locales/global/ru-MD.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && !(i % 100 >= 12 && i % 100 <= 14)) diff --git a/packages/common/locales/global/ru-UA.js b/packages/common/locales/global/ru-UA.js index b761fa6269b90..742a167587f59 100644 --- a/packages/common/locales/global/ru-UA.js +++ b/packages/common/locales/global/ru-UA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && !(i % 100 >= 12 && i % 100 <= 14)) diff --git a/packages/common/locales/global/ru.js b/packages/common/locales/global/ru.js index 63c9a13c7fd78..370c567b2a433 100644 --- a/packages/common/locales/global/ru.js +++ b/packages/common/locales/global/ru.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && !(i % 100 >= 12 && i % 100 <= 14)) diff --git a/packages/common/locales/global/rw.js b/packages/common/locales/global/rw.js index bd63081879d8e..58a07b7d5d2ba 100644 --- a/packages/common/locales/global/rw.js +++ b/packages/common/locales/global/rw.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['rw'] = [ 'rw', diff --git a/packages/common/locales/global/rwk.js b/packages/common/locales/global/rwk.js index 57800d3ab3070..504311fd9fb25 100644 --- a/packages/common/locales/global/rwk.js +++ b/packages/common/locales/global/rwk.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/sah.js b/packages/common/locales/global/sah.js index edb48091a4ab5..ec1dccec0c22b 100644 --- a/packages/common/locales/global/sah.js +++ b/packages/common/locales/global/sah.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['sah'] = [ 'sah', diff --git a/packages/common/locales/global/saq.js b/packages/common/locales/global/saq.js index 8a597173b2527..01402b7c15fc5 100644 --- a/packages/common/locales/global/saq.js +++ b/packages/common/locales/global/saq.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/sbp.js b/packages/common/locales/global/sbp.js index e7ad1f645bc30..c79fa96999446 100644 --- a/packages/common/locales/global/sbp.js +++ b/packages/common/locales/global/sbp.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['sbp'] = [ 'sbp', diff --git a/packages/common/locales/global/sd.js b/packages/common/locales/global/sd.js index dce74785b5390..3341337fbb23d 100644 --- a/packages/common/locales/global/sd.js +++ b/packages/common/locales/global/sd.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/se-FI.js b/packages/common/locales/global/se-FI.js index 7da2b23c99370..ddc2c300fbf3f 100644 --- a/packages/common/locales/global/se-FI.js +++ b/packages/common/locales/global/se-FI.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 2) return 2; diff --git a/packages/common/locales/global/se-SE.js b/packages/common/locales/global/se-SE.js index 5b76de30073d3..8e6078e66cb67 100644 --- a/packages/common/locales/global/se-SE.js +++ b/packages/common/locales/global/se-SE.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 2) return 2; diff --git a/packages/common/locales/global/se.js b/packages/common/locales/global/se.js index 27203c21ea3e8..8dfc06179cca1 100644 --- a/packages/common/locales/global/se.js +++ b/packages/common/locales/global/se.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 2) return 2; diff --git a/packages/common/locales/global/seh.js b/packages/common/locales/global/seh.js index 8bf3bcf335893..0043a3cf7684e 100644 --- a/packages/common/locales/global/seh.js +++ b/packages/common/locales/global/seh.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ses.js b/packages/common/locales/global/ses.js index cec8a6d8ffc15..ec5cc87f63067 100644 --- a/packages/common/locales/global/ses.js +++ b/packages/common/locales/global/ses.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['ses'] = [ 'ses', diff --git a/packages/common/locales/global/sg.js b/packages/common/locales/global/sg.js index 9282f7ab9d57c..2a1ecb5afd17c 100644 --- a/packages/common/locales/global/sg.js +++ b/packages/common/locales/global/sg.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['sg'] = [ 'sg', diff --git a/packages/common/locales/global/shi-Latn.js b/packages/common/locales/global/shi-Latn.js index 791eab3e0bbde..3d311f8e3cf43 100644 --- a/packages/common/locales/global/shi-Latn.js +++ b/packages/common/locales/global/shi-Latn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['shi-latn'] = [ 'shi-Latn', diff --git a/packages/common/locales/global/shi-Tfng.js b/packages/common/locales/global/shi-Tfng.js index b4fd43257ea72..defda17b600a2 100644 --- a/packages/common/locales/global/shi-Tfng.js +++ b/packages/common/locales/global/shi-Tfng.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; if (n === Math.floor(n) && n >= 2 && n <= 10) return 3; return 5; diff --git a/packages/common/locales/global/shi.js b/packages/common/locales/global/shi.js index 747ce4fb8433c..03fcff3cfbc69 100644 --- a/packages/common/locales/global/shi.js +++ b/packages/common/locales/global/shi.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; if (n === Math.floor(n) && n >= 2 && n <= 10) return 3; return 5; diff --git a/packages/common/locales/global/si.js b/packages/common/locales/global/si.js index 98a0c668c1328..03e8ad6daa57c 100644 --- a/packages/common/locales/global/si.js +++ b/packages/common/locales/global/si.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; + var i = Math.floor(Math.abs(n)), f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (n === 0 || n === 1 || i === 0 && f === 1) return 1; return 5; } diff --git a/packages/common/locales/global/sk.js b/packages/common/locales/global/sk.js index d23f3f853d9b9..398f862781737 100644 --- a/packages/common/locales/global/sk.js +++ b/packages/common/locales/global/sk.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; if (i === Math.floor(i) && i >= 2 && i <= 4 && v === 0) return 3; if (!(v === 0)) return 4; diff --git a/packages/common/locales/global/sl.js b/packages/common/locales/global/sl.js index 5316a375d5263..5455ef2b9cd96 100644 --- a/packages/common/locales/global/sl.js +++ b/packages/common/locales/global/sl.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 100 === 1) return 1; if (v === 0 && i % 100 === 2) return 2; if (v === 0 && i % 100 === Math.floor(i % 100) && i % 100 >= 3 && i % 100 <= 4 || !(v === 0)) diff --git a/packages/common/locales/global/smn.js b/packages/common/locales/global/smn.js index 4f3ea04500930..55f015a31ac79 100644 --- a/packages/common/locales/global/smn.js +++ b/packages/common/locales/global/smn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; if (n === 2) return 2; diff --git a/packages/common/locales/global/sn.js b/packages/common/locales/global/sn.js index ad23df4910788..3a94ee0c7d101 100644 --- a/packages/common/locales/global/sn.js +++ b/packages/common/locales/global/sn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/so-DJ.js b/packages/common/locales/global/so-DJ.js index c09ddff2d9e9d..581c99dd21f80 100644 --- a/packages/common/locales/global/so-DJ.js +++ b/packages/common/locales/global/so-DJ.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/so-ET.js b/packages/common/locales/global/so-ET.js index be8180f81b70b..268a155ff4dea 100644 --- a/packages/common/locales/global/so-ET.js +++ b/packages/common/locales/global/so-ET.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/so-KE.js b/packages/common/locales/global/so-KE.js index a8f48871ceffe..6e78e636c9253 100644 --- a/packages/common/locales/global/so-KE.js +++ b/packages/common/locales/global/so-KE.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/so.js b/packages/common/locales/global/so.js index e5bfebc21e9a3..5117ad613a4d9 100644 --- a/packages/common/locales/global/so.js +++ b/packages/common/locales/global/so.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/sq-MK.js b/packages/common/locales/global/sq-MK.js index 34277d3cbd77a..37bb80a2a6eaf 100644 --- a/packages/common/locales/global/sq-MK.js +++ b/packages/common/locales/global/sq-MK.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/sq-XK.js b/packages/common/locales/global/sq-XK.js index 9fce09b66c2f3..80f379044fe0f 100644 --- a/packages/common/locales/global/sq-XK.js +++ b/packages/common/locales/global/sq-XK.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/sq.js b/packages/common/locales/global/sq.js index d441c6ded5345..d7c2359f8a5c6 100644 --- a/packages/common/locales/global/sq.js +++ b/packages/common/locales/global/sq.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/sr-Cyrl-BA.js b/packages/common/locales/global/sr-Cyrl-BA.js index ac28a2cb34858..fa6d1168e2d88 100644 --- a/packages/common/locales/global/sr-Cyrl-BA.js +++ b/packages/common/locales/global/sr-Cyrl-BA.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && diff --git a/packages/common/locales/global/sr-Cyrl-ME.js b/packages/common/locales/global/sr-Cyrl-ME.js index 815d2f72adad1..e97d553bf81d8 100644 --- a/packages/common/locales/global/sr-Cyrl-ME.js +++ b/packages/common/locales/global/sr-Cyrl-ME.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && diff --git a/packages/common/locales/global/sr-Cyrl-XK.js b/packages/common/locales/global/sr-Cyrl-XK.js index c2ab9caf3c523..150fc42852116 100644 --- a/packages/common/locales/global/sr-Cyrl-XK.js +++ b/packages/common/locales/global/sr-Cyrl-XK.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && diff --git a/packages/common/locales/global/sr-Cyrl.js b/packages/common/locales/global/sr-Cyrl.js index e13f483b9009f..c17f14ece5f62 100644 --- a/packages/common/locales/global/sr-Cyrl.js +++ b/packages/common/locales/global/sr-Cyrl.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && diff --git a/packages/common/locales/global/sr-Latn-BA.js b/packages/common/locales/global/sr-Latn-BA.js index 4ecdab6a888d5..de940ac87d54d 100644 --- a/packages/common/locales/global/sr-Latn-BA.js +++ b/packages/common/locales/global/sr-Latn-BA.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['sr-latn-ba'] = [ 'sr-Latn-BA', diff --git a/packages/common/locales/global/sr-Latn-ME.js b/packages/common/locales/global/sr-Latn-ME.js index 9a01bc4fb5cc7..7897cf3e7e39a 100644 --- a/packages/common/locales/global/sr-Latn-ME.js +++ b/packages/common/locales/global/sr-Latn-ME.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['sr-latn-me'] = [ 'sr-Latn-ME', diff --git a/packages/common/locales/global/sr-Latn-XK.js b/packages/common/locales/global/sr-Latn-XK.js index bf2acb6b688c5..2970550206c06 100644 --- a/packages/common/locales/global/sr-Latn-XK.js +++ b/packages/common/locales/global/sr-Latn-XK.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['sr-latn-xk'] = [ 'sr-Latn-XK', diff --git a/packages/common/locales/global/sr-Latn.js b/packages/common/locales/global/sr-Latn.js index 2130a6a1f676e..b625cdf1b7d0d 100644 --- a/packages/common/locales/global/sr-Latn.js +++ b/packages/common/locales/global/sr-Latn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['sr-latn'] = [ 'sr-Latn', diff --git a/packages/common/locales/global/sr.js b/packages/common/locales/global/sr.js index cd8085f52f708..9a48f3aeaa6cb 100644 --- a/packages/common/locales/global/sr.js +++ b/packages/common/locales/global/sr.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length, f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0; if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && diff --git a/packages/common/locales/global/sv-AX.js b/packages/common/locales/global/sv-AX.js index 0fa55b18cc20d..d486b55213dab 100644 --- a/packages/common/locales/global/sv-AX.js +++ b/packages/common/locales/global/sv-AX.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/sv-FI.js b/packages/common/locales/global/sv-FI.js index 5d417d6e00965..808772672b200 100644 --- a/packages/common/locales/global/sv-FI.js +++ b/packages/common/locales/global/sv-FI.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/sv.js b/packages/common/locales/global/sv.js index b40fbf2770597..f07e46a397b70 100644 --- a/packages/common/locales/global/sv.js +++ b/packages/common/locales/global/sv.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/sw-CD.js b/packages/common/locales/global/sw-CD.js index 1c97c85d306c0..4bc2f0ad5f840 100644 --- a/packages/common/locales/global/sw-CD.js +++ b/packages/common/locales/global/sw-CD.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/sw-KE.js b/packages/common/locales/global/sw-KE.js index ff6930a26e3b6..1be4455ced99e 100644 --- a/packages/common/locales/global/sw-KE.js +++ b/packages/common/locales/global/sw-KE.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/sw-UG.js b/packages/common/locales/global/sw-UG.js index 36e249702515b..520baac7d9d57 100644 --- a/packages/common/locales/global/sw-UG.js +++ b/packages/common/locales/global/sw-UG.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/sw.js b/packages/common/locales/global/sw.js index 293c306a4421e..fd810d4db6864 100644 --- a/packages/common/locales/global/sw.js +++ b/packages/common/locales/global/sw.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ta-LK.js b/packages/common/locales/global/ta-LK.js index 07f32a2f06f44..3ef2d3c861964 100644 --- a/packages/common/locales/global/ta-LK.js +++ b/packages/common/locales/global/ta-LK.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ta-MY.js b/packages/common/locales/global/ta-MY.js index 42b8c2fc5f4e9..2aef753310b4c 100644 --- a/packages/common/locales/global/ta-MY.js +++ b/packages/common/locales/global/ta-MY.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ta-SG.js b/packages/common/locales/global/ta-SG.js index 4a90c2565ed18..f071266197d36 100644 --- a/packages/common/locales/global/ta-SG.js +++ b/packages/common/locales/global/ta-SG.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/ta.js b/packages/common/locales/global/ta.js index 7049c828d71d8..c25933149c979 100644 --- a/packages/common/locales/global/ta.js +++ b/packages/common/locales/global/ta.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/te.js b/packages/common/locales/global/te.js index b0265c5338863..0b520c9088622 100644 --- a/packages/common/locales/global/te.js +++ b/packages/common/locales/global/te.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/teo-KE.js b/packages/common/locales/global/teo-KE.js index b423928838cfb..60ad4a597b9ba 100644 --- a/packages/common/locales/global/teo-KE.js +++ b/packages/common/locales/global/teo-KE.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/teo.js b/packages/common/locales/global/teo.js index 7c439f141e210..b9163b9b50166 100644 --- a/packages/common/locales/global/teo.js +++ b/packages/common/locales/global/teo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/tg.js b/packages/common/locales/global/tg.js index 652481b69a69b..85c6f1afe26e9 100644 --- a/packages/common/locales/global/tg.js +++ b/packages/common/locales/global/tg.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['tg'] = [ 'tg', diff --git a/packages/common/locales/global/th.js b/packages/common/locales/global/th.js index 199d0b01b5e8f..147489bf63076 100644 --- a/packages/common/locales/global/th.js +++ b/packages/common/locales/global/th.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['th'] = [ 'th', diff --git a/packages/common/locales/global/ti-ER.js b/packages/common/locales/global/ti-ER.js index a0e2b874a414e..c45f8527c4e6f 100644 --- a/packages/common/locales/global/ti-ER.js +++ b/packages/common/locales/global/ti-ER.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/ti.js b/packages/common/locales/global/ti.js index d9645991dc27c..2d6e95bd22cd5 100644 --- a/packages/common/locales/global/ti.js +++ b/packages/common/locales/global/ti.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1) return 1; return 5; diff --git a/packages/common/locales/global/tk.js b/packages/common/locales/global/tk.js index 673fbe5d72869..c7354f999b79d 100644 --- a/packages/common/locales/global/tk.js +++ b/packages/common/locales/global/tk.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/to.js b/packages/common/locales/global/to.js index d3218d4b20e5e..46ca4cf9fd44a 100644 --- a/packages/common/locales/global/to.js +++ b/packages/common/locales/global/to.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['to'] = [ 'to', diff --git a/packages/common/locales/global/tr-CY.js b/packages/common/locales/global/tr-CY.js index eb0799e4a0226..3bc6bdfe9d6b1 100644 --- a/packages/common/locales/global/tr-CY.js +++ b/packages/common/locales/global/tr-CY.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/tr.js b/packages/common/locales/global/tr.js index 55de148b02d21..235e786b34b83 100644 --- a/packages/common/locales/global/tr.js +++ b/packages/common/locales/global/tr.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/tt.js b/packages/common/locales/global/tt.js index 8187af7484915..a9168d1d8944d 100644 --- a/packages/common/locales/global/tt.js +++ b/packages/common/locales/global/tt.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['tt'] = [ 'tt', diff --git a/packages/common/locales/global/twq.js b/packages/common/locales/global/twq.js index 4f1358379cd1c..ba27abb534ec7 100644 --- a/packages/common/locales/global/twq.js +++ b/packages/common/locales/global/twq.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['twq'] = [ 'twq', diff --git a/packages/common/locales/global/tzm.js b/packages/common/locales/global/tzm.js index a5144d97f939b..8b13668830fe9 100644 --- a/packages/common/locales/global/tzm.js +++ b/packages/common/locales/global/tzm.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === Math.floor(n) && n >= 0 && n <= 1 || n === Math.floor(n) && n >= 11 && n <= 99) return 1; diff --git a/packages/common/locales/global/ug.js b/packages/common/locales/global/ug.js index 502d59e565326..3e49901a68654 100644 --- a/packages/common/locales/global/ug.js +++ b/packages/common/locales/global/ug.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/uk.js b/packages/common/locales/global/uk.js index 58087a9c37bd5..e71da851d6b53 100644 --- a/packages/common/locales/global/uk.js +++ b/packages/common/locales/global/uk.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return 1; if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 && !(i % 100 >= 12 && i % 100 <= 14)) diff --git a/packages/common/locales/global/ur-IN.js b/packages/common/locales/global/ur-IN.js index d6ad633597754..54e472eafd15e 100644 --- a/packages/common/locales/global/ur-IN.js +++ b/packages/common/locales/global/ur-IN.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/ur.js b/packages/common/locales/global/ur.js index 2e05ce7dccee8..6cfb4c254c613 100644 --- a/packages/common/locales/global/ur.js +++ b/packages/common/locales/global/ur.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/uz-Arab.js b/packages/common/locales/global/uz-Arab.js index c091a19a736ff..3855cc475013b 100644 --- a/packages/common/locales/global/uz-Arab.js +++ b/packages/common/locales/global/uz-Arab.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['uz-arab'] = [ 'uz-Arab', diff --git a/packages/common/locales/global/uz-Cyrl.js b/packages/common/locales/global/uz-Cyrl.js index 57cf0689ddf7e..5c3bd1bc2f443 100644 --- a/packages/common/locales/global/uz-Cyrl.js +++ b/packages/common/locales/global/uz-Cyrl.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['uz-cyrl'] = [ 'uz-Cyrl', diff --git a/packages/common/locales/global/uz-Latn.js b/packages/common/locales/global/uz-Latn.js index 3d66b9cd300e0..0b03252a2f59e 100644 --- a/packages/common/locales/global/uz-Latn.js +++ b/packages/common/locales/global/uz-Latn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/uz.js b/packages/common/locales/global/uz.js index 275707d6042ac..1fc4eb355fff4 100644 --- a/packages/common/locales/global/uz.js +++ b/packages/common/locales/global/uz.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/vai-Latn.js b/packages/common/locales/global/vai-Latn.js index c6eb75aa35a61..8764ae5d325dd 100644 --- a/packages/common/locales/global/vai-Latn.js +++ b/packages/common/locales/global/vai-Latn.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['vai-latn'] = [ 'vai-Latn', diff --git a/packages/common/locales/global/vai-Vaii.js b/packages/common/locales/global/vai-Vaii.js index ae4fd662eadd2..ec335cb2ddf0d 100644 --- a/packages/common/locales/global/vai-Vaii.js +++ b/packages/common/locales/global/vai-Vaii.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['vai-vaii'] = [ 'vai-Vaii', diff --git a/packages/common/locales/global/vai.js b/packages/common/locales/global/vai.js index e6f3dc9b175bc..0637290929e90 100644 --- a/packages/common/locales/global/vai.js +++ b/packages/common/locales/global/vai.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['vai'] = [ 'vai', diff --git a/packages/common/locales/global/vi.js b/packages/common/locales/global/vi.js index 5a4642ba3121e..b93521473baac 100644 --- a/packages/common/locales/global/vi.js +++ b/packages/common/locales/global/vi.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['vi'] = [ 'vi', diff --git a/packages/common/locales/global/vo.js b/packages/common/locales/global/vo.js index 67d04b9a87d3d..95cb582c47fff 100644 --- a/packages/common/locales/global/vo.js +++ b/packages/common/locales/global/vo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/vun.js b/packages/common/locales/global/vun.js index c33a46a54bdc3..d0f73a01d2b7a 100644 --- a/packages/common/locales/global/vun.js +++ b/packages/common/locales/global/vun.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/wae.js b/packages/common/locales/global/wae.js index 62f9c53005456..db4289b914a54 100644 --- a/packages/common/locales/global/wae.js +++ b/packages/common/locales/global/wae.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/wo.js b/packages/common/locales/global/wo.js index 8cec261b21206..07ad8d0b5ee14 100644 --- a/packages/common/locales/global/wo.js +++ b/packages/common/locales/global/wo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['wo'] = [ 'wo', diff --git a/packages/common/locales/global/xh.js b/packages/common/locales/global/xh.js index b33c08d7a37a2..2662348f93bed 100644 --- a/packages/common/locales/global/xh.js +++ b/packages/common/locales/global/xh.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/xog.js b/packages/common/locales/global/xog.js index e883cca213d49..3bc11a75aaf9f 100644 --- a/packages/common/locales/global/xog.js +++ b/packages/common/locales/global/xog.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { if (n === 1) return 1; return 5; diff --git a/packages/common/locales/global/yav.js b/packages/common/locales/global/yav.js index ce0534be67fa4..c90769e3f55c1 100644 --- a/packages/common/locales/global/yav.js +++ b/packages/common/locales/global/yav.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['yav'] = [ 'yav', diff --git a/packages/common/locales/global/yi.js b/packages/common/locales/global/yi.js index f0b7880b2e75a..20f6674ace2a2 100644 --- a/packages/common/locales/global/yi.js +++ b/packages/common/locales/global/yi.js @@ -13,9 +13,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; + var i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } diff --git a/packages/common/locales/global/yo-BJ.js b/packages/common/locales/global/yo-BJ.js index 09cb6be215bce..aedd908e95e4a 100644 --- a/packages/common/locales/global/yo-BJ.js +++ b/packages/common/locales/global/yo-BJ.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['yo-bj'] = [ 'yo-BJ', diff --git a/packages/common/locales/global/yo.js b/packages/common/locales/global/yo.js index bf10dbdb0b747..00fb2ebb70eb4 100644 --- a/packages/common/locales/global/yo.js +++ b/packages/common/locales/global/yo.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['yo'] = [ 'yo', diff --git a/packages/common/locales/global/yue-Hans.js b/packages/common/locales/global/yue-Hans.js index d92a63b3e4632..53bb9832829a2 100644 --- a/packages/common/locales/global/yue-Hans.js +++ b/packages/common/locales/global/yue-Hans.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['yue-hans'] = [ 'yue-Hans', diff --git a/packages/common/locales/global/yue-Hant.js b/packages/common/locales/global/yue-Hant.js index ada7a41c13963..09e152818009e 100644 --- a/packages/common/locales/global/yue-Hant.js +++ b/packages/common/locales/global/yue-Hant.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['yue-hant'] = [ 'yue-Hant', diff --git a/packages/common/locales/global/yue.js b/packages/common/locales/global/yue.js index 99a450fe4f957..9521859e3f1e7 100644 --- a/packages/common/locales/global/yue.js +++ b/packages/common/locales/global/yue.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['yue'] = [ 'yue', diff --git a/packages/common/locales/global/zgh.js b/packages/common/locales/global/zgh.js index d431e438cfcb0..eeaa8a91fc374 100644 --- a/packages/common/locales/global/zgh.js +++ b/packages/common/locales/global/zgh.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zgh'] = [ 'zgh', diff --git a/packages/common/locales/global/zh-Hans-HK.js b/packages/common/locales/global/zh-Hans-HK.js index 5ac2fe792b9c4..7291b9f2674ba 100644 --- a/packages/common/locales/global/zh-Hans-HK.js +++ b/packages/common/locales/global/zh-Hans-HK.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zh-hans-hk'] = [ 'zh-Hans-HK', diff --git a/packages/common/locales/global/zh-Hans-MO.js b/packages/common/locales/global/zh-Hans-MO.js index 7252f091add03..0ae9fecf2a7dd 100644 --- a/packages/common/locales/global/zh-Hans-MO.js +++ b/packages/common/locales/global/zh-Hans-MO.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zh-hans-mo'] = [ 'zh-Hans-MO', diff --git a/packages/common/locales/global/zh-Hans-SG.js b/packages/common/locales/global/zh-Hans-SG.js index fdeecc45ebad4..7d9471db26553 100644 --- a/packages/common/locales/global/zh-Hans-SG.js +++ b/packages/common/locales/global/zh-Hans-SG.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zh-hans-sg'] = [ 'zh-Hans-SG', diff --git a/packages/common/locales/global/zh-Hans.js b/packages/common/locales/global/zh-Hans.js index 8d8f46ac99200..6524fed35f8e7 100644 --- a/packages/common/locales/global/zh-Hans.js +++ b/packages/common/locales/global/zh-Hans.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zh-hans'] = [ 'zh-Hans', diff --git a/packages/common/locales/global/zh-Hant-HK.js b/packages/common/locales/global/zh-Hant-HK.js index 728e970fc2b04..a8f823ebbc7ed 100644 --- a/packages/common/locales/global/zh-Hant-HK.js +++ b/packages/common/locales/global/zh-Hant-HK.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zh-hant-hk'] = [ 'zh-Hant-HK', diff --git a/packages/common/locales/global/zh-Hant-MO.js b/packages/common/locales/global/zh-Hant-MO.js index b020eecc2b762..7ab0d249298e1 100644 --- a/packages/common/locales/global/zh-Hant-MO.js +++ b/packages/common/locales/global/zh-Hant-MO.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zh-hant-mo'] = [ 'zh-Hant-MO', diff --git a/packages/common/locales/global/zh-Hant.js b/packages/common/locales/global/zh-Hant.js index 922ed5cdfd6f4..f0d01a206ea46 100644 --- a/packages/common/locales/global/zh-Hant.js +++ b/packages/common/locales/global/zh-Hant.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zh-hant'] = [ 'zh-Hant', diff --git a/packages/common/locales/global/zh.js b/packages/common/locales/global/zh.js index d5e867ce76899..7e73b32d79aa6 100644 --- a/packages/common/locales/global/zh.js +++ b/packages/common/locales/global/zh.js @@ -13,7 +13,7 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { return 5; } global.ng.common.locales['zh'] = [ 'zh', diff --git a/packages/common/locales/global/zu.js b/packages/common/locales/global/zu.js index 99afd55abfd44..1fcbbf4766e53 100644 --- a/packages/common/locales/global/zu.js +++ b/packages/common/locales/global/zu.js @@ -14,9 +14,9 @@ global.ng = global.ng || {}; global.ng.common = global.ng.common || {}; global.ng.common.locales = global.ng.common.locales || {}; - const u = undefined; + var u = undefined; function plural(n) { - let i = Math.floor(Math.abs(n)); + var i = Math.floor(Math.abs(n)); if (i === 0 || n === 1) return 1; return 5; } diff --git a/packages/common/src/directives/ng_class.ts b/packages/common/src/directives/ng_class.ts index 63ddbe198e422..d24e4dfae7796 100644 --- a/packages/common/src/directives/ng_class.ts +++ b/packages/common/src/directives/ng_class.ts @@ -7,7 +7,7 @@ */ import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer2, ɵisListLikeIterable as isListLikeIterable, ɵstringify as stringify} from '@angular/core'; -type NgClassSupportedTypes = string[] | Set<string>| {[klass: string]: any} | null | undefined; +type NgClassSupportedTypes = string[]|Set<string>|{[klass: string]: any}|null|undefined; /** * @ngModule CommonModule @@ -83,7 +83,7 @@ export class NgClass implements DoCheck { this._applyIterableChanges(iterableChanges); } } else if (this._keyValueDiffer) { - const keyValueChanges = this._keyValueDiffer.diff(this._rawClass as{[k: string]: any}); + const keyValueChanges = this._keyValueDiffer.diff(this._rawClass as {[k: string]: any}); if (keyValueChanges) { this._applyKeyValueChanges(keyValueChanges); } @@ -105,8 +105,8 @@ export class NgClass implements DoCheck { if (typeof record.item === 'string') { this._toggleClass(record.item, true); } else { - throw new Error( - `NgClass can only toggle CSS classes expressed as strings, got ${stringify(record.item)}`); + throw new Error(`NgClass can only toggle CSS classes expressed as strings, got ${ + stringify(record.item)}`); } }); diff --git a/packages/common/src/directives/ng_component_outlet.ts b/packages/common/src/directives/ng_component_outlet.ts index 23f2b856a482d..6c601b6442043 100644 --- a/packages/common/src/directives/ng_component_outlet.ts +++ b/packages/common/src/directives/ng_component_outlet.ts @@ -67,13 +67,13 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo @Directive({selector: '[ngComponentOutlet]'}) export class NgComponentOutlet implements OnChanges, OnDestroy { // TODO(issue/24571): remove '!'. - @Input() ngComponentOutlet !: Type<any>; + @Input() ngComponentOutlet!: Type<any>; // TODO(issue/24571): remove '!'. - @Input() ngComponentOutletInjector !: Injector; + @Input() ngComponentOutletInjector!: Injector; // TODO(issue/24571): remove '!'. - @Input() ngComponentOutletContent !: any[][]; + @Input() ngComponentOutletContent!: any[][]; // TODO(issue/24571): remove '!'. - @Input() ngComponentOutletNgModuleFactory !: NgModuleFactory<any>; + @Input() ngComponentOutletNgModuleFactory!: NgModuleFactory<any>; private _componentRef: ComponentRef<any>|null = null; private _moduleRef: NgModuleRef<any>|null = null; diff --git a/packages/common/src/directives/ng_for_of.ts b/packages/common/src/directives/ng_for_of.ts index 32d5955c992e2..dc176145abe3e 100644 --- a/packages/common/src/directives/ng_for_of.ts +++ b/packages/common/src/directives/ng_for_of.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, TemplateRef, TrackByFunction, ViewContainerRef, isDevMode} from '@angular/core'; +import {Directive, DoCheck, EmbeddedViewRef, Input, isDevMode, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, TemplateRef, TrackByFunction, ViewContainerRef} from '@angular/core'; /** * @publicApi @@ -14,13 +14,21 @@ import {Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, Iterab export class NgForOfContext<T, U extends NgIterable<T> = NgIterable<T>> { constructor(public $implicit: T, public ngForOf: U, public index: number, public count: number) {} - get first(): boolean { return this.index === 0; } + get first(): boolean { + return this.index === 0; + } - get last(): boolean { return this.index === this.count - 1; } + get last(): boolean { + return this.index === this.count - 1; + } - get even(): boolean { return this.index % 2 === 0; } + get even(): boolean { + return this.index % 2 === 0; + } - get odd(): boolean { return !this.even; } + get odd(): boolean { + return !this.even; + } } /** @@ -85,6 +93,7 @@ export class NgForOfContext<T, U extends NgIterable<T> = NgIterable<T>> { * more complex then a property access, for example when using the async pipe (`userStreams | * async`). * - `index: number`: The index of the current item in the iterable. + * - `count: number`: The length of the iterable. * - `first: boolean`: True when the item is the first item in the iterable. * - `last: boolean`: True when the item is the last item in the iterable. * - `even: boolean`: True when the item has an even index in the iterable. @@ -161,13 +170,15 @@ export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCh this._trackByFn = fn; } - get ngForTrackBy(): TrackByFunction<T> { return this._trackByFn; } + get ngForTrackBy(): TrackByFunction<T> { + return this._trackByFn; + } private _ngForOf: U|undefined|null = null; private _ngForOfDirty: boolean = true; private _differ: IterableDiffer<T>|null = null; // TODO(issue/24571): remove '!'. - private _trackByFn !: TrackByFunction<T>; + private _trackByFn!: TrackByFunction<T>; constructor( private _viewContainer: ViewContainerRef, @@ -199,8 +210,8 @@ export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCh try { this._differ = this._differs.find(value).create(this.ngForTrackBy); } catch { - throw new Error( - `Cannot find a differ supporting object '${value}' of type '${getTypeName(value)}'. NgFor only supports binding to Iterables such as Arrays.`); + throw new Error(`Cannot find a differ supporting object '${value}' of type '${ + getTypeName(value)}'. NgFor only supports binding to Iterables such as Arrays.`); } } } @@ -213,14 +224,14 @@ export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCh private _applyChanges(changes: IterableChanges<T>) { const insertTuples: RecordViewTuple<T, U>[] = []; changes.forEachOperation( - (item: IterableChangeRecord<any>, adjustedPreviousIndex: number | null, - currentIndex: number | null) => { + (item: IterableChangeRecord<any>, adjustedPreviousIndex: number|null, + currentIndex: number|null) => { if (item.previousIndex == null) { // NgForOf is never "null" or "undefined" here because the differ detected // that a new item needs to be inserted from the iterable. This implies that // there is an iterable value for "_ngForOf". const view = this._viewContainer.createEmbeddedView( - this._template, new NgForOfContext<T, U>(null !, this._ngForOf !, -1, -1), + this._template, new NgForOfContext<T, U>(null!, this._ngForOf!, -1, -1), currentIndex === null ? undefined : currentIndex); const tuple = new RecordViewTuple<T, U>(item, view); insertTuples.push(tuple); @@ -228,7 +239,7 @@ export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCh this._viewContainer.remove( adjustedPreviousIndex === null ? undefined : adjustedPreviousIndex); } else if (adjustedPreviousIndex !== null) { - const view = this._viewContainer.get(adjustedPreviousIndex) !; + const view = this._viewContainer.get(adjustedPreviousIndex)!; this._viewContainer.move(view, currentIndex); const tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForOfContext<T, U>>>view); insertTuples.push(tuple); @@ -243,7 +254,7 @@ export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCh const viewRef = <EmbeddedViewRef<NgForOfContext<T, U>>>this._viewContainer.get(i); viewRef.context.index = i; viewRef.context.count = ilen; - viewRef.context.ngForOf = this._ngForOf !; + viewRef.context.ngForOf = this._ngForOf!; } changes.forEachIdentityChange((record: any) => { diff --git a/packages/common/src/directives/ng_if.ts b/packages/common/src/directives/ng_if.ts index e9cb89227e4c0..2868055a973e0 100644 --- a/packages/common/src/directives/ng_if.ts +++ b/packages/common/src/directives/ng_if.ts @@ -241,11 +241,11 @@ export class NgIf<T = unknown> { * @publicApi */ export class NgIfContext<T = unknown> { - public $implicit: T = null !; - public ngIf: T = null !; + public $implicit: T = null!; + public ngIf: T = null!; } -function assertTemplate(property: string, templateRef: TemplateRef<any>| null): void { +function assertTemplate(property: string, templateRef: TemplateRef<any>|null): void { const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView); if (!isTemplateRefOrNull) { throw new Error(`${property} must be a TemplateRef, but received '${stringify(templateRef)}'.`); diff --git a/packages/common/src/directives/ng_plural.ts b/packages/common/src/directives/ng_plural.ts index 1cc7de97c4633..52158f9347c4a 100644 --- a/packages/common/src/directives/ng_plural.ts +++ b/packages/common/src/directives/ng_plural.ts @@ -8,7 +8,7 @@ import {Attribute, Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core'; -import {NgLocalization, getPluralCategory} from '../i18n/localization'; +import {getPluralCategory, NgLocalization} from '../i18n/localization'; import {SwitchView} from './ng_switch'; @@ -47,9 +47,9 @@ import {SwitchView} from './ng_switch'; @Directive({selector: '[ngPlural]'}) export class NgPlural { // TODO(issue/24571): remove '!'. - private _switchValue !: number; + private _switchValue!: number; // TODO(issue/24571): remove '!'. - private _activeView !: SwitchView; + private _activeView!: SwitchView; private _caseViews: {[k: string]: SwitchView} = {}; constructor(private _localization: NgLocalization) {} @@ -60,7 +60,9 @@ export class NgPlural { this._updateView(); } - addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; } + addCase(value: string, switchView: SwitchView): void { + this._caseViews[value] = switchView; + } private _updateView(): void { this._clearViews(); diff --git a/packages/common/src/directives/ng_style.ts b/packages/common/src/directives/ng_style.ts index 73dab1a154bb4..f13a93e6dc988 100644 --- a/packages/common/src/directives/ng_style.ts +++ b/packages/common/src/directives/ng_style.ts @@ -62,7 +62,7 @@ export class NgStyle implements DoCheck { ngDoCheck() { if (this._differ) { - const changes = this._differ.diff(this._ngStyle !); + const changes = this._differ.diff(this._ngStyle!); if (changes) { this._applyChanges(changes); } diff --git a/packages/common/src/directives/ng_switch.ts b/packages/common/src/directives/ng_switch.ts index 11c0fc8aef19c..e964491e93078 100644 --- a/packages/common/src/directives/ng_switch.ts +++ b/packages/common/src/directives/ng_switch.ts @@ -104,7 +104,7 @@ export class SwitchView { @Directive({selector: '[ngSwitch]'}) export class NgSwitch { // TODO(issue/24571): remove '!'. - private _defaultViews !: SwitchView[]; + private _defaultViews!: SwitchView[]; private _defaultUsed = false; private _caseCount = 0; private _lastCaseCheckIndex = 0; @@ -120,7 +120,9 @@ export class NgSwitch { } /** @internal */ - _addCase(): number { return this._caseCount++; } + _addCase(): number { + return this._caseCount++; + } /** @internal */ _addDefault(view: SwitchView) { @@ -193,8 +195,7 @@ export class NgSwitchCase implements DoCheck { /** * Stores the HTML template to be selected on match. */ - @Input() - ngSwitchCase: any; + @Input() ngSwitchCase: any; constructor( viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>, @@ -206,7 +207,9 @@ export class NgSwitchCase implements DoCheck { /** * Performs case matching. For internal use only. */ - ngDoCheck() { this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); } + ngDoCheck() { + this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); + } } /** diff --git a/packages/common/src/directives/ng_template_outlet.ts b/packages/common/src/directives/ng_template_outlet.ts index 69662161e7b9c..4256313edc548 100644 --- a/packages/common/src/directives/ng_template_outlet.ts +++ b/packages/common/src/directives/ng_template_outlet.ts @@ -101,7 +101,7 @@ export class NgTemplateOutlet implements OnChanges { private _updateExistingContext(ctx: Object): void { for (let propName of Object.keys(ctx)) { - (<any>this._viewRef !.context)[propName] = (<any>this.ngTemplateOutletContext)[propName]; + (<any>this._viewRef!.context)[propName] = (<any>this.ngTemplateOutletContext)[propName]; } } } diff --git a/packages/common/src/dom_adapter.ts b/packages/common/src/dom_adapter.ts index ea8ba85defbf7..5b715ff982b78 100644 --- a/packages/common/src/dom_adapter.ts +++ b/packages/common/src/dom_adapter.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -let _DOM: DomAdapter = null !; +let _DOM: DomAdapter = null!; export function getDOM(): DomAdapter { return _DOM; diff --git a/packages/common/src/i18n/format_date.ts b/packages/common/src/i18n/format_date.ts index b5df8175b377f..02609c3e2ac1c 100644 --- a/packages/common/src/i18n/format_date.ts +++ b/packages/common/src/i18n/format_date.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {FormStyle, FormatWidth, NumberSymbol, Time, TranslationWidth, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleDayNames, getLocaleDayPeriods, getLocaleEraNames, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocaleId, getLocaleMonthNames, getLocaleNumberSymbol, getLocaleTimeFormat} from './locale_data_api'; +import {FormatWidth, FormStyle, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleDayNames, getLocaleDayPeriods, getLocaleEraNames, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocaleId, getLocaleMonthNames, getLocaleNumberSymbol, getLocaleTimeFormat, NumberSymbol, Time, TranslationWidth} from './locale_data_api'; export const ISO8601_DATE_REGEX = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; @@ -62,7 +62,7 @@ enum TranslationType { * @publicApi */ export function formatDate( - value: string | number | Date, format: string, locale: string, timezone?: string): string { + value: string|number|Date, format: string, locale: string, timezone?: string): string { let date = toDate(value); const namedFormat = getNamedFormat(locale, format); format = namedFormat || format; @@ -277,26 +277,40 @@ function getDateTranslation( if (extended) { const rules = getLocaleExtraDayPeriodRules(locale); const dayPeriods = getLocaleExtraDayPeriods(locale, form, width); - let result; - rules.forEach((rule: Time | [Time, Time], index: number) => { + const index = rules.findIndex(rule => { if (Array.isArray(rule)) { // morning, afternoon, evening, night - const {hours: hoursFrom, minutes: minutesFrom} = rule[0]; - const {hours: hoursTo, minutes: minutesTo} = rule[1]; - if (currentHours >= hoursFrom && currentMinutes >= minutesFrom && - (currentHours < hoursTo || - (currentHours === hoursTo && currentMinutes < minutesTo))) { - result = dayPeriods[index]; + const [from, to] = rule; + const afterFrom = currentHours >= from.hours && currentMinutes >= from.minutes; + const beforeTo = + (currentHours < to.hours || + (currentHours === to.hours && currentMinutes < to.minutes)); + // We must account for normal rules that span a period during the day (e.g. 6am-9am) + // where `from` is less (earlier) than `to`. But also rules that span midnight (e.g. + // 10pm - 5am) where `from` is greater (later!) than `to`. + // + // In the first case the current time must be BOTH after `from` AND before `to` + // (e.g. 8am is after 6am AND before 10am). + // + // In the second case the current time must be EITHER after `from` OR before `to` + // (e.g. 4am is before 5am but not after 10pm; and 11pm is not before 5am but it is + // after 10pm). + if (from.hours < to.hours) { + if (afterFrom && beforeTo) { + return true; + } + } else if (afterFrom || beforeTo) { + return true; } } else { // noon or midnight - const {hours, minutes} = rule; - if (hours === currentHours && minutes === currentMinutes) { - result = dayPeriods[index]; + if (rule.hours === currentHours && rule.minutes === currentMinutes) { + return true; } } + return false; }); - if (result) { - return result; + if (index !== -1) { + return dayPeriods[index]; } } // if no rules for the day periods, we use am/pm by default @@ -652,7 +666,7 @@ function convertTimezoneToLocal(date: Date, timezone: string, reverse: boolean): * * Throws if unable to convert to a date. */ -export function toDate(value: string | number | Date): Date { +export function toDate(value: string|number|Date): Date { if (isDate(value)) { return value; } diff --git a/packages/common/src/i18n/format_number.ts b/packages/common/src/i18n/format_number.ts index 770b55032d09e..ba4e358e8dbdf 100644 --- a/packages/common/src/i18n/format_number.ts +++ b/packages/common/src/i18n/format_number.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {NumberFormatStyle, NumberSymbol, getLocaleNumberFormat, getLocaleNumberSymbol, getNumberOfCurrencyDigits} from './locale_data_api'; +import {getLocaleNumberFormat, getLocaleNumberSymbol, getNumberOfCurrencyDigits, NumberFormatStyle, NumberSymbol} from './locale_data_api'; export const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/; const MAX_DIGITS = 22; @@ -153,7 +153,7 @@ export function formatCurrency( const format = getLocaleNumberFormat(locale, NumberFormatStyle.Currency); const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); - pattern.minFrac = getNumberOfCurrencyDigits(currencyCode !); + pattern.minFrac = getNumberOfCurrencyDigits(currencyCode!); pattern.maxFrac = pattern.minFrac; const res = formatNumberToLocaleString( @@ -392,8 +392,8 @@ function parseNumber(num: number): ParsedNumber { */ function roundNumber(parsedNumber: ParsedNumber, minFrac: number, maxFrac: number) { if (minFrac > maxFrac) { - throw new Error( - `The minimum number of digits after fraction (${minFrac}) is higher than the maximum (${maxFrac}).`); + throw new Error(`The minimum number of digits after fraction (${ + minFrac}) is higher than the maximum (${maxFrac}).`); } let digits = parsedNumber.digits; diff --git a/packages/common/src/i18n/locale_data.ts b/packages/common/src/i18n/locale_data.ts index cfbdee94d916b..c2355c8a5d333 100644 --- a/packages/common/src/i18n/locale_data.ts +++ b/packages/common/src/i18n/locale_data.ts @@ -16,6 +16,6 @@ import {ɵregisterLocaleData} from '@angular/core'; * * @publicApi */ -export function registerLocaleData(data: any, localeId?: string | any, extraData?: any): void { +export function registerLocaleData(data: any, localeId?: string|any, extraData?: any): void { return ɵregisterLocaleData(data, localeId, extraData); } diff --git a/packages/common/src/i18n/locale_data_api.ts b/packages/common/src/i18n/locale_data_api.ts index 2354580d434ab..94bbb3fabc9be 100644 --- a/packages/common/src/i18n/locale_data_api.ts +++ b/packages/common/src/i18n/locale_data_api.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵCurrencyIndex, ɵExtraLocaleDataIndex, ɵLocaleDataIndex, ɵfindLocaleData, ɵgetLocaleCurrencyCode, ɵgetLocalePluralCase} from '@angular/core'; +import {ɵCurrencyIndex, ɵExtraLocaleDataIndex, ɵfindLocaleData, ɵgetLocaleCurrencyCode, ɵgetLocalePluralCase, ɵLocaleDataIndex} from '@angular/core'; import {CURRENCIES_EN, CurrenciesSymbols} from './currencies'; @@ -235,9 +235,9 @@ export function getLocaleId(locale: string): string { export function getLocaleDayPeriods( locale: string, formStyle: FormStyle, width: TranslationWidth): [string, string] { const data = ɵfindLocaleData(locale); - const amPmData = <[ - string, string - ][][]>[data[ɵLocaleDataIndex.DayPeriodsFormat], data[ɵLocaleDataIndex.DayPeriodsStandalone]]; + const amPmData = <[string, string][][]>[ + data[ɵLocaleDataIndex.DayPeriodsFormat], data[ɵLocaleDataIndex.DayPeriodsStandalone] + ]; const amPm = getLastDefinedValue(amPmData, formStyle); return getLastDefinedValue(amPm, width); } @@ -509,8 +509,9 @@ export const getLocalePluralCase: (locale: string) => ((value: number) => Plural function checkFullData(data: any) { if (!data[ɵLocaleDataIndex.ExtraData]) { - throw new Error( - `Missing extra locale data for the locale "${data[ɵLocaleDataIndex.LocaleId]}". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.`); + throw new Error(`Missing extra locale data for the locale "${ + data[ɵLocaleDataIndex + .LocaleId]}". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.`); } } @@ -536,11 +537,11 @@ function checkFullData(data: any) { * * @publicApi */ -export function getLocaleExtraDayPeriodRules(locale: string): (Time | [Time, Time])[] { +export function getLocaleExtraDayPeriodRules(locale: string): (Time|[Time, Time])[] { const data = ɵfindLocaleData(locale); checkFullData(data); const rules = data[ɵLocaleDataIndex.ExtraData][ɵExtraLocaleDataIndex.ExtraDayPeriodsRules] || []; - return rules.map((rule: string | [string, string]) => { + return rules.map((rule: string|[string, string]) => { if (typeof rule === 'string') { return extractTime(rule); } @@ -570,8 +571,8 @@ export function getLocaleExtraDayPeriods( const data = ɵfindLocaleData(locale); checkFullData(data); const dayPeriodsData = <string[][][]>[ - data[ɵLocaleDataIndex.ExtraData][ɵExtraLocaleDataIndex.ExtraDayPeriodFormats], - data[ɵLocaleDataIndex.ExtraData][ɵExtraLocaleDataIndex.ExtraDayPeriodStandalone] + data[ɵLocaleDataIndex.ExtraData][ɵExtraLocaleDataIndex.ExtraDayPeriodFormats], + data[ɵLocaleDataIndex.ExtraData][ɵExtraLocaleDataIndex.ExtraDayPeriodStandalone] ]; const dayPeriods = getLastDefinedValue(dayPeriodsData, formStyle) || []; return getLastDefinedValue(dayPeriods, width) || []; @@ -646,7 +647,7 @@ function extractTime(time: string): Time { * * @publicApi */ -export function getCurrencySymbol(code: string, format: 'wide' | 'narrow', locale = 'en'): string { +export function getCurrencySymbol(code: string, format: 'wide'|'narrow', locale = 'en'): string { const currency = getLocaleCurrencies(locale)[code] || CURRENCIES_EN[code] || []; const symbolNarrow = currency[ɵCurrencyIndex.SymbolNarrow]; diff --git a/packages/common/src/i18n/localization.ts b/packages/common/src/i18n/localization.ts index 64227fbcbdadf..b213aae529d83 100644 --- a/packages/common/src/i18n/localization.ts +++ b/packages/common/src/i18n/localization.ts @@ -7,7 +7,8 @@ */ import {Inject, Injectable, LOCALE_ID} from '@angular/core'; -import {Plural, getLocalePluralCase} from './locale_data_api'; + +import {getLocalePluralCase, Plural} from './locale_data_api'; /** @@ -51,7 +52,9 @@ export function getPluralCategory( */ @Injectable() export class NgLocaleLocalization extends NgLocalization { - constructor(@Inject(LOCALE_ID) protected locale: string) { super(); } + constructor(@Inject(LOCALE_ID) protected locale: string) { + super(); + } getPluralCategory(value: any, locale?: string): string { const plural = getLocalePluralCase(locale || this.locale)(value); diff --git a/packages/common/src/location/hash_location_strategy.ts b/packages/common/src/location/hash_location_strategy.ts index b25601127e320..4c17e30cbe9da 100644 --- a/packages/common/src/location/hash_location_strategy.ts +++ b/packages/common/src/location/hash_location_strategy.ts @@ -48,7 +48,9 @@ export class HashLocationStrategy extends LocationStrategy { this._platformLocation.onHashChange(fn); } - getBaseHref(): string { return this._baseHref; } + getBaseHref(): string { + return this._baseHref; + } path(includeHash: boolean = false): string { // the hash value is always prefixed with a `#` @@ -80,7 +82,11 @@ export class HashLocationStrategy extends LocationStrategy { this._platformLocation.replaceState(state, title, url); } - forward(): void { this._platformLocation.forward(); } + forward(): void { + this._platformLocation.forward(); + } - back(): void { this._platformLocation.back(); } + back(): void { + this._platformLocation.back(); + } } diff --git a/packages/common/src/location/location.ts b/packages/common/src/location/location.ts index a161654e6d6ea..415852cc2c147 100644 --- a/packages/common/src/location/location.ts +++ b/packages/common/src/location/location.ts @@ -97,7 +97,9 @@ export class Location { * Reports the current state of the location history. * @returns The current value of the `history.state` object. */ - getState(): unknown { return this._platformLocation.getState(); } + getState(): unknown { + return this._platformLocation.getState(); + } /** * Normalizes the given path and compares to the current normalized path. @@ -173,12 +175,16 @@ export class Location { /** * Navigates forward in the platform's history. */ - forward(): void { this._platformStrategy.forward(); } + forward(): void { + this._platformStrategy.forward(); + } /** * Navigates back in the platform's history. */ - back(): void { this._platformStrategy.back(); } + back(): void { + this._platformStrategy.back(); + } /** * Registers a URL change listener. Use to catch updates performed by the Angular @@ -188,7 +194,9 @@ export class Location { */ onUrlChange(fn: (url: string, state: unknown) => void) { this._urlChangeListeners.push(fn); - this.subscribe(v => { this._notifyUrlChangeListeners(v.url, v.state); }); + this.subscribe(v => { + this._notifyUrlChangeListeners(v.url, v.state); + }); } /** @internal */ diff --git a/packages/common/src/location/location_strategy.ts b/packages/common/src/location/location_strategy.ts index a4e732c20ad8d..6e3fc8bbb1772 100644 --- a/packages/common/src/location/location_strategy.ts +++ b/packages/common/src/location/location_strategy.ts @@ -126,9 +126,13 @@ export class PathLocationStrategy extends LocationStrategy { this._platformLocation.onHashChange(fn); } - getBaseHref(): string { return this._baseHref; } + getBaseHref(): string { + return this._baseHref; + } - prepareExternalUrl(internal: string): string { return joinWithSlash(this._baseHref, internal); } + prepareExternalUrl(internal: string): string { + return joinWithSlash(this._baseHref, internal); + } path(includeHash: boolean = false): string { const pathname = @@ -147,7 +151,11 @@ export class PathLocationStrategy extends LocationStrategy { this._platformLocation.replaceState(state, title, externalUrl); } - forward(): void { this._platformLocation.forward(); } + forward(): void { + this._platformLocation.forward(); + } - back(): void { this._platformLocation.back(); } + back(): void { + this._platformLocation.back(); + } } diff --git a/packages/common/src/location/platform_location.ts b/packages/common/src/location/platform_location.ts index 4139d768153c1..8470161f7174d 100644 --- a/packages/common/src/location/platform_location.ts +++ b/packages/common/src/location/platform_location.ts @@ -86,7 +86,9 @@ export interface LocationChangeEvent { /** * @publicApi */ -export interface LocationChangeListener { (event: LocationChangeEvent): any; } +export interface LocationChangeListener { + (event: LocationChangeEvent): any; +} @@ -101,8 +103,8 @@ export interface LocationChangeListener { (event: LocationChangeEvent): any; } useFactory: createBrowserPlatformLocation, }) export class BrowserPlatformLocation extends PlatformLocation { - public readonly location !: Location; - private _history !: History; + public readonly location!: Location; + private _history!: History; constructor(@Inject(DOCUMENT) private _doc: any) { super(); @@ -112,11 +114,13 @@ export class BrowserPlatformLocation extends PlatformLocation { // This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it /** @internal */ _init() { - (this as{location: Location}).location = getDOM().getLocation(); + (this as {location: Location}).location = getDOM().getLocation(); this._history = getDOM().getHistory(); } - getBaseHrefFromDOM(): string { return getDOM().getBaseHref(this._doc) !; } + getBaseHrefFromDOM(): string { + return getDOM().getBaseHref(this._doc)!; + } onPopState(fn: LocationChangeListener): void { getDOM().getGlobalEventTarget(this._doc, 'window').addEventListener('popstate', fn, false); @@ -126,14 +130,30 @@ export class BrowserPlatformLocation extends PlatformLocation { getDOM().getGlobalEventTarget(this._doc, 'window').addEventListener('hashchange', fn, false); } - get href(): string { return this.location.href; } - get protocol(): string { return this.location.protocol; } - get hostname(): string { return this.location.hostname; } - get port(): string { return this.location.port; } - get pathname(): string { return this.location.pathname; } - get search(): string { return this.location.search; } - get hash(): string { return this.location.hash; } - set pathname(newPath: string) { this.location.pathname = newPath; } + get href(): string { + return this.location.href; + } + get protocol(): string { + return this.location.protocol; + } + get hostname(): string { + return this.location.hostname; + } + get port(): string { + return this.location.port; + } + get pathname(): string { + return this.location.pathname; + } + get search(): string { + return this.location.search; + } + get hash(): string { + return this.location.hash; + } + set pathname(newPath: string) { + this.location.pathname = newPath; + } pushState(state: any, title: string, url: string): void { if (supportsState()) { @@ -151,11 +171,17 @@ export class BrowserPlatformLocation extends PlatformLocation { } } - forward(): void { this._history.forward(); } + forward(): void { + this._history.forward(); + } - back(): void { this._history.back(); } + back(): void { + this._history.back(); + } - getState(): unknown { return this._history.state; } + getState(): unknown { + return this._history.state; + } } export function supportsState(): boolean { diff --git a/packages/common/src/pipes/async_pipe.ts b/packages/common/src/pipes/async_pipe.ts index 40d565d2f18d3..54a62d69031ef 100644 --- a/packages/common/src/pipes/async_pipe.ts +++ b/packages/common/src/pipes/async_pipe.ts @@ -19,17 +19,28 @@ interface SubscriptionStrategy { class ObservableStrategy implements SubscriptionStrategy { createSubscription(async: Observable<any>, updateLatestValue: any): SubscriptionLike { - return async.subscribe({next: updateLatestValue, error: (e: any) => { throw e; }}); + return async.subscribe({ + next: updateLatestValue, + error: (e: any) => { + throw e; + } + }); } - dispose(subscription: SubscriptionLike): void { subscription.unsubscribe(); } + dispose(subscription: SubscriptionLike): void { + subscription.unsubscribe(); + } - onDestroy(subscription: SubscriptionLike): void { subscription.unsubscribe(); } + onDestroy(subscription: SubscriptionLike): void { + subscription.unsubscribe(); + } } class PromiseStrategy implements SubscriptionStrategy { createSubscription(async: Promise<any>, updateLatestValue: (v: any) => any): Promise<any> { - return async.then(updateLatestValue, e => { throw e; }); + return async.then(updateLatestValue, e => { + throw e; + }); } dispose(subscription: Promise<any>): void {} @@ -74,7 +85,7 @@ export class AsyncPipe implements OnDestroy, PipeTransform { private _subscription: SubscriptionLike|Promise<any>|null = null; private _obj: Observable<any>|Promise<any>|EventEmitter<any>|null = null; - private _strategy: SubscriptionStrategy = null !; + private _strategy: SubscriptionStrategy = null!; constructor(private _ref: ChangeDetectorRef) {} @@ -130,7 +141,7 @@ export class AsyncPipe implements OnDestroy, PipeTransform { } private _dispose(): void { - this._strategy.dispose(this._subscription !); + this._strategy.dispose(this._subscription!); this._latestValue = null; this._latestReturnedValue = null; this._subscription = null; diff --git a/packages/common/src/pipes/i18n_plural_pipe.ts b/packages/common/src/pipes/i18n_plural_pipe.ts index 0a4fa6a030ebd..e8500d86bd867 100644 --- a/packages/common/src/pipes/i18n_plural_pipe.ts +++ b/packages/common/src/pipes/i18n_plural_pipe.ts @@ -7,7 +7,9 @@ */ import {Pipe, PipeTransform} from '@angular/core'; -import {NgLocalization, getPluralCategory} from '../i18n/localization'; + +import {getPluralCategory, NgLocalization} from '../i18n/localization'; + import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; const _INTERPOLATION_REGEXP: RegExp = /#/g; diff --git a/packages/common/src/pipes/index.ts b/packages/common/src/pipes/index.ts index 9dc1e8d10a296..708b67f49ad05 100644 --- a/packages/common/src/pipes/index.ts +++ b/packages/common/src/pipes/index.ts @@ -26,11 +26,11 @@ export { CurrencyPipe, DatePipe, DecimalPipe, - KeyValue, - KeyValuePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, + KeyValue, + KeyValuePipe, LowerCasePipe, PercentPipe, SlicePipe, diff --git a/packages/common/src/pipes/json_pipe.ts b/packages/common/src/pipes/json_pipe.ts index 7ecbdddbe370b..4bae1ec2a055e 100644 --- a/packages/common/src/pipes/json_pipe.ts +++ b/packages/common/src/pipes/json_pipe.ts @@ -28,5 +28,7 @@ export class JsonPipe implements PipeTransform { /** * @param value A value of any type to convert into a JSON-format string. */ - transform(value: any): string { return JSON.stringify(value, null, 2); } + transform(value: any): string { + return JSON.stringify(value, null, 2); + } } diff --git a/packages/common/src/pipes/keyvalue_pipe.ts b/packages/common/src/pipes/keyvalue_pipe.ts index 5770f6a75bd18..a0e56be48a75d 100644 --- a/packages/common/src/pipes/keyvalue_pipe.ts +++ b/packages/common/src/pipes/keyvalue_pipe.ts @@ -47,7 +47,7 @@ export interface KeyValue<K, V> { export class KeyValuePipe implements PipeTransform { constructor(private readonly differs: KeyValueDiffers) {} - private differ !: KeyValueDiffer<any, any>; + private differ!: KeyValueDiffer<any, any>; private keyValues: Array<KeyValue<any, any>> = []; transform<K, V>(input: null, compareFn?: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number): null; @@ -55,12 +55,23 @@ export class KeyValuePipe implements PipeTransform { input: {[key: string]: V}|Map<string, V>, compareFn?: (a: KeyValue<string, V>, b: KeyValue<string, V>) => number): Array<KeyValue<string, V>>; + transform<V>( + input: {[key: string]: V}|Map<string, V>|null, + compareFn?: (a: KeyValue<string, V>, b: KeyValue<string, V>) => number): + Array<KeyValue<string, V>>|null; transform<V>( input: {[key: number]: V}|Map<number, V>, compareFn?: (a: KeyValue<number, V>, b: KeyValue<number, V>) => number): Array<KeyValue<number, V>>; + transform<V>( + input: {[key: number]: V}|Map<number, V>|null, + compareFn?: (a: KeyValue<number, V>, b: KeyValue<number, V>) => number): + Array<KeyValue<number, V>>|null; transform<K, V>(input: Map<K, V>, compareFn?: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number): Array<KeyValue<K, V>>; + transform<K, V>( + input: Map<K, V>|null, + compareFn?: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number): Array<KeyValue<K, V>>|null; transform<K, V>( input: null|{[key: string]: V, [key: number]: V}|Map<K, V>, compareFn: (a: KeyValue<K, V>, b: KeyValue<K, V>) => number = defaultComparator): @@ -79,7 +90,7 @@ export class KeyValuePipe implements PipeTransform { if (differChanges) { this.keyValues = []; differChanges.forEachItem((r: KeyValueChangeRecord<K, V>) => { - this.keyValues.push(makeKeyValuePair(r.key, r.currentValue !)); + this.keyValues.push(makeKeyValuePair(r.key, r.currentValue!)); }); this.keyValues.sort(compareFn); } diff --git a/packages/common/src/pipes/number_pipe.ts b/packages/common/src/pipes/number_pipe.ts index db8c6546faf67..e681f27803c59 100644 --- a/packages/common/src/pipes/number_pipe.ts +++ b/packages/common/src/pipes/number_pipe.ts @@ -253,7 +253,7 @@ function isEmpty(value: any): boolean { /** * Transforms a string into a number (if needed). */ -function strToNumber(value: number | string): number { +function strToNumber(value: number|string): number { // Convert strings to numbers if (typeof value === 'string' && !isNaN(Number(value) - parseFloat(value))) { return Number(value); diff --git a/packages/common/src/pipes/slice_pipe.ts b/packages/common/src/pipes/slice_pipe.ts index 13f57fc2ae7cb..59fd55da63fe2 100644 --- a/packages/common/src/pipes/slice_pipe.ts +++ b/packages/common/src/pipes/slice_pipe.ts @@ -75,5 +75,7 @@ export class SlicePipe implements PipeTransform { return value.slice(start, end); } - private supports(obj: any): boolean { return typeof obj === 'string' || Array.isArray(obj); } + private supports(obj: any): boolean { + return typeof obj === 'string' || Array.isArray(obj); + } } diff --git a/packages/common/src/viewport_scroller.ts b/packages/common/src/viewport_scroller.ts index 39044f51d93ca..9462a709a327b 100644 --- a/packages/common/src/viewport_scroller.ts +++ b/packages/common/src/viewport_scroller.ts @@ -186,7 +186,9 @@ export class NullViewportScroller implements ViewportScroller { /** * Empty implementation */ - getScrollPosition(): [number, number] { return [0, 0]; } + getScrollPosition(): [number, number] { + return [0, 0]; + } /** * Empty implementation diff --git a/packages/common/test/cookie_spec.ts b/packages/common/test/cookie_spec.ts index 36b10c0e35328..0fa17ba5ab1d6 100644 --- a/packages/common/test/cookie_spec.ts +++ b/packages/common/test/cookie_spec.ts @@ -9,12 +9,12 @@ /** -* @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 -*/ + * @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 {parseCookieValue} from '@angular/common/src/cookie'; import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; diff --git a/packages/common/test/directives/ng_class_spec.ts b/packages/common/test/directives/ng_class_spec.ts index d31771932de40..419b443f8370d 100644 --- a/packages/common/test/directives/ng_class_spec.ts +++ b/packages/common/test/directives/ng_class_spec.ts @@ -7,7 +7,7 @@ */ import {Component} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; { describe('binding to CSS class list', () => { @@ -18,14 +18,18 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; } function detectChangesAndExpectClassName(classes: string): void { - fixture !.detectChanges(); - let nonNormalizedClassName = fixture !.debugElement.children[0].nativeElement.className; + fixture!.detectChanges(); + let nonNormalizedClassName = fixture!.debugElement.children[0].nativeElement.className; expect(normalizeClassNames(nonNormalizedClassName)).toEqual(normalizeClassNames(classes)); } - function getComponent(): TestComponent { return fixture !.debugElement.componentInstance; } + function getComponent(): TestComponent { + return fixture!.debugElement.componentInstance; + } - afterEach(() => { fixture = null; }); + afterEach(() => { + fixture = null; + }); beforeEach(() => { TestBed.configureTestingModule({ @@ -43,7 +47,6 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; })); describe('expressions evaluating to objects', () => { - it('should add classes specified in an object literal', async(() => { fixture = createTestComponent('<div [ngClass]="{foo: true, bar: false}"></div>'); @@ -74,13 +77,13 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; detectChangesAndExpectClassName('foo'); - objExpr !['bar'] = true; + objExpr!['bar'] = true; detectChangesAndExpectClassName('foo bar'); - objExpr !['baz'] = true; + objExpr!['baz'] = true; detectChangesAndExpectClassName('foo bar baz'); - delete (objExpr !['bar']); + delete (objExpr!['bar']); detectChangesAndExpectClassName('foo baz'); })); @@ -129,7 +132,6 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; }); describe('expressions evaluating to lists', () => { - it('should add classes specified in a list literal', async(() => { fixture = createTestComponent(`<div [ngClass]="['foo', 'bar', 'foo-bar', 'fooBar']"></div>`); @@ -194,14 +196,13 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; it('should throw with descriptive error message when CSS class is not a string', () => { fixture = createTestComponent(`<div [ngClass]="['foo', {}]"></div>`); - expect(() => fixture !.detectChanges()) + expect(() => fixture!.detectChanges()) .toThrowError( /NgClass can only toggle CSS classes expressed as strings, got \[object Object\]/); }); }); describe('expressions evaluating to sets', () => { - it('should add and remove classes if the set instance changed', async(() => { fixture = createTestComponent('<div [ngClass]="setExpr"></div>'); let setExpr = new Set<string>(); @@ -217,7 +218,6 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; }); describe('expressions evaluating to string', () => { - it('should add classes specified in a string literal', async(() => { fixture = createTestComponent(`<div [ngClass]="'foo bar foo-bar fooBar'"></div>`); detectChangesAndExpectClassName('foo bar foo-bar fooBar'); @@ -257,19 +257,17 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; getComponent().strExpr = ''; detectChangesAndExpectClassName('foo'); })); - }); describe('cooperation with other class-changing constructs', () => { - it('should co-operate with the class attribute', async(() => { fixture = createTestComponent('<div [ngClass]="objExpr" class="init foo"></div>'); const objExpr = getComponent().objExpr; - objExpr !['bar'] = true; + objExpr!['bar'] = true; detectChangesAndExpectClassName('init foo bar'); - objExpr !['foo'] = false; + objExpr!['foo'] = false; detectChangesAndExpectClassName('init bar'); getComponent().objExpr = null; @@ -280,10 +278,10 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; fixture = createTestComponent(`<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`); const objExpr = getComponent().objExpr; - objExpr !['bar'] = true; + objExpr!['bar'] = true; detectChangesAndExpectClassName(`init foo bar`); - objExpr !['foo'] = false; + objExpr!['foo'] = false; detectChangesAndExpectClassName(`init bar`); getComponent().objExpr = null; @@ -306,10 +304,10 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; createTestComponent(`<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`); const objExpr = getComponent().objExpr; - objExpr !['bar'] = true; + objExpr!['bar'] = true; detectChangesAndExpectClassName(`init foo bar`); - objExpr !['foo'] = false; + objExpr!['foo'] = false; detectChangesAndExpectClassName(`init bar`); getComponent().objExpr = null; @@ -324,10 +322,10 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; detectChangesAndExpectClassName('init foo baz'); - objExpr !['bar'] = true; + objExpr!['bar'] = true; detectChangesAndExpectClassName('init foo baz bar'); - objExpr !['foo'] = false; + objExpr!['foo'] = false; detectChangesAndExpectClassName('init baz bar'); getComponent().condition = false; @@ -342,7 +340,7 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; detectChangesAndExpectClassName('init foo'); - cmp.objExpr !['bar'] = true; + cmp.objExpr!['bar'] = true; detectChangesAndExpectClassName('init foo bar'); cmp.strExpr = 'baz'; @@ -354,7 +352,6 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; }); describe('prevent regressions', () => { - // https://github.com/angular/angular/issues/34336 it('should not write to the native node unless the bound expression has changed', () => { fixture = createTestComponent(`<div [ngClass]="{'color-red': condition}"></div>`); @@ -392,7 +389,6 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; expect(leading.className).toBe('foo'); expect(trailing.className).toBe('foo'); }); - }); }); } @@ -406,7 +402,9 @@ class TestComponent { objExpr: {[klass: string]: any}|null = {'foo': true, 'bar': false}; strExpr: string|null = 'foo'; - constructor() { this.setExpr.add('foo'); } + constructor() { + this.setExpr.add('foo'); + } } function createTestComponent(template: string): ComponentFixture<TestComponent> { diff --git a/packages/common/test/directives/ng_component_outlet_spec.ts b/packages/common/test/directives/ng_component_outlet_spec.ts index b00c2e8efe46d..e5ddc9d63b796 100644 --- a/packages/common/test/directives/ng_component_outlet_spec.ts +++ b/packages/common/test/directives/ng_component_outlet_spec.ts @@ -8,13 +8,14 @@ import {CommonModule} from '@angular/common'; import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_outlet'; -import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core'; -import {TestBed, async} from '@angular/core/testing'; +import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NgModule, NgModuleFactory, NO_ERRORS_SCHEMA, Optional, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core'; +import {async, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; describe('insert/remove', () => { - - beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); }); + beforeEach(() => { + TestBed.configureTestingModule({imports: [TestModule]}); + }); it('should do nothing if component is null', async(() => { const template = `<ng-template *ngComponentOutlet="currentComponent"></ng-template>`; @@ -51,7 +52,7 @@ describe('insert/remove', () => { fixture.detectChanges(); expect(fixture.nativeElement).toHaveText('foo'); expect(fixture.componentInstance.cmpRef).toBeAnInstanceOf(ComponentRef); - expect(fixture.componentInstance.cmpRef !.instance).toBeAnInstanceOf(InjectedComponent); + expect(fixture.componentInstance.cmpRef!.instance).toBeAnInstanceOf(InjectedComponent); })); @@ -99,11 +100,10 @@ describe('insert/remove', () => { [{provide: TEST_TOKEN, useValue: uniqueValue}], fixture.componentRef.injector); fixture.detectChanges(); - let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !; + let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef!; expect(cmpRef).toBeAnInstanceOf(ComponentRef); expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent); expect(cmpRef.instance.testToken).toBe(uniqueValue); - })); @@ -114,7 +114,7 @@ describe('insert/remove', () => { fixture.componentInstance.cmpRef = null; fixture.componentInstance.currentComponent = InjectedComponent; fixture.detectChanges(); - let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !; + let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef!; expect(cmpRef).toBeAnInstanceOf(ComponentRef); expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent); expect(cmpRef.instance.testToken).toBeNull(); @@ -166,7 +166,7 @@ describe('insert/remove', () => { fixture.componentInstance.currentComponent = Module2InjectedComponent; fixture.detectChanges(); - const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef'] !; + const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef']!; spyOn(moduleRef, 'destroy').and.callThrough(); expect(moduleRef.destroy).not.toHaveBeenCalled(); @@ -224,21 +224,25 @@ const TEST_CMP_TEMPLATE = @Component({selector: 'test-cmp', template: TEST_CMP_TEMPLATE}) class TestComponent { // TODO(issue/24571): remove '!'. - currentComponent !: Type<any>| null; + currentComponent!: Type<any>|null; // TODO(issue/24571): remove '!'. - injector !: Injector; + injector!: Injector; // TODO(issue/24571): remove '!'. - projectables !: any[][]; + projectables!: any[][]; // TODO(issue/24571): remove '!'. - module !: NgModuleFactory<any>; + module!: NgModuleFactory<any>; - get cmpRef(): ComponentRef<any>|null { return this.ngComponentOutlet['_componentRef']; } - set cmpRef(value: ComponentRef<any>|null) { this.ngComponentOutlet['_componentRef'] = value; } + get cmpRef(): ComponentRef<any>|null { + return this.ngComponentOutlet['_componentRef']; + } + set cmpRef(value: ComponentRef<any>|null) { + this.ngComponentOutlet['_componentRef'] = value; + } // TODO(issue/24571): remove '!'. - @ViewChildren(TemplateRef) tplRefs !: QueryList<TemplateRef<any>>; + @ViewChildren(TemplateRef) tplRefs!: QueryList<TemplateRef<any>>; // TODO(issue/24571): remove '!'. - @ViewChild(NgComponentOutlet, {static: true}) ngComponentOutlet !: NgComponentOutlet; + @ViewChild(NgComponentOutlet, {static: true}) ngComponentOutlet!: NgComponentOutlet; constructor(public vcRef: ViewContainerRef) {} } diff --git a/packages/common/test/directives/ng_for_spec.ts b/packages/common/test/directives/ng_for_spec.ts index 0935650f7e275..6ce561de1221a 100644 --- a/packages/common/test/directives/ng_for_spec.ts +++ b/packages/common/test/directives/ng_for_spec.ts @@ -8,7 +8,7 @@ import {CommonModule} from '@angular/common'; import {Component} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -18,14 +18,18 @@ let thisArg: any; describe('ngFor', () => { let fixture: ComponentFixture<any>; - function getComponent(): TestComponent { return fixture.componentInstance; } + function getComponent(): TestComponent { + return fixture.componentInstance; + } function detectChangesAndExpectText(text: string): void { fixture.detectChanges(); expect(fixture.nativeElement).toHaveText(text); } - afterEach(() => { fixture = null as any; }); + afterEach(() => { + fixture = null as any; + }); beforeEach(() => { TestBed.configureTestingModule({ @@ -103,7 +107,7 @@ let thisArg: any; detectChangesAndExpectText('1;2;'); - getComponent().items = null !; + getComponent().items = null!; detectChangesAndExpectText(''); getComponent().items = [1, 2, 3]; @@ -203,6 +207,17 @@ let thisArg: any; detectChangesAndExpectText('0123456789'); })); + it('should display count correctly', async(() => { + const template = '<span *ngFor="let item of items; let len=count">{{len}}</span>'; + fixture = createTestComponent(template); + + getComponent().items = [0, 1, 2]; + detectChangesAndExpectText('333'); + + getComponent().items = [4, 3, 2, 1, 0, -1]; + detectChangesAndExpectText('666666'); + })); + it('should display first item correctly', async(() => { const template = '<span *ngFor="let item of items; let isFirst=first">{{isFirst.toString()}}</span>'; @@ -366,16 +381,24 @@ let thisArg: any; } class Foo { - toString() { return 'foo'; } + toString() { + return 'foo'; + } } @Component({selector: 'test-cmp', template: ''}) class TestComponent { value: any; items: any[] = [1, 2]; - trackById(index: number, item: any): string { return item['id']; } - trackByIndex(index: number, item: any): number { return index; } - trackByContext(): void { thisArg = this; } + trackById(index: number, item: any): string { + return item['id']; + } + trackByIndex(index: number, item: any): number { + return index; + } + trackByContext(): void { + thisArg = this; + } } const TEMPLATE = '<div><span *ngFor="let item of items">{{item.toString()}};</span></div>'; diff --git a/packages/common/test/directives/ng_if_spec.ts b/packages/common/test/directives/ng_if_spec.ts index 84b73b4ce1436..2e9cd74069a71 100644 --- a/packages/common/test/directives/ng_if_spec.ts +++ b/packages/common/test/directives/ng_if_spec.ts @@ -8,7 +8,7 @@ import {CommonModule, ɵgetDOM as getDOM} from '@angular/common'; import {Component} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -16,9 +16,13 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; describe('ngIf directive', () => { let fixture: ComponentFixture<any>; - function getComponent(): TestComponent { return fixture.componentInstance; } + function getComponent(): TestComponent { + return fixture.componentInstance; + } - afterEach(() => { fixture = null !; }); + afterEach(() => { + fixture = null!; + }); beforeEach(() => { TestBed.configureTestingModule({ diff --git a/packages/common/test/directives/ng_plural_spec.ts b/packages/common/test/directives/ng_plural_spec.ts index 07e6e66740b7f..9a5a30bb983d2 100644 --- a/packages/common/test/directives/ng_plural_spec.ts +++ b/packages/common/test/directives/ng_plural_spec.ts @@ -8,21 +8,25 @@ import {CommonModule, NgLocalization} from '@angular/common'; import {Component, Injectable} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; { describe('ngPlural', () => { let fixture: ComponentFixture<any>; - function getComponent(): TestComponent { return fixture.componentInstance; } + function getComponent(): TestComponent { + return fixture.componentInstance; + } function detectChangesAndExpectText<T>(text: string): void { fixture.detectChanges(); expect(fixture.nativeElement).toHaveText(text); } - afterEach(() => { fixture = null !; }); + afterEach(() => { + fixture = null!; + }); beforeEach(() => { TestBed.configureTestingModule({ diff --git a/packages/common/test/directives/ng_style_spec.ts b/packages/common/test/directives/ng_style_spec.ts index aaba40cd80e88..3ec43fa203aa4 100644 --- a/packages/common/test/directives/ng_style_spec.ts +++ b/packages/common/test/directives/ng_style_spec.ts @@ -8,19 +8,23 @@ import {CommonModule} from '@angular/common'; import {Component} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; { describe('NgStyle', () => { let fixture: ComponentFixture<TestComponent>; - function getComponent(): TestComponent { return fixture.componentInstance; } + function getComponent(): TestComponent { + return fixture.componentInstance; + } function expectNativeEl(fixture: ComponentFixture<any>): any { return expect(fixture.debugElement.children[0].nativeElement); } - afterEach(() => { fixture = null !; }); + afterEach(() => { + fixture = null!; + }); beforeEach(() => { TestBed.configureTestingModule({declarations: [TestComponent], imports: [CommonModule]}); @@ -159,7 +163,6 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; })); it('should not write to the native node unless the bound expression has changed', () => { - const template = `<div [ngStyle]="{'color': expr}"></div>`; fixture = createTestComponent(template); @@ -189,7 +192,6 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing'; fixture.detectChanges(); expectNativeEl(fixture).toHaveCssStyle({'width': '400px'}); }); - }); } diff --git a/packages/common/test/directives/ng_switch_spec.ts b/packages/common/test/directives/ng_switch_spec.ts index 436d8ad36bb18..0dbeab3acef4d 100644 --- a/packages/common/test/directives/ng_switch_spec.ts +++ b/packages/common/test/directives/ng_switch_spec.ts @@ -15,14 +15,18 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; describe('NgSwitch', () => { let fixture: ComponentFixture<any>; - function getComponent(): TestComponent { return fixture.componentInstance; } + function getComponent(): TestComponent { + return fixture.componentInstance; + } function detectChangesAndExpectText(text: string): void { fixture.detectChanges(); expect(fixture.nativeElement).toHaveText(text); } - afterEach(() => { fixture = null !; }); + afterEach(() => { + fixture = null!; + }); beforeEach(() => { TestBed.configureTestingModule({ @@ -118,13 +122,14 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); describe('corner cases', () => { - it('should not create the default case if another case matches', () => { const log: string[] = []; @Directive({selector: '[test]'}) class TestDirective { - constructor(@Attribute('test') test: string) { log.push(test); } + constructor(@Attribute('test') test: string) { + log.push(test); + } } const template = '<div [ngSwitch]="switchValue">' + @@ -149,7 +154,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; fixture = createTestComponent(template); detectChangesAndExpectText('when default1;when default2;'); - }); it('should allow defaults before cases', () => { @@ -223,8 +227,8 @@ class TestComponent { ` }) class ComplexComponent { - @ViewChild('foo', {static: true}) foo !: TemplateRef<any>; - @ViewChild('bar', {static: true}) bar !: TemplateRef<any>; + @ViewChild('foo', {static: true}) foo!: TemplateRef<any>; + @ViewChild('bar', {static: true}) bar!: TemplateRef<any>; state: string = 'case1'; } diff --git a/packages/common/test/directives/ng_template_outlet_spec.ts b/packages/common/test/directives/ng_template_outlet_spec.ts index 57ee45b50976b..b6f6776806e1f 100644 --- a/packages/common/test/directives/ng_template_outlet_spec.ts +++ b/packages/common/test/directives/ng_template_outlet_spec.ts @@ -8,20 +8,24 @@ import {CommonModule} from '@angular/common'; import {Component, ContentChildren, Directive, Injectable, NO_ERRORS_SCHEMA, OnDestroy, QueryList, TemplateRef} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; describe('NgTemplateOutlet', () => { let fixture: ComponentFixture<any>; - function setTplRef(value: any): void { fixture.componentInstance.currentTplRef = value; } + function setTplRef(value: any): void { + fixture.componentInstance.currentTplRef = value; + } function detectChangesAndExpectText(text: string): void { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText(text); } - afterEach(() => { fixture = null as any; }); + afterEach(() => { + fixture = null as any; + }); beforeEach(() => { TestBed.configureTestingModule({ @@ -58,7 +62,7 @@ describe('NgTemplateOutlet', () => { `<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`; fixture = createTestComponent(template); fixture.detectChanges(); - const refs = fixture.debugElement.children[0].references !['refs']; + const refs = fixture.debugElement.children[0].references!['refs']; setTplRef(refs.tplRefs.first); detectChangesAndExpectText('foo'); @@ -74,7 +78,7 @@ describe('NgTemplateOutlet', () => { fixture = createTestComponent(template); fixture.detectChanges(); - const refs = fixture.debugElement.children[0].references !['refs']; + const refs = fixture.debugElement.children[0].references!['refs']; setTplRef(refs.tplRefs.first); detectChangesAndExpectText('foo'); @@ -223,7 +227,7 @@ describe('NgTemplateOutlet', () => { `<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`; fixture = createTestComponent(template); fixture.detectChanges(); - const refs = fixture.debugElement.children[0].references !['refs']; + const refs = fixture.debugElement.children[0].references!['refs']; setTplRef(refs.tplRefs.first); detectChangesAndExpectText('foo'); @@ -236,7 +240,6 @@ describe('NgTemplateOutlet', () => { detectChangesAndExpectText('foo'); }).not.toThrow(); })); - }); @Injectable() @@ -248,19 +251,21 @@ class DestroyedSpyService { class DestroyableCmpt implements OnDestroy { constructor(private _spyService: DestroyedSpyService) {} - ngOnDestroy(): void { this._spyService.destroyed = true; } + ngOnDestroy(): void { + this._spyService.destroyed = true; + } } @Directive({selector: 'tpl-refs', exportAs: 'tplRefs'}) class CaptureTplRefs { // TODO(issue/24571): remove '!'. - @ContentChildren(TemplateRef) tplRefs !: QueryList<TemplateRef<any>>; + @ContentChildren(TemplateRef) tplRefs!: QueryList<TemplateRef<any>>; } @Component({selector: 'test-cmp', template: ''}) class TestComponent { // TODO(issue/24571): remove '!'. - currentTplRef !: TemplateRef<any>; + currentTplRef!: TemplateRef<any>; context: any = {foo: 'bar'}; value = 'bar'; } diff --git a/packages/common/test/directives/non_bindable_spec.ts b/packages/common/test/directives/non_bindable_spec.ts index d77194450cb54..ceda2fb42fd95 100644 --- a/packages/common/test/directives/non_bindable_spec.ts +++ b/packages/common/test/directives/non_bindable_spec.ts @@ -9,13 +9,12 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {Component, Directive} from '@angular/core'; import {ElementRef} from '@angular/core/src/linker/element_ref'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {hasClass} from '@angular/platform-browser/testing/src/browser_util'; import {expect} from '@angular/platform-browser/testing/src/matchers'; { describe('non-bindable', () => { - beforeEach(() => { TestBed.configureTestingModule({ declarations: [TestComponent, TestDirective], @@ -53,13 +52,17 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; @Directive({selector: '[test-dec]'}) class TestDirective { - constructor(el: ElementRef) { el.nativeElement.classList.add('compiled'); } + constructor(el: ElementRef) { + el.nativeElement.classList.add('compiled'); + } } @Component({selector: 'test-cmp', template: ''}) class TestComponent { text: string; - constructor() { this.text = 'foo'; } + constructor() { + this.text = 'foo'; + } } function createTestComponent(template: string): ComponentFixture<TestComponent> { diff --git a/packages/common/test/i18n/format_date_spec.ts b/packages/common/test/i18n/format_date_spec.ts index 7bbe33938888a..b43ac860a7555 100644 --- a/packages/common/test/i18n/format_date_spec.ts +++ b/packages/common/test/i18n/format_date_spec.ts @@ -12,35 +12,50 @@ import localeEnExtra from '@angular/common/locales/extra/en'; import localeHu from '@angular/common/locales/hu'; import localeSr from '@angular/common/locales/sr'; import localeTh from '@angular/common/locales/th'; -import {isDate, toDate, formatDate} from '@angular/common/src/i18n/format_date'; -import {ɵDEFAULT_LOCALE_ID, ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core'; +import {formatDate, isDate, toDate} from '@angular/common/src/i18n/format_date'; +import {ɵDEFAULT_LOCALE_ID, ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; describe('Format date', () => { describe('toDate', () => { - it('should support date', () => { expect(isDate(toDate(new Date()))).toBeTruthy(); }); + it('should support date', () => { + expect(isDate(toDate(new Date()))).toBeTruthy(); + }); - it('should support int', () => { expect(isDate(toDate(123456789))).toBeTruthy(); }); + it('should support int', () => { + expect(isDate(toDate(123456789))).toBeTruthy(); + }); - it('should support numeric strings', - () => { expect(isDate(toDate('123456789'))).toBeTruthy(); }); + it('should support numeric strings', () => { + expect(isDate(toDate('123456789'))).toBeTruthy(); + }); - it('should support decimal strings', - () => { expect(isDate(toDate('123456789.11'))).toBeTruthy(); }); + it('should support decimal strings', () => { + expect(isDate(toDate('123456789.11'))).toBeTruthy(); + }); - it('should support ISO string', - () => { expect(isDate(toDate('2015-06-15T21:43:11Z'))).toBeTruthy(); }); + it('should support ISO string', () => { + expect(isDate(toDate('2015-06-15T21:43:11Z'))).toBeTruthy(); + }); - it('should throw for empty string', () => { expect(() => toDate('')).toThrow(); }); + it('should throw for empty string', () => { + expect(() => toDate('')).toThrow(); + }); - it('should throw for alpha numeric strings', - () => { expect(() => toDate('123456789 hello')).toThrow(); }); + it('should throw for alpha numeric strings', () => { + expect(() => toDate('123456789 hello')).toThrow(); + }); - it('should throw for NaN', () => { expect(() => toDate(Number.NaN)).toThrow(); }); + it('should throw for NaN', () => { + expect(() => toDate(Number.NaN)).toThrow(); + }); - it('should support ISO string without time', - () => { expect(isDate(toDate('2015-01-01'))).toBeTruthy(); }); + it('should support ISO string without time', () => { + expect(isDate(toDate('2015-01-01'))).toBeTruthy(); + }); - it('should throw for objects', () => { expect(() => toDate({} as any)).toThrow(); }); + it('should throw for objects', () => { + expect(() => toDate({} as any)).toThrow(); + }); }); describe('formatDate', () => { @@ -49,7 +64,7 @@ describe('Format date', () => { let date: Date; // Check the transformation of a date into a pattern - function expectDateFormatAs(date: Date | string, pattern: any, output: string): void { + function expectDateFormatAs(date: Date|string, pattern: any, output: string): void { expect(formatDate(date, pattern, ɵDEFAULT_LOCALE_ID)) .toEqual(output, `pattern: "${pattern}"`); } @@ -65,7 +80,9 @@ describe('Format date', () => { afterAll(() => ɵunregisterLocaleData()); - beforeEach(() => { date = new Date(2015, 5, 15, 9, 3, 1, 550); }); + beforeEach(() => { + date = new Date(2015, 5, 15, 9, 3, 1, 550); + }); it('should format each component correctly', () => { const dateFixtures: any = { @@ -185,6 +202,19 @@ describe('Format date', () => { BBBBB: 'mi', }; + const midnightCrossingPeriods: any = { + b: 'night', + bb: 'night', + bbb: 'night', + bbbb: 'night', + bbbbb: 'night', + B: 'at night', + BB: 'at night', + BBB: 'at night', + BBBB: 'at night', + BBBBB: 'at night', + }; + Object.keys(dateFixtures).forEach((pattern: string) => { expectDateFormatAs(date, pattern, dateFixtures[pattern]); }); @@ -192,6 +222,11 @@ describe('Format date', () => { Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => { expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]); }); + + const nightTime = new Date(2015, 5, 15, 2, 3, 1, 550); + Object.keys(midnightCrossingPeriods).forEach(pattern => { + expectDateFormatAs(nightTime, pattern, midnightCrossingPeriods[pattern]); + }); }); it('should format with timezones', () => { @@ -293,7 +328,7 @@ describe('Format date', () => { }); it('should remove bidi control characters', - () => expect(formatDate(date, 'MM/dd/yyyy', ɵDEFAULT_LOCALE_ID) !.length).toEqual(10)); + () => expect(formatDate(date, 'MM/dd/yyyy', ɵDEFAULT_LOCALE_ID)!.length).toEqual(10)); it(`should format the date correctly in various locales`, () => { expect(formatDate(date, 'short', 'de')).toEqual('15.06.15, 09:03'); diff --git a/packages/common/test/i18n/format_number_spec.ts b/packages/common/test/i18n/format_number_spec.ts index c6f384095398b..f16cb9ec1731a 100644 --- a/packages/common/test/i18n/format_number_spec.ts +++ b/packages/common/test/i18n/format_number_spec.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ +import {formatCurrency, formatNumber, formatPercent} from '@angular/common'; +import localeAr from '@angular/common/locales/ar'; import localeEn from '@angular/common/locales/en'; import localeEsUS from '@angular/common/locales/es-US'; import localeFr from '@angular/common/locales/fr'; -import localeAr from '@angular/common/locales/ar'; -import {formatCurrency, formatNumber, formatPercent} from '@angular/common'; +import {ɵDEFAULT_LOCALE_ID, ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; -import {ɵDEFAULT_LOCALE_ID, ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core'; describe('Format number', () => { beforeAll(() => { @@ -44,8 +44,9 @@ describe('Format number', () => { }); describe('transform with custom locales', () => { - it('should return the correct format for es-US', - () => { expect(formatNumber(9999999.99, 'es-US', '1.2-2')).toEqual('9,999,999.99'); }); + it('should return the correct format for es-US', () => { + expect(formatNumber(9999999.99, 'es-US', '1.2-2')).toEqual('9,999,999.99'); + }); }); }); diff --git a/packages/common/test/i18n/locale_data_api_spec.ts b/packages/common/test/i18n/locale_data_api_spec.ts index c4cb78d6f002b..74eb1914e765a 100644 --- a/packages/common/test/i18n/locale_data_api_spec.ts +++ b/packages/common/test/i18n/locale_data_api_spec.ts @@ -6,14 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; - import localeEn from '@angular/common/locales/en'; -import localeFr from '@angular/common/locales/fr'; -import localeZh from '@angular/common/locales/zh'; import localeEnAU from '@angular/common/locales/en-AU'; +import localeFr from '@angular/common/locales/fr'; import localeHe from '@angular/common/locales/he'; -import {getCurrencySymbol, getLocaleDateFormat, FormatWidth, getNumberOfCurrencyDigits, getLocaleDirection} from '../../src/i18n/locale_data_api'; +import localeZh from '@angular/common/locales/zh'; +import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; + +import {FormatWidth, getCurrencySymbol, getLocaleDateFormat, getLocaleDirection, getNumberOfCurrencyDigits} from '../../src/i18n/locale_data_api'; { describe('locale data api', () => { @@ -25,7 +25,9 @@ import {getCurrencySymbol, getLocaleDateFormat, FormatWidth, getNumberOfCurrency ɵregisterLocaleData(localeHe); }); - afterAll(() => { ɵunregisterLocaleData(); }); + afterAll(() => { + ɵunregisterLocaleData(); + }); describe('getting currency symbol', () => { it('should return the correct symbol', () => { @@ -55,16 +57,19 @@ import {getCurrencySymbol, getLocaleDateFormat, FormatWidth, getNumberOfCurrency }); describe('getLastDefinedValue', () => { - it('should find the last defined date format when format not defined', - () => { expect(getLocaleDateFormat('zh', FormatWidth.Long)).toEqual('y年M月d日'); }); + it('should find the last defined date format when format not defined', () => { + expect(getLocaleDateFormat('zh', FormatWidth.Long)).toEqual('y年M月d日'); + }); }); describe('getDirectionality', () => { - it('should have correct direction for rtl languages', - () => { expect(getLocaleDirection('he')).toEqual('rtl'); }); + it('should have correct direction for rtl languages', () => { + expect(getLocaleDirection('he')).toEqual('rtl'); + }); - it('should have correct direction for ltr languages', - () => { expect(getLocaleDirection('en')).toEqual('ltr'); }); + it('should have correct direction for ltr languages', () => { + expect(getLocaleDirection('en')).toEqual('ltr'); + }); }); }); } diff --git a/packages/common/test/i18n/localization_spec.ts b/packages/common/test/i18n/localization_spec.ts index dd078c6d89c4d..01c22b4e6c7d1 100644 --- a/packages/common/test/i18n/localization_spec.ts +++ b/packages/common/test/i18n/localization_spec.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ +import localeFr from '@angular/common/locales/fr'; import localeRo from '@angular/common/locales/ro'; import localeSr from '@angular/common/locales/sr'; import localeZgh from '@angular/common/locales/zgh'; -import localeFr from '@angular/common/locales/fr'; -import {LOCALE_ID, ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core'; -import {TestBed, inject} from '@angular/core/testing'; -import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '@angular/common/src/i18n/localization'; +import {getPluralCategory, NgLocaleLocalization, NgLocalization} from '@angular/common/src/i18n/localization'; +import {LOCALE_ID, ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; +import {inject, TestBed} from '@angular/core/testing'; { describe('l10n', () => { diff --git a/packages/common/test/location/location_spec.ts b/packages/common/test/location/location_spec.ts index 92b876f5f656f..9bee33fcba47f 100644 --- a/packages/common/test/location/location_spec.ts +++ b/packages/common/test/location/location_spec.ts @@ -8,7 +8,7 @@ import {CommonModule, Location, LocationStrategy, PathLocationStrategy, PlatformLocation} from '@angular/common'; import {MockPlatformLocation} from '@angular/common/testing'; -import {TestBed, inject} from '@angular/core/testing'; +import {inject, TestBed} from '@angular/core/testing'; const baseUrl = '/base'; @@ -46,14 +46,18 @@ describe('Location Class', () => { imports: [CommonModule], providers: [ {provide: LocationStrategy, useClass: PathLocationStrategy}, - {provide: PlatformLocation, useFactory: () => { return new MockPlatformLocation(); }}, + { + provide: PlatformLocation, + useFactory: () => { + return new MockPlatformLocation(); + } + }, {provide: Location, useClass: Location, deps: [LocationStrategy, PlatformLocation]}, ] }); }); it('should get the state object', inject([Location], (location: Location) => { - expect(location.getState()).toBe(null); location.go('/test', '', {foo: 'bar'}); @@ -62,7 +66,6 @@ describe('Location Class', () => { })); it('should work after using back button', inject([Location], (location: Location) => { - expect(location.getState()).toBe(null); location.go('/test1', '', {url: 'test1'}); @@ -74,7 +77,6 @@ describe('Location Class', () => { expect(location.getState()).toEqual({url: 'test1'}); })); - }); describe('location.onUrlChange()', () => { @@ -83,7 +85,12 @@ describe('Location Class', () => { imports: [CommonModule], providers: [ {provide: LocationStrategy, useClass: PathLocationStrategy}, - {provide: PlatformLocation, useFactory: () => { return new MockPlatformLocation(); }}, + { + provide: PlatformLocation, + useFactory: () => { + return new MockPlatformLocation(); + } + }, {provide: Location, useClass: Location, deps: [LocationStrategy, PlatformLocation]}, ] }); @@ -95,8 +102,9 @@ describe('Location Class', () => { it('should add registered functions to urlChangeListeners', inject([Location], (location: Location) => { - - function changeListener(url: string, state: unknown) { return undefined; } + function changeListener(url: string, state: unknown) { + return undefined; + } expect((location as any)._urlChangeListeners.length).toBe(0); @@ -104,8 +112,6 @@ describe('Location Class', () => { expect((location as any)._urlChangeListeners.length).toBe(1); expect((location as any)._urlChangeListeners[0]).toEqual(changeListener); - })); - }); }); \ No newline at end of file diff --git a/packages/common/test/pipes/async_pipe_spec.ts b/packages/common/test/pipes/async_pipe_spec.ts index 29f945ef9b86a..0d1635938c78c 100644 --- a/packages/common/test/pipes/async_pipe_spec.ts +++ b/packages/common/test/pipes/async_pipe_spec.ts @@ -15,7 +15,6 @@ import {SpyChangeDetectorRef} from '../spies'; { describe('AsyncPipe', () => { - describe('Observable', () => { let emitter: EventEmitter<any>; let pipe: AsyncPipe; @@ -29,8 +28,9 @@ import {SpyChangeDetectorRef} from '../spies'; }); describe('transform', () => { - it('should return null when subscribing to an observable', - () => { expect(pipe.transform(emitter)).toBe(null); }); + it('should return null when subscribing to an observable', () => { + expect(pipe.transform(emitter)).toBe(null); + }); it('should return the latest available value wrapped', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { @@ -96,8 +96,9 @@ import {SpyChangeDetectorRef} from '../spies'; }); describe('ngOnDestroy', () => { - it('should do nothing when no subscription', - () => { expect(() => pipe.ngOnDestroy()).not.toThrow(); }); + it('should do nothing when no subscription', () => { + expect(() => pipe.ngOnDestroy()).not.toThrow(); + }); it('should dispose of the existing subscription', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { @@ -133,8 +134,9 @@ import {SpyChangeDetectorRef} from '../spies'; }); describe('transform', () => { - it('should return null when subscribing to a promise', - () => { expect(pipe.transform(promise)).toBe(null); }); + it('should return null when subscribing to a promise', () => { + expect(pipe.transform(promise)).toBe(null); + }); it('should return the latest available value', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { @@ -189,8 +191,9 @@ import {SpyChangeDetectorRef} from '../spies'; })); describe('ngOnDestroy', () => { - it('should do nothing when no source', - () => { expect(() => pipe.ngOnDestroy()).not.toThrow(); }); + it('should do nothing when no source', () => { + expect(() => pipe.ngOnDestroy()).not.toThrow(); + }); it('should dispose of the existing source', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { diff --git a/packages/common/test/pipes/case_conversion_pipes_spec.ts b/packages/common/test/pipes/case_conversion_pipes_spec.ts index 22458d785508e..5584f5e468d2b 100644 --- a/packages/common/test/pipes/case_conversion_pipes_spec.ts +++ b/packages/common/test/pipes/case_conversion_pipes_spec.ts @@ -12,41 +12,55 @@ import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from '@angular/common'; describe('LowerCasePipe', () => { let pipe: LowerCasePipe; - beforeEach(() => { pipe = new LowerCasePipe(); }); + beforeEach(() => { + pipe = new LowerCasePipe(); + }); - it('should return lowercase', () => { expect(pipe.transform('FOO')).toEqual('foo'); }); + it('should return lowercase', () => { + expect(pipe.transform('FOO')).toEqual('foo'); + }); it('should lowercase when there is a new value', () => { expect(pipe.transform('FOO')).toEqual('foo'); expect(pipe.transform('BAr')).toEqual('bar'); }); - it('should not support other objects', - () => { expect(() => pipe.transform(<any>{})).toThrowError(); }); + it('should not support other objects', () => { + expect(() => pipe.transform(<any>{})).toThrowError(); + }); }); describe('TitleCasePipe', () => { let pipe: TitleCasePipe; - beforeEach(() => { pipe = new TitleCasePipe(); }); + beforeEach(() => { + pipe = new TitleCasePipe(); + }); - it('should return titlecase', () => { expect(pipe.transform('foo')).toEqual('Foo'); }); + it('should return titlecase', () => { + expect(pipe.transform('foo')).toEqual('Foo'); + }); - it('should return titlecase for subsequent words', - () => { expect(pipe.transform('one TWO Three fouR')).toEqual('One Two Three Four'); }); + it('should return titlecase for subsequent words', () => { + expect(pipe.transform('one TWO Three fouR')).toEqual('One Two Three Four'); + }); - it('should support empty strings', () => { expect(pipe.transform('')).toEqual(''); }); + it('should support empty strings', () => { + expect(pipe.transform('')).toEqual(''); + }); - it('should persist whitespace', - () => { expect(pipe.transform('one two')).toEqual('One Two'); }); + it('should persist whitespace', () => { + expect(pipe.transform('one two')).toEqual('One Two'); + }); it('should titlecase when there is a new value', () => { expect(pipe.transform('bar')).toEqual('Bar'); expect(pipe.transform('foo')).toEqual('Foo'); }); - it('should not capitalize letter after the quotes', - () => { expect(pipe.transform('it\'s complicated')).toEqual('It\'s Complicated'); }); + it('should not capitalize letter after the quotes', () => { + expect(pipe.transform('it\'s complicated')).toEqual('It\'s Complicated'); + }); it('should not treat non-space character as a separator', () => { expect(pipe.transform('one,two,three')).toEqual('One,two,three'); @@ -66,23 +80,29 @@ import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from '@angular/common'; expect(pipe.transform('éric')).toEqual('Éric'); }); - it('should not support other objects', - () => { expect(() => pipe.transform(<any>{})).toThrowError(); }); + it('should not support other objects', () => { + expect(() => pipe.transform(<any>{})).toThrowError(); + }); }); describe('UpperCasePipe', () => { let pipe: UpperCasePipe; - beforeEach(() => { pipe = new UpperCasePipe(); }); + beforeEach(() => { + pipe = new UpperCasePipe(); + }); - it('should return uppercase', () => { expect(pipe.transform('foo')).toEqual('FOO'); }); + it('should return uppercase', () => { + expect(pipe.transform('foo')).toEqual('FOO'); + }); it('should uppercase when there is a new value', () => { expect(pipe.transform('foo')).toEqual('FOO'); expect(pipe.transform('bar')).toEqual('BAR'); }); - it('should not support other objects', - () => { expect(() => pipe.transform(<any>{})).toThrowError(); }); + it('should not support other objects', () => { + expect(() => pipe.transform(<any>{})).toThrowError(); + }); }); } diff --git a/packages/common/test/pipes/date_pipe_spec.ts b/packages/common/test/pipes/date_pipe_spec.ts index d651ceb7d431f..c1542d60e4dbb 100644 --- a/packages/common/test/pipes/date_pipe_spec.ts +++ b/packages/common/test/pipes/date_pipe_spec.ts @@ -10,8 +10,8 @@ import {DatePipe} from '@angular/common'; import localeEn from '@angular/common/locales/en'; import localeEnExtra from '@angular/common/locales/extra/en'; import {PipeResolver} from '@angular/compiler/src/pipe_resolver'; +import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector'; -import {ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core'; { let date: Date; @@ -28,33 +28,44 @@ import {ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core'; }); it('should be marked as pure', () => { - expect(new PipeResolver(new JitReflector()).resolve(DatePipe) !.pure).toEqual(true); + expect(new PipeResolver(new JitReflector()).resolve(DatePipe)!.pure).toEqual(true); }); describe('supports', () => { - it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); }); + it('should support date', () => { + expect(() => pipe.transform(date)).not.toThrow(); + }); - it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); }); + it('should support int', () => { + expect(() => pipe.transform(123456789)).not.toThrow(); + }); - it('should support numeric strings', - () => { expect(() => pipe.transform('123456789')).not.toThrow(); }); + it('should support numeric strings', () => { + expect(() => pipe.transform('123456789')).not.toThrow(); + }); - it('should support decimal strings', - () => { expect(() => pipe.transform('123456789.11')).not.toThrow(); }); + it('should support decimal strings', () => { + expect(() => pipe.transform('123456789.11')).not.toThrow(); + }); it('should support ISO string', () => expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow()); - it('should return null for empty string', - () => { expect(pipe.transform('')).toEqual(null); }); + it('should return null for empty string', () => { + expect(pipe.transform('')).toEqual(null); + }); - it('should return null for NaN', () => { expect(pipe.transform(Number.NaN)).toEqual(null); }); + it('should return null for NaN', () => { + expect(pipe.transform(Number.NaN)).toEqual(null); + }); - it('should support ISO string without time', - () => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); }); + it('should support ISO string without time', () => { + expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); + }); - it('should not support other objects', - () => { expect(() => pipe.transform({})).toThrowError(/InvalidPipeArgument/); }); + it('should not support other objects', () => { + expect(() => pipe.transform({})).toThrowError(/InvalidPipeArgument/); + }); }); describe('transform', () => { diff --git a/packages/common/test/pipes/i18n_plural_pipe_spec.ts b/packages/common/test/pipes/i18n_plural_pipe_spec.ts index 2941584b3cf8b..104716b195d21 100644 --- a/packages/common/test/pipes/i18n_plural_pipe_spec.ts +++ b/packages/common/test/pipes/i18n_plural_pipe_spec.ts @@ -29,7 +29,7 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle }); it('should be marked as pure', () => { - expect(new PipeResolver(new JitReflector()).resolve(I18nPluralPipe) !.pure).toEqual(true); + expect(new PipeResolver(new JitReflector()).resolve(I18nPluralPipe)!.pure).toEqual(true); }); describe('transform', () => { @@ -54,17 +54,19 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle }); it('should use "" if value is undefined', () => { - const val = pipe.transform(void(0) as any, mapping); + const val = pipe.transform(void (0) as any, mapping); expect(val).toEqual(''); }); - it('should not support bad arguments', - () => { expect(() => pipe.transform(0, <any>'hey')).toThrowError(); }); + it('should not support bad arguments', () => { + expect(() => pipe.transform(0, <any>'hey')).toThrowError(); + }); }); - }); } class TestLocalization extends NgLocalization { - getPluralCategory(value: number): string { return value > 1 && value < 6 ? 'many' : 'other'; } + getPluralCategory(value: number): string { + return value > 1 && value < 6 ? 'many' : 'other'; + } } diff --git a/packages/common/test/pipes/i18n_select_pipe_spec.ts b/packages/common/test/pipes/i18n_select_pipe_spec.ts index 2d68a237d325c..998ab7fd40a19 100644 --- a/packages/common/test/pipes/i18n_select_pipe_spec.ts +++ b/packages/common/test/pipes/i18n_select_pipe_spec.ts @@ -16,7 +16,7 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle const mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'}; it('should be marked as pure', () => { - expect(new PipeResolver(new JitReflector()).resolve(I18nSelectPipe) !.pure).toEqual(true); + expect(new PipeResolver(new JitReflector()).resolve(I18nSelectPipe)!.pure).toEqual(true); }); describe('transform', () => { @@ -30,17 +30,18 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle expect(val).toEqual('Invite her.'); }); - it('should return the "other" text if value is neither "male" nor "female"', - () => { expect(pipe.transform('Anything else', mapping)).toEqual('Invite them.'); }); + it('should return the "other" text if value is neither "male" nor "female"', () => { + expect(pipe.transform('Anything else', mapping)).toEqual('Invite them.'); + }); it('should return an empty text if value is null or undefined', () => { expect(pipe.transform(null, mapping)).toEqual(''); expect(pipe.transform(void 0, mapping)).toEqual(''); }); - it('should throw on bad arguments', - () => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); }); + it('should throw on bad arguments', () => { + expect(() => pipe.transform('male', <any>'hey')).toThrowError(); + }); }); - }); } diff --git a/packages/common/test/pipes/json_pipe_spec.ts b/packages/common/test/pipes/json_pipe_spec.ts index a9f5e4637d7bc..b1a9fb5bb58ec 100644 --- a/packages/common/test/pipes/json_pipe_spec.ts +++ b/packages/common/test/pipes/json_pipe_spec.ts @@ -8,7 +8,7 @@ import {CommonModule, JsonPipe} from '@angular/common'; import {Component} from '@angular/core'; -import {TestBed, async} from '@angular/core/testing'; +import {async, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; { @@ -18,7 +18,9 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; let inceptionObjString: string; let pipe: JsonPipe; - function normalize(obj: string): string { return obj.replace(regNewLine, ''); } + function normalize(obj: string): string { + return obj.replace(regNewLine, ''); + } beforeEach(() => { inceptionObj = {dream: {dream: {dream: 'Limbo'}}}; @@ -35,8 +37,9 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); describe('transform', () => { - it('should return JSON-formatted string', - () => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); }); + it('should return JSON-formatted string', () => { + expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); + }); it('should return JSON-formatted string even when normalized', () => { const dream1 = normalize(pipe.transform(inceptionObj)); @@ -52,7 +55,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); describe('integration', () => { - @Component({selector: 'test-comp', template: '{{data | json}}'}) class TestComp { data: any; diff --git a/packages/common/test/pipes/keyvalue_pipe_spec.ts b/packages/common/test/pipes/keyvalue_pipe_spec.ts index 082fce6712212..e4af0fc130760 100644 --- a/packages/common/test/pipes/keyvalue_pipe_spec.ts +++ b/packages/common/test/pipes/keyvalue_pipe_spec.ts @@ -63,6 +63,16 @@ describe('KeyValuePipe', () => { const transform2 = pipe.transform({1: 3}); expect(transform1 !== transform2).toEqual(true); }); + it('should accept a type union of an object with string keys and null', () => { + let value!: {[key: string]: string}|null; + const pipe = new KeyValuePipe(defaultKeyValueDiffers); + expect(pipe.transform(value)).toEqual(null); + }); + it('should accept a type union of an object with number keys and null', () => { + let value!: {[key: number]: string}|null; + const pipe = new KeyValuePipe(defaultKeyValueDiffers); + expect(pipe.transform(value)).toEqual(null); + }); }); describe('Map', () => { @@ -115,6 +125,11 @@ describe('KeyValuePipe', () => { const transform2 = pipe.transform(new Map([[1, 3]])); expect(transform1 !== transform2).toEqual(true); }); + it('should accept a type union of a Map and null', () => { + let value!: Map<number, number>|null; + const pipe = new KeyValuePipe(defaultKeyValueDiffers); + expect(pipe.transform(value)).toEqual(null); + }); }); }); diff --git a/packages/common/test/pipes/number_pipe_spec.ts b/packages/common/test/pipes/number_pipe_spec.ts index c0a456451d6b9..a57690451018e 100644 --- a/packages/common/test/pipes/number_pipe_spec.ts +++ b/packages/common/test/pipes/number_pipe_spec.ts @@ -6,14 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ +import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common'; +import localeAr from '@angular/common/locales/ar'; +import localeDa from '@angular/common/locales/da'; +import localeDeAt from '@angular/common/locales/de-AT'; import localeEn from '@angular/common/locales/en'; import localeEsUS from '@angular/common/locales/es-US'; import localeFr from '@angular/common/locales/fr'; -import localeAr from '@angular/common/locales/ar'; -import localeDeAt from '@angular/common/locales/de-AT'; -import localeDa from '@angular/common/locales/da'; -import {ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core'; -import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common'; +import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; { @@ -32,7 +32,9 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin describe('DecimalPipe', () => { describe('transform', () => { let pipe: DecimalPipe; - beforeEach(() => { pipe = new DecimalPipe('en-US'); }); + beforeEach(() => { + pipe = new DecimalPipe('en-US'); + }); it('should return correct value for numbers', () => { expect(pipe.transform(12345)).toEqual('12,345'); @@ -68,7 +70,9 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin describe('PercentPipe', () => { let pipe: PercentPipe; - beforeEach(() => { pipe = new PercentPipe('en-US'); }); + beforeEach(() => { + pipe = new PercentPipe('en-US'); + }); describe('transform', () => { it('should return correct value for numbers', () => { @@ -89,7 +93,9 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin describe('CurrencyPipe', () => { let pipe: CurrencyPipe; - beforeEach(() => { pipe = new CurrencyPipe('en-US', 'USD'); }); + beforeEach(() => { + pipe = new CurrencyPipe('en-US', 'USD'); + }); describe('transform', () => { it('should return correct value for numbers', () => { diff --git a/packages/common/test/pipes/slice_pipe_spec.ts b/packages/common/test/pipes/slice_pipe_spec.ts index 63c79f188067a..e86edf75651a8 100644 --- a/packages/common/test/pipes/slice_pipe_spec.ts +++ b/packages/common/test/pipes/slice_pipe_spec.ts @@ -8,7 +8,7 @@ import {CommonModule, SlicePipe} from '@angular/common'; import {Component} from '@angular/core'; -import {TestBed, async} from '@angular/core/testing'; +import {async, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; { @@ -24,24 +24,32 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); describe('supports', () => { - it('should support strings', () => { expect(() => pipe.transform(str, 0)).not.toThrow(); }); - it('should support lists', () => { expect(() => pipe.transform(list, 0)).not.toThrow(); }); - it('should support readonly lists', - () => { expect(() => pipe.transform(list as ReadonlyArray<number>, 0)).not.toThrow(); }); + it('should support strings', () => { + expect(() => pipe.transform(str, 0)).not.toThrow(); + }); + it('should support lists', () => { + expect(() => pipe.transform(list, 0)).not.toThrow(); + }); + it('should support readonly lists', () => { + expect(() => pipe.transform(list as ReadonlyArray<number>, 0)).not.toThrow(); + }); it('should not support other objects', // this would not compile // so we cast as `any` to check that it throws for unsupported objects - () => { expect(() => pipe.transform({} as any, 0)).toThrow(); }); + () => { + expect(() => pipe.transform({} as any, 0)).toThrow(); + }); }); describe('transform', () => { + it('should return null if the value is null', () => { + expect(pipe.transform(null, 1)).toBe(null); + }); - it('should return null if the value is null', - () => { expect(pipe.transform(null, 1)).toBe(null); }); - - it('should return undefined if the value is undefined', - () => { expect(pipe.transform(undefined, 1)).toBe(undefined); }); + it('should return undefined if the value is undefined', () => { + expect(pipe.transform(undefined, 1)).toBe(undefined); + }); it('should return all items after START index when START is positive and END is omitted', () => { @@ -85,11 +93,9 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; expect(pipe.transform(list, 2)).toEqual([3, 4, 5]); expect(list).toEqual([1, 2, 3, 4, 5]); }); - }); describe('integration', () => { - @Component({selector: 'test-comp', template: '{{(data | slice:1).join(",") }}'}) class TestComp { data: any; diff --git a/packages/common/test/spies.ts b/packages/common/test/spies.ts index e956a4821c474..e889b9431e49c 100644 --- a/packages/common/test/spies.ts +++ b/packages/common/test/spies.ts @@ -18,4 +18,6 @@ export class SpyChangeDetectorRef extends SpyObject { export class SpyNgControl extends SpyObject {} -export class SpyValueAccessor extends SpyObject { writeValue: any; } +export class SpyValueAccessor extends SpyObject { + writeValue: any; +} diff --git a/packages/common/test/viewport_scroller_spec.ts b/packages/common/test/viewport_scroller_spec.ts index 281ef96d37d47..fd0485546a0ea 100644 --- a/packages/common/test/viewport_scroller_spec.ts +++ b/packages/common/test/viewport_scroller_spec.ts @@ -9,12 +9,12 @@ /** -* @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 -*/ + * @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 {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {BrowserViewportScroller, ViewportScroller} from '../src/viewport_scroller'; @@ -25,7 +25,7 @@ import {BrowserViewportScroller, ViewportScroller} from '../src/viewport_scrolle let documentSpy: any; beforeEach(() => { documentSpy = jasmine.createSpyObj('document', ['querySelector']); - scroller = new BrowserViewportScroller(documentSpy, {scrollTo: 1}, null !); + scroller = new BrowserViewportScroller(documentSpy, {scrollTo: 1}, null!); }); it('escapes invalid characters selectors', () => { const invalidSelectorChars = `"' :.[],=`; diff --git a/packages/common/testing/src/location_mock.ts b/packages/common/testing/src/location_mock.ts index a5efc1ffb318b..b02d5e2b2fd05 100644 --- a/packages/common/testing/src/location_mock.ts +++ b/packages/common/testing/src/location_mock.ts @@ -25,19 +25,27 @@ export class SpyLocation implements Location { /** @internal */ _baseHref: string = ''; /** @internal */ - _platformStrategy: LocationStrategy = null !; + _platformStrategy: LocationStrategy = null!; /** @internal */ - _platformLocation: PlatformLocation = null !; + _platformLocation: PlatformLocation = null!; /** @internal */ _urlChangeListeners: ((url: string, state: unknown) => void)[] = []; - setInitialPath(url: string) { this._history[this._historyIndex].path = url; } + setInitialPath(url: string) { + this._history[this._historyIndex].path = url; + } - setBaseHref(url: string) { this._baseHref = url; } + setBaseHref(url: string) { + this._baseHref = url; + } - path(): string { return this._history[this._historyIndex].path; } + path(): string { + return this._history[this._historyIndex].path; + } - getState(): unknown { return this._history[this._historyIndex].state; } + getState(): unknown { + return this._history[this._historyIndex].state; + } isCurrentPathEqualTo(path: string, query: string = ''): boolean { const givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path; @@ -115,7 +123,9 @@ export class SpyLocation implements Location { } onUrlChange(fn: (url: string, state: unknown) => void) { this._urlChangeListeners.push(fn); - this.subscribe(v => { this._notifyUrlChangeListeners(v.url, v.state); }); + this.subscribe(v => { + this._notifyUrlChangeListeners(v.url, v.state); + }); } /** @internal */ @@ -129,7 +139,9 @@ export class SpyLocation implements Location { return this._subject.subscribe({next: onNext, error: onThrow, complete: onReturn}); } - normalize(url: string): string { return null !; } + normalize(url: string): string { + return null!; + } } class LocationState { diff --git a/packages/common/testing/src/mock_location_strategy.ts b/packages/common/testing/src/mock_location_strategy.ts index 2c38f8d39f591..8cd329aa64f25 100644 --- a/packages/common/testing/src/mock_location_strategy.ts +++ b/packages/common/testing/src/mock_location_strategy.ts @@ -26,14 +26,18 @@ export class MockLocationStrategy extends LocationStrategy { /** @internal */ _subject: EventEmitter<any> = new EventEmitter(); private stateChanges: any[] = []; - constructor() { super(); } + constructor() { + super(); + } simulatePopState(url: string): void { this.internalPath = url; this._subject.emit(new _MockPopStateEvent(this.path())); } - path(includeHash: boolean = false): string { return this.internalPath; } + path(includeHash: boolean = false): string { + return this.internalPath; + } prepareExternalUrl(internal: string): string { if (internal.startsWith('/') && this.internalBaseHref.endsWith('/')) { @@ -68,9 +72,13 @@ export class MockLocationStrategy extends LocationStrategy { this.urlChanges.push('replace: ' + externalUrl); } - onPopState(fn: (value: any) => void): void { this._subject.subscribe({next: fn}); } + onPopState(fn: (value: any) => void): void { + this._subject.subscribe({next: fn}); + } - getBaseHref(): string { return this.internalBaseHref; } + getBaseHref(): string { + return this.internalBaseHref; + } back(): void { if (this.urlChanges.length > 0) { @@ -81,9 +89,13 @@ export class MockLocationStrategy extends LocationStrategy { } } - forward(): void { throw 'not implemented'; } + forward(): void { + throw 'not implemented'; + } - getState(): unknown { return this.stateChanges[(this.stateChanges.length || 1) - 1]; } + getState(): unknown { + return this.stateChanges[(this.stateChanges.length || 1) - 1]; + } } class _MockPopStateEvent { diff --git a/packages/common/testing/src/mock_platform_location.ts b/packages/common/testing/src/mock_platform_location.ts index 4f7c15602252f..4cbb5a677ceb9 100644 --- a/packages/common/testing/src/mock_platform_location.ts +++ b/packages/common/testing/src/mock_platform_location.ts @@ -126,23 +126,41 @@ export class MockPlatformLocation implements PlatformLocation { } } - get hostname() { return this.urlChanges[0].hostname; } - get protocol() { return this.urlChanges[0].protocol; } - get port() { return this.urlChanges[0].port; } - get pathname() { return this.urlChanges[0].pathname; } - get search() { return this.urlChanges[0].search; } - get hash() { return this.urlChanges[0].hash; } - get state() { return this.urlChanges[0].state; } + get hostname() { + return this.urlChanges[0].hostname; + } + get protocol() { + return this.urlChanges[0].protocol; + } + get port() { + return this.urlChanges[0].port; + } + get pathname() { + return this.urlChanges[0].pathname; + } + get search() { + return this.urlChanges[0].search; + } + get hash() { + return this.urlChanges[0].hash; + } + get state() { + return this.urlChanges[0].state; + } - getBaseHrefFromDOM(): string { return this.baseHref; } + getBaseHrefFromDOM(): string { + return this.baseHref; + } onPopState(fn: LocationChangeListener): void { // No-op: a state stack is not implemented, so // no events will ever come. } - onHashChange(fn: LocationChangeListener): void { this.hashUpdate.subscribe(fn); } + onHashChange(fn: LocationChangeListener): void { + this.hashUpdate.subscribe(fn); + } get href(): string { let url = `${this.protocol}//${this.hostname}${this.port ? ':' + this.port : ''}`; @@ -150,7 +168,9 @@ export class MockPlatformLocation implements PlatformLocation { return url; } - get url(): string { return `${this.pathname}${this.search}${this.hash}`; } + get url(): string { + return `${this.pathname}${this.search}${this.hash}`; + } private parseChanges(state: unknown, url: string, baseHref: string = '') { // When the `history.state` value is stored, it is always copied. @@ -169,7 +189,9 @@ export class MockPlatformLocation implements PlatformLocation { this.urlChanges.unshift({...this.urlChanges[0], pathname, search, hash, state: parsedState}); } - forward(): void { throw new Error('Not implemented'); } + forward(): void { + throw new Error('Not implemented'); + } back(): void { const oldUrl = this.url; @@ -178,13 +200,15 @@ export class MockPlatformLocation implements PlatformLocation { const newHash = this.hash; if (oldHash !== newHash) { - scheduleMicroTask(() => this.hashUpdate.next({ - type: 'hashchange', state: null, oldUrl, newUrl: this.url - } as LocationChangeEvent)); + scheduleMicroTask( + () => this.hashUpdate.next( + {type: 'hashchange', state: null, oldUrl, newUrl: this.url} as LocationChangeEvent)); } } - getState(): unknown { return this.state; } + getState(): unknown { + return this.state; + } } export function scheduleMicroTask(cb: () => any) { diff --git a/packages/common/upgrade/src/location_shim.ts b/packages/common/upgrade/src/location_shim.ts index 7f03b46c0b360..de4d3f3a8aa1c 100644 --- a/packages/common/upgrade/src/location_shim.ts +++ b/packages/common/upgrade/src/location_shim.ts @@ -8,6 +8,7 @@ import {Location, LocationStrategy, PlatformLocation} from '@angular/common'; import {UpgradeModule} from '@angular/upgrade/static'; +import {ReplaySubject} from 'rxjs'; import {UrlCodec} from './params'; import {deepEqual, isAnchor, isPromise} from './utils'; @@ -50,7 +51,7 @@ export class $locationShim { private cachedState: unknown = null; - + private urlChanges = new ReplaySubject<{newUrl: string, newState: unknown}>(1); constructor( $injector: any, private location: Location, private platformLocation: PlatformLocation, @@ -71,6 +72,10 @@ export class $locationShim { this.cacheState(); this.$$state = this.browserState(); + this.location.onUrlChange((newUrl, newState) => { + this.urlChanges.next({newUrl, newState}); + }); + if (isPromise($injector)) { $injector.then($i => this.initialize($i)); } else { @@ -88,7 +93,7 @@ export class $locationShim { return; } - let elm: (Node & ParentNode)|null = event.target; + let elm: (Node&ParentNode)|null = event.target; // traverse the DOM up to find first A tag while (elm && elm.nodeName.toLowerCase() !== 'a') { @@ -124,9 +129,9 @@ export class $locationShim { } }); - this.location.onUrlChange((newUrl, newState) => { - let oldUrl = this.absUrl(); - let oldState = this.$$state; + this.urlChanges.subscribe(({newUrl, newState}) => { + const oldUrl = this.absUrl(); + const oldState = this.$$state; this.$$parse(newUrl); newUrl = this.absUrl(); this.$$state = newState; @@ -286,7 +291,9 @@ export class $locationShim { * This function emulates the $browser.state() function from AngularJS. It will cause * history.state to be cached unless changed with deep equality check. */ - private browserState(): unknown { return this.cachedState; } + private browserState(): unknown { + return this.cachedState; + } private stripBaseUrl(base: string, url: string) { if (url.startsWith(base)) { @@ -446,7 +453,9 @@ export class $locationShim { * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" * ``` */ - absUrl(): string { return this.$$absUrl; } + absUrl(): string { + return this.$$absUrl; + } /** * Retrieves the current URL, or sets a new URL. When setting a URL, @@ -488,7 +497,9 @@ export class $locationShim { * // => "http" * ``` */ - protocol(): string { return this.$$protocol; } + protocol(): string { + return this.$$protocol; + } /** * Retrieves the protocol of the current URL. @@ -509,7 +520,9 @@ export class $locationShim { * // => "example.com:8080" * ``` */ - host(): string { return this.$$host; } + host(): string { + return this.$$host; + } /** * Retrieves the port of the current URL. @@ -520,7 +533,9 @@ export class $locationShim { * // => 80 * ``` */ - port(): number|null { return this.$$port; } + port(): number|null { + return this.$$port; + } /** * Retrieves the path of the current URL, or changes the path and returns a reference to its own @@ -553,7 +568,7 @@ export class $locationShim { } /** - * Retrieves a map of the search parameters of the current URL, or changes a search + * Retrieves a map of the search parameters of the current URL, or changes a search * part and returns a reference to its own instance. * * @@ -576,7 +591,8 @@ export class $locationShim { * If the argument is a hash object containing an array of values, these values will be encoded * as duplicate search parameters in the URL. * - * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue` + * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, + * then `paramValue` * will override only a single search property. * * If `paramValue` is an array, it will override the property of the `search` component of diff --git a/packages/common/upgrade/src/location_upgrade_module.ts b/packages/common/upgrade/src/location_upgrade_module.ts index 720dd986a0125..d11f9dfd23eb2 100644 --- a/packages/common/upgrade/src/location_upgrade_module.ts +++ b/packages/common/upgrade/src/location_upgrade_module.ts @@ -55,7 +55,7 @@ const APP_BASE_HREF_RESOLVED = new InjectionToken<string>('APP_BASE_HREF_RESOLVE /** * `NgModule` used for providing and configuring Angular's Unified Location Service for upgrading. - * + * * @see [Using the Unified Angular Location Service](guide/upgrade#using-the-unified-angular-location-service) * * @publicApi diff --git a/packages/common/upgrade/src/params.ts b/packages/common/upgrade/src/params.ts index ac9fe5f7d64b6..587816ecb0e92 100644 --- a/packages/common/upgrade/src/params.ts +++ b/packages/common/upgrade/src/params.ts @@ -152,7 +152,9 @@ export class AngularJSUrlCodec implements UrlCodec { } // https://github.com/angular/angular.js/blob/864c7f0/src/ng/location.js#L72 - decodeSearch(search: string) { return parseKeyValue(search); } + decodeSearch(search: string) { + return parseKeyValue(search); + } // https://github.com/angular/angular.js/blob/864c7f0/src/ng/location.js#L73 decodeHash(hash: string) { @@ -193,7 +195,9 @@ export class AngularJSUrlCodec implements UrlCodec { } } - areEqual(valA: string, valB: string) { return this.normalize(valA) === this.normalize(valB); } + areEqual(valA: string, valB: string) { + return this.normalize(valA) === this.normalize(valB); + } // https://github.com/angular/angular.js/blob/864c7f0/src/ng/urlUtils.js#L60 parse(url: string, base?: string) { diff --git a/packages/common/upgrade/src/utils.ts b/packages/common/upgrade/src/utils.ts index 48cba8e2fee97..0ce909ecda499 100644 --- a/packages/common/upgrade/src/utils.ts +++ b/packages/common/upgrade/src/utils.ts @@ -27,7 +27,7 @@ export function deepEqual(a: any, b: any): boolean { } } -export function isAnchor(el: (Node & ParentNode) | Element | null): el is HTMLAnchorElement { +export function isAnchor(el: (Node&ParentNode)|Element|null): el is HTMLAnchorElement { return (<HTMLAnchorElement>el).href !== undefined; } diff --git a/packages/common/upgrade/test/upgrade.spec.ts b/packages/common/upgrade/test/upgrade.spec.ts index cab39addc56b3..47d3af76d32aa 100644 --- a/packages/common/upgrade/test/upgrade.spec.ts +++ b/packages/common/upgrade/test/upgrade.spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule, PathLocationStrategy} from '@angular/common'; -import {TestBed, inject} from '@angular/core/testing'; +import {inject, TestBed} from '@angular/core/testing'; import {UpgradeModule} from '@angular/upgrade/static'; import {$locationShim} from '../src/location_shim'; @@ -43,15 +43,26 @@ export function injectorFactory() { export class $rootScopeMock { private watchers: any[] = []; private events: {[k: string]: any[]} = {}; - runWatchers() { this.watchers.forEach(fn => fn()); } + runWatchers() { + this.watchers.forEach(fn => fn()); + } - $watch(fn: any) { this.watchers.push(fn); } + $watch(fn: any) { + this.watchers.push(fn); + } $broadcast(evt: string, ...args: any[]) { if (this.events[evt]) { - this.events[evt].forEach(fn => { fn.apply(fn, args); }); + this.events[evt].forEach(fn => { + fn.apply(fn, args); + }); } - return {defaultPrevented: false, preventDefault() { this.defaultPrevented = true; }}; + return { + defaultPrevented: false, + preventDefault() { + this.defaultPrevented = true; + } + }; } $on(evt: string, fn: any) { @@ -61,7 +72,9 @@ export class $rootScopeMock { this.events[evt].push(fn); } - $evalAsync(fn: any) { fn(); } + $evalAsync(fn: any) { + fn(); + } } describe('LocationProvider', () => { @@ -83,7 +96,6 @@ describe('LocationProvider', () => { expect($location).toBeDefined(); expect($location instanceof $locationShim).toBe(true); })); - }); @@ -105,7 +117,9 @@ describe('LocationHtml5Url', function() { upgradeModule.$injector = {get: injectorFactory()}; }); - beforeEach(inject([$locationShim], (loc: $locationShim) => { $location = loc; })); + beforeEach(inject([$locationShim], (loc: $locationShim) => { + $location = loc; + })); it('should set the URL', () => { @@ -158,8 +172,9 @@ describe('LocationHtml5Url', function() { }).toThrow(); }); - it('should support state', - function() { expect($location.state({a: 2}).state()).toEqual({a: 2}); }); + it('should support state', function() { + expect($location.state({a: 2}).state()).toEqual({a: 2}); + }); }); @@ -180,10 +195,14 @@ describe('NewUrl', function() { upgradeModule.$injector = {get: injectorFactory()}; }); - beforeEach(inject([$locationShim], (loc: $locationShim) => { $location = loc; })); + beforeEach(inject([$locationShim], (loc: $locationShim) => { + $location = loc; + })); // Sets the default most of these tests rely on - function setupUrl(url = '/path/b?search=a&b=c&d#hash') { $location.url(url); } + function setupUrl(url = '/path/b?search=a&b=c&d#hash') { + $location.url(url); + } it('should provide common getters', function() { setupUrl(); @@ -409,7 +428,6 @@ describe('NewUrl', function() { }); describe('encoding', function() { - it('should encode special characters', function() { $location.path('/a <>#'); $location.search({'i j': '<>#'}); @@ -455,7 +473,6 @@ describe('NewUrl', function() { $location.search({'a+b': 'c+d'}); expect($location.search()).toEqual({'a+b': 'c+d'}); }); - }); it('should not preserve old properties when parsing new url', function() { @@ -486,7 +503,9 @@ describe('New URL Parsing', () => { upgradeModule.$injector = {get: injectorFactory()}; }); - beforeEach(inject([$locationShim], (loc: $locationShim) => { $location = loc; })); + beforeEach(inject([$locationShim], (loc: $locationShim) => { + $location = loc; + })); it('should prepend path with basePath', function() { $location.$$parse('http://server/base/abc?a'); @@ -496,7 +515,6 @@ describe('New URL Parsing', () => { $location.path('/new/path'); expect($location.absUrl()).toBe('http://server/base/new/path?a'); }); - }); describe('New URL Parsing', () => { @@ -516,7 +534,9 @@ describe('New URL Parsing', () => { upgradeModule.$injector = {get: injectorFactory()}; }); - beforeEach(inject([$locationShim], (loc: $locationShim) => { $location = loc; })); + beforeEach(inject([$locationShim], (loc: $locationShim) => { + $location = loc; + })); it('should parse new url', function() { $location.$$parse('http://host.com/base'); @@ -549,8 +569,9 @@ describe('New URL Parsing', () => { }); it('should throw error when invalid server url given', function() { - - expect(function() { $location.$$parse('http://other.server.org/path#/path'); }) + expect(function() { + $location.$$parse('http://other.server.org/path#/path'); + }) .toThrowError( 'Invalid url "http://other.server.org/path#/path", missing path prefix "http://host.com/".'); }); @@ -620,12 +641,10 @@ describe('New URL Parsing', () => { // After watchers have been run, location should be updated and `state` should change expect($location.state()).toBe(null); }); - }); }); describe('$location.onChange()', () => { - let $location: $locationShim; let upgradeModule: UpgradeModule; let mock$rootScope: $rootScopeMock; @@ -644,13 +663,18 @@ describe('$location.onChange()', () => { mock$rootScope = upgradeModule.$injector.get('$rootScope'); }); - beforeEach(inject([$locationShim], (loc: $locationShim) => { $location = loc; })); + beforeEach(inject([$locationShim], (loc: $locationShim) => { + $location = loc; + })); - it('should have onChange method', () => { expect(typeof $location.onChange).toBe('function'); }); + it('should have onChange method', () => { + expect(typeof $location.onChange).toBe('function'); + }); it('should add registered functions to changeListeners', () => { - - function changeListener(url: string, state: unknown) { return undefined; } + function changeListener(url: string, state: unknown) { + return undefined; + } function errorHandler(e: Error) {} expect(($location as any).$$changeListeners.length).toBe(0); @@ -663,7 +687,6 @@ describe('$location.onChange()', () => { }); it('should call changeListeners when URL is updated', () => { - const onChangeVals = {url: 'url', state: 'state' as unknown, oldUrl: 'oldUrl', oldState: 'oldState' as unknown}; @@ -688,7 +711,6 @@ describe('$location.onChange()', () => { }); it('should call changeListeners after $locationChangeSuccess', () => { - let changeListenerCalled = false; let locationChangeSuccessEmitted = false; @@ -715,13 +737,14 @@ describe('$location.onChange()', () => { }); it('should call forward errors to error handler', () => { - - let error !: Error; + let error!: Error; function changeListener(url: string, state: unknown, oldUrl: string, oldState: unknown) { throw new Error('Handle error'); } - function errorHandler(e: Error) { error = e; } + function errorHandler(e: Error) { + error = e; + } $location.onChange(changeListener, errorHandler); @@ -729,7 +752,6 @@ describe('$location.onChange()', () => { mock$rootScope.runWatchers(); expect(error.message).toBe('Handle error'); }); - }); function parseLinkAndReturn(location: $locationShim, toUrl: string, relHref?: string) { diff --git a/packages/common/upgrade/test/upgrade_location_test_module.ts b/packages/common/upgrade/test/upgrade_location_test_module.ts index efcb0ab607bd5..52f093c64d5fc 100644 --- a/packages/common/upgrade/test/upgrade_location_test_module.ts +++ b/packages/common/upgrade/test/upgrade_location_test_module.ts @@ -72,7 +72,7 @@ export class LocationUpgradeTestModule { appBaseHref: config && config.appBaseHref, useHash: config && config.useHash || false }) - .providers ! + .providers! ], }; } diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/dep.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/dep.ts index 4cc1c02b7d985..2e3c21564bae7 100644 --- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/dep.ts +++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/dep.ts @@ -20,7 +20,9 @@ export class NormalService { }) export class AppComponent { found: boolean; - constructor(service: ShakeableService) { this.found = !!service.normal; } + constructor(service: ShakeableService) { + this.found = !!service.normal; + } } @NgModule({ diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/hierarchy.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/hierarchy.ts index 86daf887989b8..2465c74ad0385 100644 --- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/hierarchy.ts +++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/hierarchy.ts @@ -29,7 +29,9 @@ export class AppComponent { export class ChildComponent { found: boolean; - constructor(@Optional() @Self() service: Service|null) { this.found = !!service; } + constructor(@Optional() @Self() service: Service|null) { + this.found = !!service; + } } @NgModule({ diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/self.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/self.ts index fb37c76e40657..324b05931541d 100644 --- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/self.ts +++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/self.ts @@ -21,7 +21,9 @@ export class NormalService { }) export class AppComponent { found: boolean; - constructor(service: NormalService) { this.found = !!service.shakeable; } + constructor(service: NormalService) { + this.found = !!service.shakeable; + } } @NgModule({ diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/string.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/string.ts index fca98267bd6eb..3187a87e9b44a 100644 --- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/string.ts +++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/string.ts @@ -17,7 +17,9 @@ import {ServerModule} from '@angular/platform-server'; }) export class AppComponent { data: string; - constructor(service: Service) { this.data = service.data; } + constructor(service: Service) { + this.data = service.data; + } } @NgModule({ diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/token.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/token.ts index 3b93c3e5d8591..e3ca53e5d465a 100644 --- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/token.ts +++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/token.ts @@ -6,11 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Inject, Injectable, InjectionToken, NgModule, forwardRef, inject} from '@angular/core'; +import {Component, forwardRef, Inject, inject, Injectable, InjectionToken, NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {ServerModule} from '@angular/platform-server'; -export interface IService { readonly dep: {readonly data: string;}; } +export interface IService { + readonly dep: {readonly data: string;}; +} @NgModule({}) export class TokenModule { @@ -28,7 +30,9 @@ export const TOKEN = new InjectionToken('test', { }) export class AppComponent { data: string; - constructor(@Inject(TOKEN) service: IService) { this.data = service.dep.data; } + constructor(@Inject(TOKEN) service: IService) { + this.data = service.dep.data; + } } @NgModule({ diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts index 793a27c4d6891..5311662b1062b 100644 --- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts +++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, INJECTOR, Injectable, NgModule} from '@angular/core'; +import {Component, Injectable, INJECTOR, NgModule} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {renderModuleFactory} from '@angular/platform-server'; import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory'; @@ -104,7 +104,6 @@ describe('ngInjectableDef Bazel Integration', () => { }); it('allows provider override in JIT for module-scoped @Injectables', () => { - @NgModule() class Module { } @@ -172,7 +171,9 @@ describe('ngInjectableDef Bazel Integration', () => { // ChildServices exteds ParentService but does not have @Injectable class ChildService extends ParentService { - constructor(value: string) { super(value); } + constructor(value: string) { + super(value); + } static ngInjectableDef = { providedIn: 'root', factory: () => new ChildService('child'), diff --git a/packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app/test/module_spec.ts b/packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app/test/module_spec.ts index 2ff1d1364de26..ea3f99aaca41c 100644 --- a/packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app/test/module_spec.ts +++ b/packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app/test/module_spec.ts @@ -6,17 +6,23 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injectable, InjectionToken, Injector, NgModule, forwardRef, ɵcreateInjector as createInjector} from '@angular/core'; +import {forwardRef, Injectable, InjectionToken, Injector, NgModule, ɵcreateInjector as createInjector} from '@angular/core'; import {AOT_TOKEN, AotModule, AotService} from 'app_built/src/module'; describe('Ivy NgModule', () => { describe('AOT', () => { let injector: Injector; - beforeEach(() => { injector = createInjector(AotModule); }); - it('works', () => { expect(injector.get(AotService) instanceof AotService).toBeTruthy(); }); + beforeEach(() => { + injector = createInjector(AotModule); + }); + it('works', () => { + expect(injector.get(AotService) instanceof AotService).toBeTruthy(); + }); - it('merges imports and exports', () => { expect(injector.get(AOT_TOKEN)).toEqual('exports'); }); + it('merges imports and exports', () => { + expect(injector.get(AOT_TOKEN)).toEqual('exports'); + }); }); @@ -38,7 +44,9 @@ describe('Ivy NgModule', () => { class JitAppModule { } - it('works', () => { createInjector(JitAppModule); }); + it('works', () => { + createInjector(JitAppModule); + }); it('throws an error on circular module dependencies', () => { @NgModule({ diff --git a/packages/compiler-cli/integrationtest/ngtools_src/feature/lazy-feature.module.ts b/packages/compiler-cli/integrationtest/ngtools_src/feature/lazy-feature.module.ts index 64e9bc4488092..b57ecc46e54a1 100644 --- a/packages/compiler-cli/integrationtest/ngtools_src/feature/lazy-feature.module.ts +++ b/packages/compiler-cli/integrationtest/ngtools_src/feature/lazy-feature.module.ts @@ -16,10 +16,8 @@ export class LazyFeatureComponent { @NgModule({ imports: [RouterModule.forChild([ {path: '', component: LazyFeatureComponent, pathMatch: 'full'}, - {path: 'feature', loadChildren: './feature.module#FeatureModule'}, { - path: 'nested-feature', - loadChildren: './lazy-feature-nested.module#LazyFeatureNestedModule' - } + {path: 'feature', loadChildren: './feature.module#FeatureModule'}, + {path: 'nested-feature', loadChildren: './lazy-feature-nested.module#LazyFeatureNestedModule'} ])], declarations: [LazyFeatureComponent] }) diff --git a/packages/compiler-cli/integrationtest/src/animate.ts b/packages/compiler-cli/integrationtest/src/animate.ts index 0c746888bda40..3b187e9130bcd 100644 --- a/packages/compiler-cli/integrationtest/src/animate.ts +++ b/packages/compiler-cli/integrationtest/src/animate.ts @@ -5,7 +5,7 @@ * 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 {AUTO_STYLE, animate, state, style, transition, trigger} from '@angular/animations'; +import {animate, AUTO_STYLE, state, style, transition, trigger} from '@angular/animations'; import {Component} from '@angular/core'; @Component({ @@ -30,8 +30,16 @@ import {Component} from '@angular/core'; }) export class AnimateCmp { stateExpression: string; - constructor() { this.setAsClosed(); } - setAsSomethingElse() { this.stateExpression = 'something'; } - setAsOpen() { this.stateExpression = 'open'; } - setAsClosed() { this.stateExpression = 'closed'; } + constructor() { + this.setAsClosed(); + } + setAsSomethingElse() { + this.stateExpression = 'something'; + } + setAsOpen() { + this.stateExpression = 'open'; + } + setAsClosed() { + this.stateExpression = 'closed'; + } } diff --git a/packages/compiler-cli/integrationtest/src/custom_token.ts b/packages/compiler-cli/integrationtest/src/custom_token.ts index 698b4dd5fe2e2..4db05e42320c4 100644 --- a/packages/compiler-cli/integrationtest/src/custom_token.ts +++ b/packages/compiler-cli/integrationtest/src/custom_token.ts @@ -8,6 +8,8 @@ import {InjectionToken} from '@angular/core'; -export interface Named { name: string; } +export interface Named { + name: string; +} export const CUSTOM = new InjectionToken<Named>('CUSTOM'); diff --git a/packages/compiler-cli/integrationtest/src/errors.ts b/packages/compiler-cli/integrationtest/src/errors.ts index fc25afe56e2ab..0165521d8cdd0 100644 --- a/packages/compiler-cli/integrationtest/src/errors.ts +++ b/packages/compiler-cli/integrationtest/src/errors.ts @@ -10,5 +10,7 @@ import {Component} from '@angular/core'; @Component({selector: 'comp-with-error', templateUrl: 'errors.html'}) export class BindingErrorComp { - createError() { throw new Error('Test'); } + createError() { + throw new Error('Test'); + } } diff --git a/packages/compiler-cli/integrationtest/src/features.ts b/packages/compiler-cli/integrationtest/src/features.ts index 756cbd0b79288..47cde6908b84a 100644 --- a/packages/compiler-cli/integrationtest/src/features.ts +++ b/packages/compiler-cli/integrationtest/src/features.ts @@ -7,7 +7,7 @@ */ import * as common from '@angular/common'; -import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, EventEmitter, Inject, InjectionToken, NgModule, Output, forwardRef} from '@angular/core'; +import {Component, CUSTOM_ELEMENTS_SCHEMA, Directive, EventEmitter, forwardRef, Inject, InjectionToken, NgModule, Output} from '@angular/core'; import {Observable} from 'rxjs'; import {wrapInArray} from './funcs'; @@ -62,7 +62,9 @@ export class CompUsingCustomElements { }) export class CompConsumingEvents { handleDomEventVoid(e: any): void {} - handleDomEventPreventDefault(e: any): boolean { return false; } + handleDomEventPreventDefault(e: any): boolean { + return false; + } handleDirEvent(e: any): void {} } @@ -70,8 +72,7 @@ export class CompConsumingEvents { selector: '[dirEvent]', }) export class DirPublishingEvents { - @Output('dirEvent') - dirEvent: Observable<string> = new EventEmitter(); + @Output('dirEvent') dirEvent: Observable<string> = new EventEmitter(); } @NgModule({schemas: [CUSTOM_ELEMENTS_SCHEMA], declarations: wrapInArray(CompUsingCustomElements)}) diff --git a/packages/compiler-cli/integrationtest/src/jit_summaries.ts b/packages/compiler-cli/integrationtest/src/jit_summaries.ts index 8d2bccf698645..9221608aa12a0 100644 --- a/packages/compiler-cli/integrationtest/src/jit_summaries.ts +++ b/packages/compiler-cli/integrationtest/src/jit_summaries.ts @@ -11,7 +11,7 @@ import {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core'; const instances = new Map<any, Base>(); export function expectInstanceCreated(type: any) { - const instance = instances.get(type) !; + const instance = instances.get(type)!; expect(instance).toBeDefined(); expect(instance.dep instanceof SomeDep).toBe(true); } @@ -19,7 +19,9 @@ export function expectInstanceCreated(type: any) { export class SomeDep {} export class Base { - constructor(public dep: SomeDep) { instances.set(Object.getPrototypeOf(this).constructor, this); } + constructor(public dep: SomeDep) { + instances.set(Object.getPrototypeOf(this).constructor, this); + } } @Component({templateUrl: './jit_summaries.html'}) @@ -36,7 +38,9 @@ export class SomeDirective extends Base { @Pipe({name: 'somePipe'}) export class SomePipe extends Base { - transform(value: any) { return value; } + transform(value: any) { + return value; + } } @Injectable() diff --git a/packages/compiler-cli/integrationtest/src/module.ts b/packages/compiler-cli/integrationtest/src/module.ts index a3d8711a9387f..ead5e36920b1f 100644 --- a/packages/compiler-cli/integrationtest/src/module.ts +++ b/packages/compiler-cli/integrationtest/src/module.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ApplicationRef, NgModule, forwardRef} from '@angular/core'; +import {ApplicationRef, forwardRef, NgModule} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {ServerModule} from '@angular/platform-server'; import {FlatModule} from 'flat_module'; diff --git a/packages/compiler-cli/integrationtest/src/module_fixtures.ts b/packages/compiler-cli/integrationtest/src/module_fixtures.ts index 18fe662ac1d0f..0fd71d8c69110 100644 --- a/packages/compiler-cli/integrationtest/src/module_fixtures.ts +++ b/packages/compiler-cli/integrationtest/src/module_fixtures.ts @@ -19,24 +19,26 @@ export class ServiceUsingLibModule { @Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) export class SomeDirectiveInRootModule { - @Input() - someDir: string; + @Input() someDir: string; } @Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) export class SomeDirectiveInLibModule { - @Input() - someDir: string; + @Input() someDir: string; } @Pipe({name: 'somePipe'}) export class SomePipeInRootModule { - transform(value: string): any { return `transformed ${value}`; } + transform(value: string): any { + return `transformed ${value}`; + } } @Pipe({name: 'somePipe'}) export class SomePipeInLibModule { - transform(value: string): any { return `transformed ${value}`; } + transform(value: string): any { + return `transformed ${value}`; + } } @Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`}) @@ -66,8 +68,8 @@ export class SomeLibModule { return { ngModule: SomeLibModule, providers: [ - ServiceUsingLibModule, provideValueWithEntryComponents( - [{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}]) + ServiceUsingLibModule, + provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}]) ] }; } diff --git a/packages/compiler-cli/integrationtest/test.js b/packages/compiler-cli/integrationtest/test.js index c30bb482c6179..39e23aa4b6a1d 100644 --- a/packages/compiler-cli/integrationtest/test.js +++ b/packages/compiler-cli/integrationtest/test.js @@ -34,8 +34,8 @@ function nodejs_repository() { const nodejsBinaryExt = os.platform() === 'win32' ? '.bat' : '.sh'; const ngcBin = require.resolve(`./ngc_bin${nodejsBinaryExt}`); const xi18nBin = require.resolve(`./ng_xi18n${nodejsBinaryExt}`); -const nodeBin = require.resolve( - `${nodejs_repository()}/${(os.platform() === 'win32' ? 'bin/nodejs/node.exe' : 'bin/nodejs/bin/node')}`); +const nodeBin = require.resolve(`${nodejs_repository()}/${ + (os.platform() === 'win32' ? 'bin/nodejs/node.exe' : 'bin/nodejs/bin/node')}`); const jasmineBin = require.resolve('npm/node_modules/jasmine/bin/jasmine.js'); // Prepare the test directory before building the integration test output. This ensures that diff --git a/packages/compiler-cli/integrationtest/test/jit_summaries_spec.ts b/packages/compiler-cli/integrationtest/test/jit_summaries_spec.ts index e68db802e2717..66ee77a3ca966 100644 --- a/packages/compiler-cli/integrationtest/test/jit_summaries_spec.ts +++ b/packages/compiler-cli/integrationtest/test/jit_summaries_spec.ts @@ -8,9 +8,9 @@ import {Component} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {ServerTestingModule, platformServerTesting} from '@angular/platform-server/testing'; +import {platformServerTesting, ServerTestingModule} from '@angular/platform-server/testing'; -import {SomeDep, SomeDirective, SomeModule, SomePipe, SomePrivateComponent, SomeService, expectInstanceCreated} from '../src/jit_summaries'; +import {expectInstanceCreated, SomeDep, SomeDirective, SomeModule, SomePipe, SomePrivateComponent, SomeService} from '../src/jit_summaries'; import {SomeModuleNgSummary} from '../src/jit_summaries.ngsummary'; describe('Jit Summaries', () => { @@ -18,7 +18,9 @@ describe('Jit Summaries', () => { TestBed.initTestEnvironment(ServerTestingModule, platformServerTesting(), SomeModuleNgSummary); }); - afterEach(() => { TestBed.resetTestEnvironment(); }); + afterEach(() => { + TestBed.resetTestEnvironment(); + }); it('should use directive metadata from summaries', () => { @Component({template: '<div someDir></div>'}) diff --git a/packages/compiler-cli/integrationtest/test/ng_module_spec.ts b/packages/compiler-cli/integrationtest/test/ng_module_spec.ts index 4e58a8f4ea998..151c64211c398 100644 --- a/packages/compiler-cli/integrationtest/test/ng_module_spec.ts +++ b/packages/compiler-cli/integrationtest/test/ng_module_spec.ts @@ -10,7 +10,7 @@ import './init'; import {ComponentUsingThirdParty} from '../src/comp_using_3rdp'; import {ComponentUsingFlatModule} from '../src/comp_using_flat_module'; import {MainModule} from '../src/module'; -import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures'; +import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, ServiceUsingLibModule, SOME_TOKEN, SomeLibModule, SomeService} from '../src/module_fixtures'; import {createComponent, createModule} from './util'; diff --git a/packages/compiler-cli/integrationtest/test/query_spec.ts b/packages/compiler-cli/integrationtest/test/query_spec.ts index 1d66f58c89a79..a97c91f142f14 100644 --- a/packages/compiler-cli/integrationtest/test/query_spec.ts +++ b/packages/compiler-cli/integrationtest/test/query_spec.ts @@ -19,7 +19,6 @@ describe('child queries', () => { debugElement.query(By.directive(CompWithChildQuery)); expect(childQueryCompFixture.componentInstance.child).toBeDefined(); expect(childQueryCompFixture.componentInstance.child instanceof CompForChildQuery).toBe(true); - }); it('should support compiling children queries', () => { diff --git a/packages/compiler-cli/integrationtest/test/source_map_spec.ts b/packages/compiler-cli/integrationtest/test/source_map_spec.ts index 25b4dd7b1b384..2b4ad439797a1 100644 --- a/packages/compiler-cli/integrationtest/test/source_map_spec.ts +++ b/packages/compiler-cli/integrationtest/test/source_map_spec.ts @@ -31,7 +31,7 @@ function getSourcePositionForStack(stack: string): {source: string, line: number const htmlLocations = stack .split('\n') // e.g. at View_MyComp_0 (...html:153:40) - .map(line => /\((.*\.html):(\d+):(\d+)/.exec(line) !) + .map(line => /\((.*\.html):(\d+):(\d+)/.exec(line)!) .filter(match => !!match) .map(match => ({ source: match[1], diff --git a/packages/compiler-cli/integrationtest/test/test_ngtools_api.ts b/packages/compiler-cli/integrationtest/test/test_ngtools_api.ts index f98bb420517d1..bff1e6cbc7369 100644 --- a/packages/compiler-cli/integrationtest/test/test_ngtools_api.ts +++ b/packages/compiler-cli/integrationtest/test/test_ngtools_api.ts @@ -22,10 +22,15 @@ import {createProgram, readConfiguration} from '@angular/compiler-cli'; * properly read and wrote. */ function main() { - Promise.resolve().then(() => lazyRoutesTest()).then(() => { process.exit(0); }).catch((err) => { - console.error(err.stack); - process.exit(1); - }); + Promise.resolve() + .then(() => lazyRoutesTest()) + .then(() => { + process.exit(0); + }) + .catch((err) => { + console.error(err.stack); + process.exit(1); + }); } function lazyRoutesTest() { @@ -36,7 +41,8 @@ function lazyRoutesTest() { const host = ts.createCompilerHost(config.options, true); const program = createProgram({ rootNames: config.rootNames, - options: config.options, host, + options: config.options, + host, }); config.options.basePath = basePath; diff --git a/packages/compiler-cli/integrationtest/test/util.ts b/packages/compiler-cli/integrationtest/test/util.ts index 0fe1ae64d17de..05f6943f86882 100644 --- a/packages/compiler-cli/integrationtest/test/util.ts +++ b/packages/compiler-cli/integrationtest/test/util.ts @@ -13,7 +13,7 @@ import {platformServerTesting} from '@angular/platform-server/testing'; import {MainModule} from '../src/module'; import {MainModuleNgFactory} from '../src/module.ngfactory'; -let mainModuleRef: NgModuleRef<MainModule> = null !; +let mainModuleRef: NgModuleRef<MainModule> = null!; beforeEach((done) => { platformServerTesting().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => { mainModuleRef = moduleRef; diff --git a/packages/compiler-cli/ngcc/BUILD.bazel b/packages/compiler-cli/ngcc/BUILD.bazel index db91dca21a052..3d8553c095191 100644 --- a/packages/compiler-cli/ngcc/BUILD.bazel +++ b/packages/compiler-cli/ngcc/BUILD.bazel @@ -12,6 +12,7 @@ ts_library( deps = [ "//packages:types", "//packages/compiler", + "//packages/compiler-cli", "//packages/compiler-cli/src/ngtsc/annotations", "//packages/compiler-cli/src/ngtsc/cycles", "//packages/compiler-cli/src/ngtsc/diagnostics", diff --git a/packages/compiler-cli/ngcc/index.ts b/packages/compiler-cli/ngcc/index.ts index 0c1a5810b0bbc..c508021afc7a5 100644 --- a/packages/compiler-cli/ngcc/index.ts +++ b/packages/compiler-cli/ngcc/index.ts @@ -5,18 +5,18 @@ * 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 {CachedFileSystem, NodeJSFileSystem, setFileSystem} from '../src/ngtsc/file_system'; +import {NodeJSFileSystem, setFileSystem} from '../src/ngtsc/file_system'; + +import {mainNgcc} from './src/main'; +import {AsyncNgccOptions, NgccOptions, SyncNgccOptions} from './src/ngcc_options'; -import {AsyncNgccOptions, NgccOptions, SyncNgccOptions, mainNgcc} from './src/main'; export {ConsoleLogger} from './src/logging/console_logger'; -export {LogLevel, Logger} from './src/logging/logger'; -export {AsyncNgccOptions, NgccOptions, SyncNgccOptions} from './src/main'; -export {PathMappings} from './src/utils'; +export {Logger, LogLevel} from './src/logging/logger'; +export {AsyncNgccOptions, NgccOptions, PathMappings, SyncNgccOptions} from './src/ngcc_options'; export function process(options: AsyncNgccOptions): Promise<void>; export function process(options: SyncNgccOptions): void; export function process(options: NgccOptions): void|Promise<void> { - // Recreate the file system on each call to reset the cache - setFileSystem(new CachedFileSystem(new NodeJSFileSystem())); + setFileSystem(new NodeJSFileSystem()); return mainNgcc(options); } diff --git a/packages/compiler-cli/ngcc/main-ngcc.ts b/packages/compiler-cli/ngcc/main-ngcc.ts index 394d9cb52da93..2afc9481ab6a3 100644 --- a/packages/compiler-cli/ngcc/main-ngcc.ts +++ b/packages/compiler-cli/ngcc/main-ngcc.ts @@ -6,138 +6,25 @@ * 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 * as yargs from 'yargs'; - -import {resolve, setFileSystem, CachedFileSystem, NodeJSFileSystem} from '../src/ngtsc/file_system'; import {mainNgcc} from './src/main'; -import {ConsoleLogger} from './src/logging/console_logger'; -import {LogLevel} from './src/logging/logger'; +import {parseCommandLineOptions} from './src/command_line_options'; // CLI entry point if (require.main === module) { + process.title = 'ngcc'; const startTime = Date.now(); - - const args = process.argv.slice(2); - const options = - yargs - .option('s', { - alias: 'source', - describe: - 'A path (relative to the working directory) of the `node_modules` folder to process.', - default: './node_modules' - }) - .option('f', {alias: 'formats', hidden: true, array: true}) - .option('p', { - alias: 'properties', - array: true, - describe: - 'An array of names of properties in package.json to compile (e.g. `module` or `es2015`)\n' + - 'Each of these properties should hold the path to a bundle-format.\n' + - 'If provided, only the specified properties are considered for processing.\n' + - 'If not provided, all the supported format properties (e.g. fesm2015, fesm5, es2015, esm2015, esm5, main, module) in the package.json are considered.' - }) - .option('t', { - alias: 'target', - describe: - 'A relative path (from the `source` path) to a single entry-point to process (plus its dependencies).\n' + - 'If this property is provided then `error-on-failed-entry-point` is forced to true', - }) - .option('first-only', { - describe: - 'If specified then only the first matching package.json property will be compiled.', - type: 'boolean' - }) - .option('create-ivy-entry-points', { - describe: - 'If specified then new `*_ivy_ngcc` entry-points will be added to package.json rather than modifying the ones in-place.\n' + - 'For this to work you need to have custom resolution set up (e.g. in webpack) to look for these new entry-points.\n' + - 'The Angular CLI does this already, so it is safe to use this option if the project is being built via the CLI.', - type: 'boolean', - }) - .option('legacy-message-ids', { - describe: 'Render `$localize` messages with legacy format ids.\n' + - 'The default value is `true`. Only set this to `false` if you do not want legacy message ids to\n' + - 'be rendered. For example, if you are not using legacy message ids in your translation files\n' + - 'AND are not doing compile-time inlining of translations, in which case the extra message ids\n' + - 'would add unwanted size to the final source bundle.\n' + - 'It is safe to leave this set to true if you are doing compile-time inlining because the extra\n' + - 'legacy message ids will all be stripped during translation.', - type: 'boolean', - default: true, - }) - .option('async', { - describe: - 'Whether to compile asynchronously. This is enabled by default as it allows compilations to be parallelized.\n' + - 'Disabling asynchronous compilation may be useful for debugging.', - type: 'boolean', - default: true, - }) - .option('l', { - alias: 'loglevel', - describe: 'The lowest severity logging message that should be output.', - choices: ['debug', 'info', 'warn', 'error'], - }) - .option('invalidate-entry-point-manifest', { - describe: - 'If this is set then ngcc will not read an entry-point manifest file from disk.\n' + - 'Instead it will walk the directory tree as normal looking for entry-points, and then write a new manifest file.', - type: 'boolean', - default: false, - }) - .option('error-on-failed-entry-point', { - describe: - 'Set this option in order to terminate immediately with an error code if an entry-point fails to be processed.\n' + - 'If `-t`/`--target` is provided then this property is always true and cannot be changed. Otherwise the default is false.\n' + - 'When set to false, ngcc will continue to process entry-points after a failure. In which case it will log an error and resume processing other entry-points.', - type: 'boolean', - default: false, - }) - .strict() - .help() - .parse(args); - - if (options['f'] && options['f'].length) { - console.error( - 'The formats option (-f/--formats) has been removed. Consider the properties option (-p/--properties) instead.'); - process.exit(1); - } - - setFileSystem(new CachedFileSystem(new NodeJSFileSystem())); - - const baseSourcePath = resolve(options['s'] || './node_modules'); - const propertiesToConsider: string[] = options['p']; - const targetEntryPointPath = options['t'] ? options['t'] : undefined; - const compileAllFormats = !options['first-only']; - const createNewEntryPointFormats = options['create-ivy-entry-points']; - const logLevel = options['l'] as keyof typeof LogLevel | undefined; - const enableI18nLegacyMessageIdFormat = options['legacy-message-ids']; - const invalidateEntryPointManifest = options['invalidate-entry-point-manifest']; - const errorOnFailedEntryPoint = options['error-on-failed-entry-point']; - - (async() => { + const options = parseCommandLineOptions(process.argv.slice(2)); + (async () => { try { - const logger = logLevel && new ConsoleLogger(LogLevel[logLevel]); - - await mainNgcc({ - basePath: baseSourcePath, - propertiesToConsider, - targetEntryPointPath, - compileAllFormats, - createNewEntryPointFormats, - logger, - enableI18nLegacyMessageIdFormat, - async: options['async'], invalidateEntryPointManifest, errorOnFailedEntryPoint, - }); - - if (logger) { + await mainNgcc(options); + if (options.logger) { const duration = Math.round((Date.now() - startTime) / 1000); - logger.debug(`Run ngcc in ${duration}s.`); + options.logger.debug(`Run ngcc in ${duration}s.`); } - process.exitCode = 0; } catch (e) { console.error(e.stack || e.message); - process.exitCode = 1; + process.exit(1); } })(); } diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index a4c5f105f6678..2b66c5662dd1a 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -8,10 +8,11 @@ import {ConstantPool} from '@angular/compiler'; import * as ts from 'typescript'; +import {ParsedConfiguration} from '../../..'; import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations'; import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles'; import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics'; -import {FileSystem, LogicalFileSystem, absoluteFrom, dirname, resolve} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, dirname, FileSystem, LogicalFileSystem, resolve} from '../../../src/ngtsc/file_system'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports'; import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../../src/ngtsc/metadata'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; @@ -27,7 +28,7 @@ import {EntryPointBundle} from '../packages/entry_point_bundle'; import {DefaultMigrationHost} from './migration_host'; import {NgccTraitCompiler} from './ngcc_trait_compiler'; import {CompiledClass, CompiledFile, DecorationAnalyses} from './types'; -import {NOOP_DEPENDENCY_TRACKER, isWithinPackage} from './util'; +import {isWithinPackage, NOOP_DEPENDENCY_TRACKER} from './util'; @@ -37,8 +38,12 @@ import {NOOP_DEPENDENCY_TRACKER, isWithinPackage} from './util'; class NgccResourceLoader implements ResourceLoader { constructor(private fs: FileSystem) {} canPreload = false; - preload(): undefined|Promise<void> { throw new Error('Not implemented.'); } - load(url: string): string { return this.fs.readFile(resolve(url)); } + preload(): undefined|Promise<void> { + throw new Error('Not implemented.'); + } + load(url: string): string { + return this.fs.readFile(resolve(url)); + } resolve(url: string, containingFile: string): string { return resolve(dirname(absoluteFrom(containingFile)), url); } @@ -55,6 +60,7 @@ export class DecorationAnalyzer { private rootDirs = this.bundle.rootDirs; private packagePath = this.bundle.entryPoint.package; private isCore = this.bundle.isCore; + private compilerOptions = this.tsConfig !== null ? this.tsConfig.options : {}; moduleResolver = new ModuleResolver(this.program, this.options, this.host, /* moduleResolutionCache */ null); @@ -71,8 +77,9 @@ export class DecorationAnalyzer { // based on whether a bestGuessOwningModule is present in the Reference. new LogicalProjectStrategy(this.reflectionHost, new LogicalFileSystem(this.rootDirs)), ]); - aliasingHost = this.bundle.entryPoint.generateDeepReexports? - new PrivateExportAliasingHost(this.reflectionHost): null; + aliasingHost = this.bundle.entryPoint.generateDeepReexports ? + new PrivateExportAliasingHost(this.reflectionHost) : + null; dtsModuleScopeResolver = new MetadataDtsModuleScopeResolver(this.dtsMetaReader, this.aliasingHost); scopeRegistry = new LocalModuleScopeRegistry( @@ -87,7 +94,7 @@ export class DecorationAnalyzer { new ComponentDecoratorHandler( this.reflectionHost, this.evaluator, this.fullRegistry, this.fullMetaReader, this.scopeRegistry, this.scopeRegistry, this.isCore, this.resourceManager, this.rootDirs, - /* defaultPreserveWhitespaces */ false, + !!this.compilerOptions.preserveWhitespaces, /* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat, this.moduleResolver, this.cycleAnalyzer, this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, NOOP_DEPENDENCY_TRACKER, this.injectableRegistry, /* annotateForClosureCompiler */ false), @@ -123,7 +130,8 @@ export class DecorationAnalyzer { constructor( private fs: FileSystem, private bundle: EntryPointBundle, private reflectionHost: NgccReflectionHost, private referencesRegistry: ReferencesRegistry, - private diagnosticHandler: (error: ts.Diagnostic) => void = () => {}) {} + private diagnosticHandler: (error: ts.Diagnostic) => void = () => {}, + private tsConfig: ParsedConfiguration|null = null) {} /** * Analyze a program to find all the decorated files should be transformed. @@ -188,7 +196,9 @@ export class DecorationAnalyzer { }); } - protected reportDiagnostics() { this.compiler.diagnostics.forEach(this.diagnosticHandler); } + protected reportDiagnostics() { + this.compiler.diagnostics.forEach(this.diagnosticHandler); + } protected compileFile(sourceFile: ts.SourceFile): CompiledFile { const constantPool = new ConstantPool(); @@ -208,7 +218,8 @@ export class DecorationAnalyzer { compiledClasses.push({ name: record.node.name.text, decorators: this.compiler.getAllDecorators(record.node), - declaration: record.node, compilation + declaration: record.node, + compilation }); } @@ -221,7 +232,7 @@ export class DecorationAnalyzer { if (!exportStatements.has(sf.fileName)) { return []; } - const exports = exportStatements.get(sf.fileName) !; + const exports = exportStatements.get(sf.fileName)!; const reexports: Reexport[] = []; exports.forEach(([fromModule, symbolName], asAlias) => { diff --git a/packages/compiler-cli/ngcc/src/analysis/module_with_providers_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/module_with_providers_analyzer.ts index 8c6ad1ee8343b..eb7a0b4a8cdf8 100644 --- a/packages/compiler-cli/ngcc/src/analysis/module_with_providers_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/module_with_providers_analyzer.ts @@ -75,10 +75,9 @@ export class ModuleWithProvidersAnalyzer { const dtsClass = this.host.getDtsDeclaration(containerClass.declaration.valueDeclaration); // Get the declaration of the matching static method dtsFn = dtsClass && ts.isClassDeclaration(dtsClass) ? - dtsClass.members - .find( - member => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) && - member.name.text === fn.name) as ts.Declaration : + dtsClass.members.find( + member => ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) && + member.name.text === fn.name) as ts.Declaration : null; } else { dtsFn = this.host.getDtsDeclaration(fn.declaration); @@ -87,8 +86,8 @@ export class ModuleWithProvidersAnalyzer { throw new Error(`Matching type declaration for ${fn.declaration.getText()} is missing`); } if (!isFunctionOrMethod(dtsFn)) { - throw new Error( - `Matching type declaration for ${fn.declaration.getText()} is not a function: ${dtsFn.getText()}`); + throw new Error(`Matching type declaration for ${ + fn.declaration.getText()} is not a function: ${dtsFn.getText()}`); } return dtsFn; } @@ -106,12 +105,14 @@ export class ModuleWithProvidersAnalyzer { // to its type declaration. const dtsNgModule = this.host.getDtsDeclaration(ngModule.node); if (!dtsNgModule) { - throw new Error( - `No typings declaration can be found for the referenced NgModule class in ${fn.declaration.getText()}.`); + throw new Error(`No typings declaration can be found for the referenced NgModule class in ${ + fn.declaration.getText()}.`); } if (!ts.isClassDeclaration(dtsNgModule) || !hasNameIdentifier(dtsNgModule)) { - throw new Error( - `The referenced NgModule in ${fn.declaration.getText()} is not a named class declaration in the typings program; instead we get ${dtsNgModule.getText()}`); + throw new Error(`The referenced NgModule in ${ + fn.declaration + .getText()} is not a named class declaration in the typings program; instead we get ${ + dtsNgModule.getText()}`); } return {node: dtsNgModule, known: null, viaModule: null}; diff --git a/packages/compiler-cli/ngcc/src/analysis/ngcc_references_registry.ts b/packages/compiler-cli/ngcc/src/analysis/ngcc_references_registry.ts index ba8e0dbb2d6b1..f557cd9d6cf76 100644 --- a/packages/compiler-cli/ngcc/src/analysis/ngcc_references_registry.ts +++ b/packages/compiler-cli/ngcc/src/analysis/ngcc_references_registry.ts @@ -45,5 +45,7 @@ export class NgccReferencesRegistry implements ReferencesRegistry { * Create and return a mapping for the registered resolved references. * @returns A map of reference identifiers to reference declarations. */ - getDeclarationMap(): Map<ts.Identifier, ConcreteDeclaration> { return this.map; } + getDeclarationMap(): Map<ts.Identifier, ConcreteDeclaration> { + return this.map; + } } diff --git a/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts b/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts index f4d7f80fba59d..1c526bc78a02e 100644 --- a/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts +++ b/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts @@ -29,7 +29,9 @@ export class NgccTraitCompiler extends TraitCompiler { /* compileNonExportedClasses */ true, new DtsTransformRegistry()); } - get analyzedFiles(): ts.SourceFile[] { return Array.from(this.fileToClasses.keys()); } + get analyzedFiles(): ts.SourceFile[] { + return Array.from(this.fileToClasses.keys()); + } /** * Analyzes the source file in search for classes to process. For any class that is found in the @@ -81,5 +83,7 @@ export class NgccTraitCompiler extends TraitCompiler { } class NoIncrementalBuild implements IncrementalBuild<any> { - priorWorkFor(sf: ts.SourceFile): any[]|null { return null; } + priorWorkFor(sf: ts.SourceFile): any[]|null { + return null; + } } diff --git a/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts index d005345201245..afc99f25613db 100644 --- a/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/private_declarations_analyzer.ts @@ -7,10 +7,11 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFromSourceFile} from '../../../src/ngtsc/file_system'; +import {absoluteFromSourceFile, AbsoluteFsPath} from '../../../src/ngtsc/file_system'; import {ConcreteDeclaration} from '../../../src/ngtsc/reflection'; import {NgccReflectionHost} from '../host/ngcc_host'; import {hasNameIdentifier, isDefined} from '../utils'; + import {NgccReferencesRegistry} from './ngcc_references_registry'; export interface ExportInfo { @@ -48,7 +49,7 @@ export class PrivateDeclarationsAnalyzer { exports.forEach((declaration, exportedName) => { if (declaration.node !== null && hasNameIdentifier(declaration.node)) { if (privateDeclarations.has(declaration.node.name)) { - const privateDeclaration = privateDeclarations.get(declaration.node.name) !; + const privateDeclaration = privateDeclarations.get(declaration.node.name)!; if (privateDeclaration.node !== declaration.node) { throw new Error(`${declaration.node.name.text} is declared multiple times.`); } @@ -62,7 +63,7 @@ export class PrivateDeclarationsAnalyzer { return Array.from(privateDeclarations.keys()).map(id => { const from = absoluteFromSourceFile(id.getSourceFile()); - const declaration = privateDeclarations.get(id) !; + const declaration = privateDeclarations.get(id)!; const dtsDeclaration = this.host.getDtsDeclaration(declaration.node); const dtsFrom = dtsDeclaration && absoluteFromSourceFile(dtsDeclaration.getSourceFile()); diff --git a/packages/compiler-cli/ngcc/src/analysis/util.ts b/packages/compiler-cli/ngcc/src/analysis/util.ts index a953d4608cacd..fb51a2c7ebfa6 100644 --- a/packages/compiler-cli/ngcc/src/analysis/util.ts +++ b/packages/compiler-cli/ngcc/src/analysis/util.ts @@ -7,11 +7,12 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFromSourceFile, relative} from '../../../src/ngtsc/file_system'; +import {absoluteFromSourceFile, AbsoluteFsPath, relative} from '../../../src/ngtsc/file_system'; import {DependencyTracker} from '../../../src/ngtsc/incremental/api'; export function isWithinPackage(packagePath: AbsoluteFsPath, sourceFile: ts.SourceFile): boolean { - return !relative(packagePath, absoluteFromSourceFile(sourceFile)).startsWith('..'); + const relativePath = relative(packagePath, absoluteFromSourceFile(sourceFile)); + return !relativePath.startsWith('..') && !relativePath.startsWith('node_modules/'); } class NoopDependencyTracker implements DependencyTracker { diff --git a/packages/compiler-cli/ngcc/src/command_line_options.ts b/packages/compiler-cli/ngcc/src/command_line_options.ts new file mode 100644 index 0000000000000..65ca7fac61bc0 --- /dev/null +++ b/packages/compiler-cli/ngcc/src/command_line_options.ts @@ -0,0 +1,139 @@ +#!/usr/bin/env node +/** + * @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 * as yargs from 'yargs'; + +import {resolve, setFileSystem, NodeJSFileSystem} from '../../src/ngtsc/file_system'; +import {ConsoleLogger} from './logging/console_logger'; +import {LogLevel} from './logging/logger'; +import {NgccOptions} from './ngcc_options'; + +export function parseCommandLineOptions(args: string[]): NgccOptions { + const options = + yargs + .option('s', { + alias: 'source', + describe: + 'A path (relative to the working directory) of the `node_modules` folder to process.', + default: './node_modules' + }) + .option('f', {alias: 'formats', hidden: true, array: true}) + .option('p', { + alias: 'properties', + array: true, + describe: + 'An array of names of properties in package.json to compile (e.g. `module` or `es2015`)\n' + + 'Each of these properties should hold the path to a bundle-format.\n' + + 'If provided, only the specified properties are considered for processing.\n' + + 'If not provided, all the supported format properties (e.g. fesm2015, fesm5, es2015, esm2015, esm5, main, module) in the package.json are considered.' + }) + .option('t', { + alias: 'target', + describe: + 'A relative path (from the `source` path) to a single entry-point to process (plus its dependencies).\n' + + 'If this property is provided then `error-on-failed-entry-point` is forced to true', + }) + .option('first-only', { + describe: + 'If specified then only the first matching package.json property will be compiled.', + type: 'boolean' + }) + .option('create-ivy-entry-points', { + describe: + 'If specified then new `*_ivy_ngcc` entry-points will be added to package.json rather than modifying the ones in-place.\n' + + 'For this to work you need to have custom resolution set up (e.g. in webpack) to look for these new entry-points.\n' + + 'The Angular CLI does this already, so it is safe to use this option if the project is being built via the CLI.', + type: 'boolean', + }) + .option('legacy-message-ids', { + describe: 'Render `$localize` messages with legacy format ids.\n' + + 'The default value is `true`. Only set this to `false` if you do not want legacy message ids to\n' + + 'be rendered. For example, if you are not using legacy message ids in your translation files\n' + + 'AND are not doing compile-time inlining of translations, in which case the extra message ids\n' + + 'would add unwanted size to the final source bundle.\n' + + 'It is safe to leave this set to true if you are doing compile-time inlining because the extra\n' + + 'legacy message ids will all be stripped during translation.', + type: 'boolean', + default: true, + }) + .option('async', { + describe: + 'Whether to compile asynchronously. This is enabled by default as it allows compilations to be parallelized.\n' + + 'Disabling asynchronous compilation may be useful for debugging.', + type: 'boolean', + default: true, + }) + .option('l', { + alias: 'loglevel', + describe: 'The lowest severity logging message that should be output.', + choices: ['debug', 'info', 'warn', 'error'], + }) + .option('invalidate-entry-point-manifest', { + describe: + 'If this is set then ngcc will not read an entry-point manifest file from disk.\n' + + 'Instead it will walk the directory tree as normal looking for entry-points, and then write a new manifest file.', + type: 'boolean', + default: false, + }) + .option('error-on-failed-entry-point', { + describe: + 'Set this option in order to terminate immediately with an error code if an entry-point fails to be processed.\n' + + 'If `-t`/`--target` is provided then this property is always true and cannot be changed. Otherwise the default is false.\n' + + 'When set to false, ngcc will continue to process entry-points after a failure. In which case it will log an error and resume processing other entry-points.', + type: 'boolean', + default: false, + }) + .option('tsconfig', { + describe: + 'A path to a tsconfig.json file that will be used to configure the Angular compiler and module resolution used by ngcc.\n' + + 'If not provided, ngcc will attempt to read a `tsconfig.json` file from the folder above that given by the `-s` option.\n' + + 'Set to false (via `--no-tsconfig`) if you do not want ngcc to use any `tsconfig.json` file.', + type: 'string', + }) + .strict() + .help() + .parse(args); + + if (options['f'] && options['f'].length) { + console.error( + 'The formats option (-f/--formats) has been removed. Consider the properties option (-p/--properties) instead.'); + process.exit(1); + } + + setFileSystem(new NodeJSFileSystem()); + + const baseSourcePath = resolve(options['s'] || './node_modules'); + const propertiesToConsider: string[] = options['p']; + const targetEntryPointPath = options['t'] ? options['t'] : undefined; + const compileAllFormats = !options['first-only']; + const createNewEntryPointFormats = options['create-ivy-entry-points']; + const logLevel = options['l'] as keyof typeof LogLevel | undefined; + const enableI18nLegacyMessageIdFormat = options['legacy-message-ids']; + const invalidateEntryPointManifest = options['invalidate-entry-point-manifest']; + const errorOnFailedEntryPoint = options['error-on-failed-entry-point']; + // yargs is not so great at mixed string+boolean types, so we have to test tsconfig against a + // string "false" to capture the `tsconfig=false` option. + // And we have to convert the option to a string to handle `no-tsconfig`, which will be `false`. + const tsConfigPath = `${options['tsconfig']}` === 'false' ? null : options['tsconfig']; + + const logger = logLevel && new ConsoleLogger(LogLevel[logLevel]); + + return { + basePath: baseSourcePath, + propertiesToConsider, + targetEntryPointPath, + compileAllFormats, + createNewEntryPointFormats, + logger, + enableI18nLegacyMessageIdFormat, + async: options['async'], + invalidateEntryPointManifest, + errorOnFailedEntryPoint, + tsConfigPath + }; +} \ No newline at end of file diff --git a/packages/compiler-cli/ngcc/src/dependencies/commonjs_dependency_host.ts b/packages/compiler-cli/ngcc/src/dependencies/commonjs_dependency_host.ts index 3d4ab17b8e701..ef783179ca4b9 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/commonjs_dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/commonjs_dependency_host.ts @@ -6,8 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; + import {AbsoluteFsPath} from '../../../src/ngtsc/file_system'; -import {RequireCall, isReexportStatement, isRequireCall} from '../host/commonjs_umd_utils'; +import {isReexportStatement, isRequireCall, RequireCall} from '../host/commonjs_umd_utils'; + import {DependencyHostBase} from './dependency_host'; import {ResolvedDeepImport, ResolvedRelativeModule} from './module_resolver'; @@ -120,5 +122,7 @@ export class CommonJsDependencyHost extends DependencyHostBase { * @returns false if there are definitely no require calls * in this file, true otherwise. */ - private hasRequireCalls(source: string): boolean { return /require\(['"]/.test(source); } + private hasRequireCalls(source: string): boolean { + return /require\(['"]/.test(source); + } } diff --git a/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts b/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts index d71beca388393..e086d71772b6c 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {AbsoluteFsPath, FileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +import {EntryPoint} from '../packages/entry_point'; import {resolveFileWithPostfixes} from '../utils'; import {ModuleResolver} from './module_resolver'; @@ -21,6 +22,11 @@ export interface DependencyInfo { deepImports: Set<AbsoluteFsPath>; } +export interface EntryPointWithDependencies { + entryPoint: EntryPoint; + depInfo: DependencyInfo; +} + export function createDependencyInfo(): DependencyInfo { return {dependencies: new Set(), missing: new Set(), deepImports: new Set()}; } diff --git a/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts b/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts index ef9d2c4e550fa..06e544791d098 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts @@ -7,12 +7,14 @@ */ import {DepGraph} from 'dependency-graph'; + import {AbsoluteFsPath, FileSystem, resolve} from '../../../src/ngtsc/file_system'; import {Logger} from '../logging/logger'; import {NgccConfiguration} from '../packages/configuration'; -import {EntryPoint, EntryPointFormat, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from '../packages/entry_point'; +import {EntryPoint, EntryPointFormat, getEntryPointFormat, SUPPORTED_FORMAT_PROPERTIES} from '../packages/entry_point'; import {PartiallyOrderedList} from '../utils'; -import {DependencyHost, DependencyInfo, createDependencyInfo} from './dependency_host'; + +import {createDependencyInfo, DependencyHost, EntryPointWithDependencies} from './dependency_host'; const builtinNodeJsModules = new Set<string>(require('module').builtinModules); @@ -92,7 +94,7 @@ export class DependencyResolver { * @param target If provided, only return entry-points depended on by this entry-point. * @returns the result of sorting the entry points by dependency. */ - sortEntryPointsByDependency(entryPoints: EntryPoint[], target?: EntryPoint): + sortEntryPointsByDependency(entryPoints: EntryPointWithDependencies[], target?: EntryPoint): SortedEntryPointsInfo { const {invalidEntryPoints, ignoredDependencies, graph} = this.computeDependencyGraph(entryPoints); @@ -118,17 +120,21 @@ export class DependencyResolver { }; } - getEntryPointDependencies(entryPoint: EntryPoint): DependencyInfo { - const formatInfo = this.getEntryPointFormatInfo(entryPoint); - const host = this.hosts[formatInfo.format]; - if (!host) { - throw new Error( - `Could not find a suitable format for computing dependencies of entry-point: '${entryPoint.path}'.`); + getEntryPointWithDependencies(entryPoint: EntryPoint): EntryPointWithDependencies { + const dependencies = createDependencyInfo(); + if (entryPoint.compiledByAngular) { + // Only bother to compute dependencies of entry-points that have been compiled by Angular + const formatInfo = this.getEntryPointFormatInfo(entryPoint); + const host = this.hosts[formatInfo.format]; + if (!host) { + throw new Error( + `Could not find a suitable format for computing dependencies of entry-point: '${ + entryPoint.path}'.`); + } + host.collectDependencies(formatInfo.path, dependencies); + this.typingsHost.collectDependencies(entryPoint.typings, dependencies); } - const depInfo = createDependencyInfo(); - host.collectDependencies(formatInfo.path, depInfo); - this.typingsHost.collectDependencies(entryPoint.typings, depInfo); - return depInfo; + return {entryPoint, depInfo: dependencies}; } /** @@ -137,20 +143,18 @@ export class DependencyResolver { * The graph only holds entry-points that ngcc cares about and whose dependencies * (direct and transitive) all exist. */ - private computeDependencyGraph(entryPoints: EntryPoint[]): DependencyGraph { + private computeDependencyGraph(entryPoints: EntryPointWithDependencies[]): DependencyGraph { const invalidEntryPoints: InvalidEntryPoint[] = []; const ignoredDependencies: IgnoredDependency[] = []; const graph = new DepGraph<EntryPoint>(); - const angularEntryPoints = entryPoints.filter(entryPoint => entryPoint.compiledByAngular); + const angularEntryPoints = entryPoints.filter(e => e.entryPoint.compiledByAngular); // Add the Angular compiled entry points to the graph as nodes - angularEntryPoints.forEach(entryPoint => graph.addNode(entryPoint.path, entryPoint)); + angularEntryPoints.forEach(e => graph.addNode(e.entryPoint.path, e.entryPoint)); // Now add the dependencies between them - angularEntryPoints.forEach(entryPoint => { - const {dependencies, missing, deepImports} = this.getEntryPointDependencies(entryPoint); - + angularEntryPoints.forEach(({entryPoint, depInfo: {dependencies, missing, deepImports}}) => { const missingDependencies = Array.from(missing).filter(dep => !builtinNodeJsModules.has(dep)); if (missingDependencies.length > 0 && !entryPoint.ignoreMissingDependencies) { diff --git a/packages/compiler-cli/ngcc/src/dependencies/dts_dependency_host.ts b/packages/compiler-cli/ngcc/src/dependencies/dts_dependency_host.ts index 5d23c596a7e73..7eef7f3a3810a 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dts_dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dts_dependency_host.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; -import {PathMappings} from '../utils'; +import {PathMappings} from '../ngcc_options'; import {EsmDependencyHost} from './esm_dependency_host'; import {ModuleResolver} from './module_resolver'; diff --git a/packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts b/packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts index 1179dcd53fa5d..4409ece280d7b 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/module_resolver.ts @@ -5,8 +5,9 @@ * 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 {AbsoluteFsPath, FileSystem, absoluteFrom, dirname, isRoot, join, resolve} from '../../../src/ngtsc/file_system'; -import {PathMappings, isRelativePath, resolveFileWithPostfixes} from '../utils'; +import {absoluteFrom, AbsoluteFsPath, dirname, FileSystem, isRoot, join, resolve} from '../../../src/ngtsc/file_system'; +import {PathMappings} from '../ngcc_options'; +import {isRelativePath, resolveFileWithPostfixes} from '../utils'; /** * This is a very cut-down implementation of the TypeScript module resolution strategy. @@ -222,7 +223,7 @@ export class ModuleResolver { } /** The result of resolving an import to a module. */ -export type ResolvedModule = ResolvedExternalModule | ResolvedRelativeModule | ResolvedDeepImport; +export type ResolvedModule = ResolvedExternalModule|ResolvedRelativeModule|ResolvedDeepImport; /** * A module that is external to the package doing the importing. diff --git a/packages/compiler-cli/ngcc/src/dependencies/umd_dependency_host.ts b/packages/compiler-cli/ngcc/src/dependencies/umd_dependency_host.ts index ab2eb677e5bbf..7cc1b1c17220d 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/umd_dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/umd_dependency_host.ts @@ -84,5 +84,7 @@ export class UmdDependencyHost extends DependencyHostBase { * @returns false if there are definitely no require calls * in this file, true otherwise. */ - private hasRequireCalls(source: string): boolean { return /require\(['"]/.test(source); } + private hasRequireCalls(source: string): boolean { + return /require\(['"]/.test(source); + } } diff --git a/packages/compiler-cli/ngcc/src/entry_point_finder/directory_walker_entry_point_finder.ts b/packages/compiler-cli/ngcc/src/entry_point_finder/directory_walker_entry_point_finder.ts index 50a67e8100920..8ac19b1495826 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/directory_walker_entry_point_finder.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/directory_walker_entry_point_finder.ts @@ -5,23 +5,25 @@ * 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 {AbsoluteFsPath, FileSystem, join, resolve} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver'; import {Logger} from '../logging/logger'; +import {PathMappings} from '../ngcc_options'; import {NgccConfiguration} from '../packages/configuration'; -import {EntryPoint, INVALID_ENTRY_POINT, NO_ENTRY_POINT, getEntryPointInfo} from '../packages/entry_point'; +import {getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from '../packages/entry_point'; import {EntryPointManifest} from '../packages/entry_point_manifest'; -import {PathMappings} from '../utils'; import {NGCC_DIRECTORY} from '../writing/new_entry_point_file_writer'; + import {EntryPointFinder} from './interface'; -import {getBasePaths} from './utils'; +import {getBasePaths, trackDuration} from './utils'; /** * An EntryPointFinder that searches for all entry-points that can be found given a `basePath` and * `pathMappings`. */ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder { - private basePaths = getBasePaths(this.sourceDirectory, this.pathMappings); + private basePaths = getBasePaths(this.logger, this.sourceDirectory, this.pathMappings); constructor( private fs: FileSystem, private config: NgccConfiguration, private logger: Logger, private resolver: DependencyResolver, private entryPointManifest: EntryPointManifest, @@ -31,143 +33,155 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder { * all package entry-points. */ findEntryPoints(): SortedEntryPointsInfo { - const unsortedEntryPoints: EntryPoint[] = []; + const unsortedEntryPoints: EntryPointWithDependencies[] = []; for (const basePath of this.basePaths) { - let entryPoints = this.entryPointManifest.readEntryPointsUsingManifest(basePath); - if (entryPoints === null) { - this.logger.debug( - `No manifest found for ${basePath} so walking the directories for entry-points.`); - const startTime = Date.now(); - entryPoints = this.walkDirectoryForEntryPoints(basePath); - const duration = Math.round((Date.now() - startTime) / 100) / 10; - this.logger.debug(`Walking directories took ${duration}s.`); - - this.entryPointManifest.writeEntryPointManifest(basePath, entryPoints); - } - unsortedEntryPoints.push(...entryPoints); + const entryPoints = this.entryPointManifest.readEntryPointsUsingManifest(basePath) || + this.walkBasePathForPackages(basePath); + entryPoints.forEach(e => unsortedEntryPoints.push(e)); } return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints); } /** - * Look for entry points that need to be compiled, starting at the source directory. + * Search the `basePath` for possible Angular packages and entry-points. + * + * @param basePath The path at which to start the search + * @returns an array of `EntryPoint`s that were found within `basePath`. + */ + walkBasePathForPackages(basePath: AbsoluteFsPath): EntryPointWithDependencies[] { + this.logger.debug( + `No manifest found for ${basePath} so walking the directories for entry-points.`); + const entryPoints = trackDuration( + () => this.walkDirectoryForPackages(basePath), + duration => this.logger.debug(`Walking ${basePath} for entry-points took ${duration}s.`)); + this.entryPointManifest.writeEntryPointManifest(basePath, entryPoints); + return entryPoints; + } + + /** + * Look for Angular packages that need to be compiled, starting at the source directory. * The function will recurse into directories that start with `@...`, e.g. `@angular/...`. + * * @param sourceDirectory An absolute path to the root directory where searching begins. + * @returns an array of `EntryPoint`s that were found within `sourceDirectory`. */ - walkDirectoryForEntryPoints(sourceDirectory: AbsoluteFsPath): EntryPoint[] { - const entryPoints = this.getEntryPointsForPackage(sourceDirectory); - if (entryPoints === null) { + walkDirectoryForPackages(sourceDirectory: AbsoluteFsPath): EntryPointWithDependencies[] { + // Try to get a primary entry point from this directory + const primaryEntryPoint = + getEntryPointInfo(this.fs, this.config, this.logger, sourceDirectory, sourceDirectory); + + // If there is an entry-point but it is not compatible with ngcc (it has a bad package.json or + // invalid typings) then exit. It is unlikely that such an entry point has a dependency on an + // Angular library. + if (primaryEntryPoint === INCOMPATIBLE_ENTRY_POINT) { return []; } - if (entryPoints.length > 0) { - // The `sourceDirectory` is an entry point itself so no need to search its sub-directories. - // Also check for any nested node_modules in this package but only if it was compiled by - // Angular. - // It is unlikely that a non Angular entry point has a dependency on an Angular library. - if (entryPoints.some(e => e.compiledByAngular)) { + const entryPoints: EntryPointWithDependencies[] = []; + if (primaryEntryPoint !== NO_ENTRY_POINT) { + entryPoints.push(this.resolver.getEntryPointWithDependencies(primaryEntryPoint)); + this.collectSecondaryEntryPoints( + entryPoints, sourceDirectory, sourceDirectory, this.fs.readdir(sourceDirectory)); + + // Also check for any nested node_modules in this package but only if at least one of the + // entry-points was compiled by Angular. + if (entryPoints.some(e => e.entryPoint.compiledByAngular)) { const nestedNodeModulesPath = this.fs.join(sourceDirectory, 'node_modules'); if (this.fs.exists(nestedNodeModulesPath)) { - entryPoints.push(...this.walkDirectoryForEntryPoints(nestedNodeModulesPath)); + entryPoints.push(...this.walkDirectoryForPackages(nestedNodeModulesPath)); } } return entryPoints; } - this.fs - .readdir(sourceDirectory) - // Not interested in hidden files - .filter(p => !p.startsWith('.')) - // Ignore node_modules - .filter(p => p !== 'node_modules' && p !== NGCC_DIRECTORY) - // Only interested in directories (and only those that are not symlinks) - .filter(p => { - const stat = this.fs.lstat(resolve(sourceDirectory, p)); - return stat.isDirectory() && !stat.isSymbolicLink(); - }) - .forEach(p => { - // Package is a potential namespace containing packages (e.g `@angular`). - const packagePath = join(sourceDirectory, p); - entryPoints.push(...this.walkDirectoryForEntryPoints(packagePath)); - }); + // The `sourceDirectory` was not a package (i.e. there was no package.json) + // So search its sub-directories for Angular packages and entry-points + for (const path of this.fs.readdir(sourceDirectory)) { + if (isIgnorablePath(path)) { + // Ignore hidden files, node_modules and ngcc directory + continue; + } + + const absolutePath = this.fs.resolve(sourceDirectory, path); + const stat = this.fs.lstat(absolutePath); + if (stat.isSymbolicLink() || !stat.isDirectory()) { + // Ignore symbolic links and non-directories + continue; + } + + entryPoints.push(...this.walkDirectoryForPackages(this.fs.join(sourceDirectory, path))); + } + return entryPoints; } /** - * Recurse the folder structure looking for all the entry points - * @param packagePath The absolute path to an npm package that may contain entry points - * @returns An array of entry points that were discovered or null when it's not a valid entrypoint + * Search the `directory` looking for any secondary entry-points for a package, adding any that + * are found to the `entryPoints` array. + * + * @param entryPoints An array where we will add any entry-points found in this directory + * @param packagePath The absolute path to the package that may contain entry-points + * @param directory The current directory being searched + * @param paths The paths contained in the current `directory`. */ - private getEntryPointsForPackage(packagePath: AbsoluteFsPath): EntryPoint[]|null { - const entryPoints: EntryPoint[] = []; - - // Try to get an entry point from the top level package directory - const topLevelEntryPoint = - getEntryPointInfo(this.fs, this.config, this.logger, packagePath, packagePath); - - // If there is no primary entry-point then exit - if (topLevelEntryPoint === NO_ENTRY_POINT) { - return []; - } + private collectSecondaryEntryPoints( + entryPoints: EntryPointWithDependencies[], packagePath: AbsoluteFsPath, + directory: AbsoluteFsPath, paths: PathSegment[]): void { + for (const path of paths) { + if (isIgnorablePath(path)) { + // Ignore hidden files, node_modules and ngcc directory + continue; + } - if (topLevelEntryPoint === INVALID_ENTRY_POINT) { - return null; - } + const absolutePath = this.fs.resolve(directory, path); + const stat = this.fs.lstat(absolutePath); + if (stat.isSymbolicLink()) { + // Ignore symbolic links + continue; + } - // Otherwise store it and search for secondary entry-points - entryPoints.push(topLevelEntryPoint); - this.walkDirectory(packagePath, packagePath, (path, isDirectory) => { + const isDirectory = stat.isDirectory(); if (!path.endsWith('.js') && !isDirectory) { - return false; + // Ignore files that do not end in `.js` + continue; } - // If the path is a JS file then strip its extension and see if we can match an entry-point. - const possibleEntryPointPath = isDirectory ? path : stripJsExtension(path); + // If the path is a JS file then strip its extension and see if we can match an + // entry-point. + const possibleEntryPointPath = isDirectory ? absolutePath : stripJsExtension(absolutePath); + let isEntryPoint = false; const subEntryPoint = getEntryPointInfo(this.fs, this.config, this.logger, packagePath, possibleEntryPointPath); - if (subEntryPoint === NO_ENTRY_POINT || subEntryPoint === INVALID_ENTRY_POINT) { - return false; + if (subEntryPoint !== NO_ENTRY_POINT && subEntryPoint !== INCOMPATIBLE_ENTRY_POINT) { + entryPoints.push(this.resolver.getEntryPointWithDependencies(subEntryPoint)); + isEntryPoint = true; } - entryPoints.push(subEntryPoint); - return true; - }); - return entryPoints; - } + if (!isDirectory) { + // This path is not a directory so we are done. + continue; + } - /** - * Recursively walk a directory and its sub-directories, applying a given - * function to each directory. - * @param dir the directory to recursively walk. - * @param fn the function to apply to each directory. - */ - private walkDirectory( - packagePath: AbsoluteFsPath, dir: AbsoluteFsPath, - fn: (path: AbsoluteFsPath, isDirectory: boolean) => boolean) { - return this.fs - .readdir(dir) - // Not interested in hidden files - .filter(path => !path.startsWith('.')) - // Ignore node_modules - .filter(path => path !== 'node_modules' && path !== NGCC_DIRECTORY) - .forEach(path => { - const absolutePath = resolve(dir, path); - const stat = this.fs.lstat(absolutePath); - - if (stat.isSymbolicLink()) { - // We are not interested in symbolic links - return; - } - - const containsEntryPoint = fn(absolutePath, stat.isDirectory()); - if (containsEntryPoint) { - this.walkDirectory(packagePath, absolutePath, fn); - } - }); + // This directory may contain entry-points of its own. + const childPaths = this.fs.readdir(absolutePath); + if (!isEntryPoint && + childPaths.some( + childPath => childPath.endsWith('.js') && + this.fs.stat(this.fs.resolve(absolutePath, childPath)).isFile())) { + // We do not consider non-entry-point directories that contain JS files as they are very + // unlikely to be containers for sub-entry-points. + continue; + } + this.collectSecondaryEntryPoints(entryPoints, packagePath, absolutePath, childPaths); + } } } function stripJsExtension<T extends string>(filePath: T): T { return filePath.replace(/\.js$/, '') as T; } + +function isIgnorablePath(path: PathSegment): boolean { + return path.startsWith('.') || path === 'node_modules' || path === NGCC_DIRECTORY; +} diff --git a/packages/compiler-cli/ngcc/src/entry_point_finder/targeted_entry_point_finder.ts b/packages/compiler-cli/ngcc/src/entry_point_finder/targeted_entry_point_finder.ts index 35a63a5698179..abd5c2f1a9ff0 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/targeted_entry_point_finder.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/targeted_entry_point_finder.ts @@ -5,13 +5,15 @@ * 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 {AbsoluteFsPath, FileSystem, PathSegment, join, relative, relativeFrom} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, join, PathSegment, relative, relativeFrom} from '../../../src/ngtsc/file_system'; +import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver'; import {Logger} from '../logging/logger'; +import {PathMappings} from '../ngcc_options'; import {hasBeenProcessed} from '../packages/build_marker'; import {NgccConfiguration} from '../packages/configuration'; -import {EntryPoint, EntryPointJsonProperty, INVALID_ENTRY_POINT, NO_ENTRY_POINT, getEntryPointInfo} from '../packages/entry_point'; -import {PathMappings} from '../utils'; +import {EntryPoint, EntryPointJsonProperty, getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from '../packages/entry_point'; + import {EntryPointFinder} from './interface'; import {getBasePaths} from './utils'; @@ -24,8 +26,8 @@ import {getBasePaths} from './utils'; */ export class TargetedEntryPointFinder implements EntryPointFinder { private unprocessedPaths: AbsoluteFsPath[] = []; - private unsortedEntryPoints = new Map<AbsoluteFsPath, EntryPoint>(); - private basePaths = getBasePaths(this.basePath, this.pathMappings); + private unsortedEntryPoints = new Map<AbsoluteFsPath, EntryPointWithDependencies>(); + private basePaths = getBasePaths(this.logger, this.basePath, this.pathMappings); constructor( private fs: FileSystem, private config: NgccConfiguration, private logger: Logger, @@ -39,7 +41,7 @@ export class TargetedEntryPointFinder implements EntryPointFinder { } const targetEntryPoint = this.unsortedEntryPoints.get(this.targetPath); const entryPoints = this.resolver.sortEntryPointsByDependency( - Array.from(this.unsortedEntryPoints.values()), targetEntryPoint); + Array.from(this.unsortedEntryPoints.values()), targetEntryPoint?.entryPoint); const invalidTarget = entryPoints.invalidEntryPoints.find(i => i.entryPoint.path === this.targetPath); @@ -76,14 +78,14 @@ export class TargetedEntryPointFinder implements EntryPointFinder { } private processNextPath(): void { - const path = this.unprocessedPaths.shift() !; + const path = this.unprocessedPaths.shift()!; const entryPoint = this.getEntryPoint(path); if (entryPoint === null || !entryPoint.compiledByAngular) { return; } - this.unsortedEntryPoints.set(entryPoint.path, entryPoint); - const deps = this.resolver.getEntryPointDependencies(entryPoint); - deps.dependencies.forEach(dep => { + const entryPointWithDeps = this.resolver.getEntryPointWithDependencies(entryPoint); + this.unsortedEntryPoints.set(entryPoint.path, entryPointWithDeps); + entryPointWithDeps.depInfo.dependencies.forEach(dep => { if (!this.unsortedEntryPoints.has(dep)) { this.unprocessedPaths.push(dep); } @@ -94,7 +96,7 @@ export class TargetedEntryPointFinder implements EntryPointFinder { const packagePath = this.computePackagePath(entryPointPath); const entryPoint = getEntryPointInfo(this.fs, this.config, this.logger, packagePath, entryPointPath); - if (entryPoint === NO_ENTRY_POINT || entryPoint === INVALID_ENTRY_POINT) { + if (entryPoint === NO_ENTRY_POINT || entryPoint === INCOMPATIBLE_ENTRY_POINT) { return null; } return entryPoint; @@ -130,7 +132,7 @@ export class TargetedEntryPointFinder implements EntryPointFinder { // Start the search at the deepest nested `node_modules` folder that is below the `basePath` // but above the `entryPointPath`, if there are any. while (nodeModulesIndex >= 0) { - packagePath = join(packagePath, segments.shift() !); + packagePath = join(packagePath, segments.shift()!); nodeModulesIndex--; } @@ -150,13 +152,35 @@ export class TargetedEntryPointFinder implements EntryPointFinder { break; } } - // If we get here then none of the `basePaths` matched the `entryPointPath`, which - // is somewhat unexpected and means that this entry-point lives completely outside - // any of the `basePaths`. - // All we can do is assume that his entry-point is a primary entry-point to a package. - return entryPointPath; - } + // We couldn't find a `packagePath` using `basePaths` so try to find the nearest `node_modules` + // that contains the `entryPointPath`, if there is one, and use it as a `basePath`. + let packagePath = entryPointPath; + let scopedPackagePath = packagePath; + let containerPath = this.fs.dirname(packagePath); + while (!this.fs.isRoot(containerPath) && !containerPath.endsWith('node_modules')) { + scopedPackagePath = packagePath; + packagePath = containerPath; + containerPath = this.fs.dirname(containerPath); + } + + if (this.fs.exists(join(packagePath, 'package.json'))) { + // The directory directly below `node_modules` is a package - use it + return packagePath; + } else if ( + this.fs.basename(packagePath).startsWith('@') && + this.fs.exists(join(scopedPackagePath, 'package.json'))) { + // The directory directly below the `node_modules` is a scope and the directory directly + // below that is a scoped package - use it + return scopedPackagePath; + } else { + // If we get here then none of the `basePaths` contained the `entryPointPath` and the + // `entryPointPath` contains no `node_modules` that contains a package or a scoped + // package. All we can do is assume that this entry-point is a primary entry-point to a + // package. + return entryPointPath; + } + } /** * Split the given `path` into path segments using an FS independent algorithm. diff --git a/packages/compiler-cli/ngcc/src/entry_point_finder/utils.ts b/packages/compiler-cli/ngcc/src/entry_point_finder/utils.ts index 7a43c237a7fc8..9442958e66c5f 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/utils.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/utils.ts @@ -5,8 +5,9 @@ * 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 {AbsoluteFsPath, getFileSystem, join, relative, resolve} from '../../../src/ngtsc/file_system'; -import {PathMappings} from '../utils'; +import {AbsoluteFsPath, getFileSystem, relative, resolve} from '../../../src/ngtsc/file_system'; +import {Logger} from '../logging/logger'; +import {PathMappings} from '../ngcc_options'; /** * Extract all the base-paths that we need to search for entry-points. @@ -28,22 +29,45 @@ import {PathMappings} from '../utils'; * @param pathMappings Path mapping configuration, from which to extract additional base-paths. */ export function getBasePaths( - sourceDirectory: AbsoluteFsPath, pathMappings: PathMappings | undefined): AbsoluteFsPath[] { + logger: Logger, sourceDirectory: AbsoluteFsPath, + pathMappings: PathMappings|undefined): AbsoluteFsPath[] { const fs = getFileSystem(); - let basePaths = [sourceDirectory]; + const basePaths = [sourceDirectory]; if (pathMappings) { const baseUrl = resolve(pathMappings.baseUrl); + if (fs.isRoot(baseUrl)) { + logger.warn( + `The provided pathMappings baseUrl is the root path ${baseUrl}.\n` + + `This is likely to mess up how ngcc finds entry-points and is probably not correct.\n` + + `Please check your path mappings configuration such as in the tsconfig.json file.`); + } Object.values(pathMappings.paths).forEach(paths => paths.forEach(path => { // We only want base paths that exist and are not files - let basePath = join(baseUrl, extractPathPrefix(path)); - while (basePath !== baseUrl && (!fs.exists(basePath) || fs.stat(basePath).isFile())) { + let basePath = fs.resolve(baseUrl, extractPathPrefix(path)); + if (fs.exists(basePath) && fs.stat(basePath).isFile()) { basePath = fs.dirname(basePath); } - basePaths.push(basePath); + if (fs.exists(basePath)) { + basePaths.push(basePath); + } else { + logger.debug( + `The basePath "${basePath}" computed from baseUrl "${baseUrl}" and path mapping "${ + path}" does not exist in the file-system.\n` + + `It will not be scanned for entry-points.`); + } })); } basePaths.sort().reverse(); // Get the paths in order with the longer ones first. - return basePaths.filter(removeContainedPaths); + const dedupedBasePaths = basePaths.filter(removeContainedPaths); + + // We want to ensure that the `sourceDirectory` is included when it is a node_modules folder. + // Otherwise our entry-point finding algorithm would fail to walk that folder. + if (fs.basename(sourceDirectory) === 'node_modules' && + !dedupedBasePaths.includes(sourceDirectory)) { + dedupedBasePaths.unshift(sourceDirectory); + } + + return dedupedBasePaths; } /** @@ -78,3 +102,19 @@ function removeContainedPaths(value: AbsoluteFsPath, index: number, array: Absol } return true; } + +/** + * Run a task and track how long it takes. + * + * @param task The task whose duration we are tracking + * @param log The function to call with the duration of the task + * @returns The result of calling `task`. + */ +export function trackDuration<T = void>(task: () => T extends Promise<unknown>? never : T, + log: (duration: number) => void): T { + const startTime = Date.now(); + const result = task(); + const duration = Math.round((Date.now() - startTime) / 100) / 10; + log(duration); + return result; +} diff --git a/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts b/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts new file mode 100644 index 0000000000000..0a3643e4decc1 --- /dev/null +++ b/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts @@ -0,0 +1,172 @@ +/** + * @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 {DepGraph} from 'dependency-graph'; + +import {FileSystem} from '../../../src/ngtsc/file_system'; +import {InvalidEntryPoint} from '../dependencies/dependency_resolver'; +import {EntryPointFinder} from '../entry_point_finder/interface'; +import {ParallelTaskQueue} from '../execution/tasks/queues/parallel_task_queue'; +import {SerialTaskQueue} from '../execution/tasks/queues/serial_task_queue'; +import {computeTaskDependencies} from '../execution/tasks/utils'; +import {Logger} from '../logging/logger'; +import {hasBeenProcessed} from '../packages/build_marker'; +import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FORMAT_PROPERTIES} from '../packages/entry_point'; +import {cleanOutdatedPackages} from '../writing/cleaning/package_cleaner'; + +import {AnalyzeEntryPointsFn} from './api'; +import {PartiallyOrderedTasks, TaskQueue} from './tasks/api'; + +/** + * Create the function for performing the analysis of the entry-points. + */ +export function getAnalyzeEntryPointsFn( + logger: Logger, finder: EntryPointFinder, fileSystem: FileSystem, + supportedPropertiesToConsider: EntryPointJsonProperty[], compileAllFormats: boolean, + propertiesToConsider: string[], inParallel: boolean): AnalyzeEntryPointsFn { + return () => { + logger.debug('Analyzing entry-points...'); + const startTime = Date.now(); + + let entryPointInfo = finder.findEntryPoints(); + const cleaned = cleanOutdatedPackages(fileSystem, entryPointInfo.entryPoints); + if (cleaned) { + // If we had to clean up one or more packages then we must read in the entry-points again. + entryPointInfo = finder.findEntryPoints(); + } + + const {entryPoints, invalidEntryPoints, graph} = entryPointInfo; + logInvalidEntryPoints(logger, invalidEntryPoints); + + const unprocessableEntryPointPaths: string[] = []; + // The tasks are partially ordered by virtue of the entry-points being partially ordered too. + const tasks: PartiallyOrderedTasks = [] as any; + + for (const entryPoint of entryPoints) { + const packageJson = entryPoint.packageJson; + const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings'); + const {propertiesToProcess, equivalentPropertiesMap} = + getPropertiesToProcess(packageJson, supportedPropertiesToConsider, compileAllFormats); + let processDts = !hasProcessedTypings; + + if (propertiesToProcess.length === 0) { + // This entry-point is unprocessable (i.e. there is no format property that is of interest + // and can be processed). This will result in an error, but continue looping over + // entry-points in order to collect all unprocessable ones and display a more informative + // error. + unprocessableEntryPointPaths.push(entryPoint.path); + continue; + } + + for (const formatProperty of propertiesToProcess) { + if (hasBeenProcessed(entryPoint.packageJson, formatProperty)) { + // The format-path which the property maps to is already processed - nothing to do. + logger.debug(`Skipping ${entryPoint.name} : ${formatProperty} (already compiled).`); + continue; + } + + const formatPropertiesToMarkAsProcessed = equivalentPropertiesMap.get(formatProperty)!; + tasks.push({entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts}); + + // Only process typings for the first property (if not already processed). + processDts = false; + } + } + + // Check for entry-points for which we could not process any format at all. + if (unprocessableEntryPointPaths.length > 0) { + throw new Error( + 'Unable to process any formats for the following entry-points (tried ' + + `${propertiesToConsider.join(', ')}): ` + + unprocessableEntryPointPaths.map(path => `\n - ${path}`).join('')); + } + + const duration = Math.round((Date.now() - startTime) / 100) / 10; + logger.debug( + `Analyzed ${entryPoints.length} entry-points in ${duration}s. ` + + `(Total tasks: ${tasks.length})`); + + return getTaskQueue(logger, inParallel, tasks, graph); + }; +} + +function logInvalidEntryPoints(logger: Logger, invalidEntryPoints: InvalidEntryPoint[]): void { + invalidEntryPoints.forEach(invalidEntryPoint => { + logger.debug( + `Invalid entry-point ${invalidEntryPoint.entryPoint.path}.`, + `It is missing required dependencies:\n` + + invalidEntryPoint.missingDependencies.map(dep => ` - ${dep}`).join('\n')); + }); +} + +/** + * This function computes and returns the following: + * - `propertiesToProcess`: An (ordered) list of properties that exist and need to be processed, + * based on the provided `propertiesToConsider`, the properties in `package.json` and their + * corresponding format-paths. NOTE: Only one property per format-path needs to be processed. + * - `equivalentPropertiesMap`: A mapping from each property in `propertiesToProcess` to the list of + * other format properties in `package.json` that need to be marked as processed as soon as the + * former has been processed. + */ +function getPropertiesToProcess( + packageJson: EntryPointPackageJson, propertiesToConsider: EntryPointJsonProperty[], + compileAllFormats: boolean): { + propertiesToProcess: EntryPointJsonProperty[]; + equivalentPropertiesMap: Map<EntryPointJsonProperty, EntryPointJsonProperty[]>; +} { + const formatPathsToConsider = new Set<string>(); + + const propertiesToProcess: EntryPointJsonProperty[] = []; + for (const prop of propertiesToConsider) { + const formatPath = packageJson[prop]; + + // Ignore properties that are not defined in `package.json`. + if (typeof formatPath !== 'string') continue; + + // Ignore properties that map to the same format-path as a preceding property. + if (formatPathsToConsider.has(formatPath)) continue; + + // Process this property, because it is the first one to map to this format-path. + formatPathsToConsider.add(formatPath); + propertiesToProcess.push(prop); + + // If we only need one format processed, there is no need to process any more properties. + if (!compileAllFormats) break; + } + + const formatPathToProperties: {[formatPath: string]: EntryPointJsonProperty[]} = {}; + for (const prop of SUPPORTED_FORMAT_PROPERTIES) { + const formatPath = packageJson[prop]; + + // Ignore properties that are not defined in `package.json`. + if (typeof formatPath !== 'string') continue; + + // Ignore properties that do not map to a format-path that will be considered. + if (!formatPathsToConsider.has(formatPath)) continue; + + // Add this property to the map. + const list = formatPathToProperties[formatPath] || (formatPathToProperties[formatPath] = []); + list.push(prop); + } + + const equivalentPropertiesMap = new Map<EntryPointJsonProperty, EntryPointJsonProperty[]>(); + for (const prop of propertiesToConsider) { + const formatPath = packageJson[prop]!; + const equivalentProperties = formatPathToProperties[formatPath]; + equivalentPropertiesMap.set(prop, equivalentProperties); + } + + return {propertiesToProcess, equivalentPropertiesMap}; +} + +function getTaskQueue( + logger: Logger, inParallel: boolean, tasks: PartiallyOrderedTasks, + graph: DepGraph<EntryPoint>): TaskQueue { + const dependencies = computeTaskDependencies(tasks, graph); + return inParallel ? new ParallelTaskQueue(logger, tasks, dependencies) : + new SerialTaskQueue(logger, tasks, dependencies); +} diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/api.ts b/packages/compiler-cli/ngcc/src/execution/cluster/api.ts index 970e3fd5be166..2325d52670cdb 100644 --- a/packages/compiler-cli/ngcc/src/execution/cluster/api.ts +++ b/packages/compiler-cli/ngcc/src/execution/cluster/api.ts @@ -44,7 +44,7 @@ export interface UpdatePackageJsonMessage extends JsonObject { } /** The type of messages sent from cluster workers to the cluster master. */ -export type MessageFromWorker = ErrorMessage | TaskCompletedMessage | UpdatePackageJsonMessage; +export type MessageFromWorker = ErrorMessage|TaskCompletedMessage|UpdatePackageJsonMessage; /** The type of messages sent from the cluster master to cluster workers. */ export type MessageToWorker = ProcessTaskMessage; diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/executor.ts b/packages/compiler-cli/ngcc/src/execution/cluster/executor.ts index dd381bb81c9b5..b595b077818d7 100644 --- a/packages/compiler-cli/ngcc/src/execution/cluster/executor.ts +++ b/packages/compiler-cli/ngcc/src/execution/cluster/executor.ts @@ -5,11 +5,7 @@ * 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 */ - -/// <reference types="node" /> - -import * as cluster from 'cluster'; - +import {FileSystem} from '../../../../src/ngtsc/file_system'; import {AsyncLocker} from '../../locking/async_locker'; import {Logger} from '../../logging/logger'; import {PackageJsonUpdater} from '../../writing/package_json_updater'; @@ -17,8 +13,6 @@ import {AnalyzeEntryPointsFn, CreateCompileFn, Executor} from '../api'; import {CreateTaskCompletedCallback} from '../tasks/api'; import {ClusterMaster} from './master'; -import {ClusterWorker} from './worker'; - /** * An `Executor` that processes tasks in parallel (on multiple processes) and completes @@ -26,26 +20,19 @@ import {ClusterWorker} from './worker'; */ export class ClusterExecutor implements Executor { constructor( - private workerCount: number, private logger: Logger, + private workerCount: number, private fileSystem: FileSystem, private logger: Logger, private pkgJsonUpdater: PackageJsonUpdater, private lockFile: AsyncLocker, private createTaskCompletedCallback: CreateTaskCompletedCallback) {} - async execute(analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn): + async execute(analyzeEntryPoints: AnalyzeEntryPointsFn, _createCompileFn: CreateCompileFn): Promise<void> { - if (cluster.isMaster) { - // This process is the cluster master. - return this.lockFile.lock(() => { - this.logger.debug( - `Running ngcc on ${this.constructor.name} (using ${this.workerCount} worker processes).`); - const master = new ClusterMaster( - this.workerCount, this.logger, this.pkgJsonUpdater, analyzeEntryPoints, - this.createTaskCompletedCallback); - return master.run(); - }); - } else { - // This process is a cluster worker. - const worker = new ClusterWorker(this.logger, createCompileFn); - return worker.run(); - } + return this.lockFile.lock(() => { + this.logger.debug( + `Running ngcc on ${this.constructor.name} (using ${this.workerCount} worker processes).`); + const master = new ClusterMaster( + this.workerCount, this.fileSystem, this.logger, this.pkgJsonUpdater, analyzeEntryPoints, + this.createTaskCompletedCallback); + return master.run(); + }); } } diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/master.ts b/packages/compiler-cli/ngcc/src/execution/cluster/master.ts index b3a0abec4fe1a..669960612e79f 100644 --- a/packages/compiler-cli/ngcc/src/execution/cluster/master.ts +++ b/packages/compiler-cli/ngcc/src/execution/cluster/master.ts @@ -10,7 +10,7 @@ import * as cluster from 'cluster'; -import {resolve} from '../../../../src/ngtsc/file_system'; +import {FileSystem} from '../../../../src/ngtsc/file_system'; import {Logger} from '../../logging/logger'; import {PackageJsonUpdater} from '../../writing/package_json_updater'; import {AnalyzeEntryPointsFn} from '../api'; @@ -33,13 +33,16 @@ export class ClusterMaster { private onTaskCompleted: TaskCompletedCallback; constructor( - private workerCount: number, private logger: Logger, + private maxWorkerCount: number, private fileSystem: FileSystem, private logger: Logger, private pkgJsonUpdater: PackageJsonUpdater, analyzeEntryPoints: AnalyzeEntryPointsFn, createTaskCompletedCallback: CreateTaskCompletedCallback) { if (!cluster.isMaster) { throw new Error('Tried to instantiate `ClusterMaster` on a worker process.'); } + // Set the worker entry-point + cluster.setupMaster({exec: this.fileSystem.resolve(__dirname, 'worker.js')}); + this.taskQueue = analyzeEntryPoints(); this.onTaskCompleted = createTaskCompletedCallback(this.taskQueue); } @@ -105,14 +108,14 @@ export class ClusterMaster { } if (!isWorkerAvailable) { - if (this.taskAssignments.size < this.workerCount) { + const spawnedWorkerCount = Object.keys(cluster.workers).length; + if (spawnedWorkerCount < this.maxWorkerCount) { this.logger.debug('Spawning another worker process as there is more work to be done.'); cluster.fork(); } else { // If there are no available workers or no available tasks, log (for debugging purposes). this.logger.debug( - `All ${this.taskAssignments.size} workers are currently busy and cannot take on more ` + - 'work.'); + `All ${spawnedWorkerCount} workers are currently busy and cannot take on more work.`); } } else { const busyWorkers = Array.from(this.taskAssignments) @@ -227,7 +230,7 @@ export class ClusterMaster { JSON.stringify(msg)); } - const expectedPackageJsonPath = resolve(task.entryPoint.path, 'package.json'); + const expectedPackageJsonPath = this.fileSystem.resolve(task.entryPoint.path, 'package.json'); const parsedPackageJson = task.entryPoint.packageJson; if (expectedPackageJsonPath !== msg.packageJsonPath) { @@ -262,7 +265,7 @@ export class ClusterMaster { */ private wrapEventHandler<Args extends unknown[]>(fn: (...args: Args) => void|Promise<void>): (...args: Args) => Promise<void> { - return async(...args: Args) => { + return async (...args: Args) => { try { await fn(...args); } catch (err) { diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/package_json_updater.ts b/packages/compiler-cli/ngcc/src/execution/cluster/package_json_updater.ts index 6014045507581..3f0ccde6b4af8 100644 --- a/packages/compiler-cli/ngcc/src/execution/cluster/package_json_updater.ts +++ b/packages/compiler-cli/ngcc/src/execution/cluster/package_json_updater.ts @@ -12,32 +12,32 @@ import * as cluster from 'cluster'; import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system'; import {JsonObject} from '../../packages/entry_point'; -import {PackageJsonChange, PackageJsonUpdate, PackageJsonUpdater, applyChange} from '../../writing/package_json_updater'; +import {applyChange, PackageJsonChange, PackageJsonUpdate, PackageJsonUpdater} from '../../writing/package_json_updater'; import {sendMessageToMaster} from './utils'; /** - * A `PackageJsonUpdater` that can safely handle update operations on multiple processes. + * A `PackageJsonUpdater` for cluster workers that will send update changes to the master process so + * that it can safely handle update operations on multiple processes. */ -export class ClusterPackageJsonUpdater implements PackageJsonUpdater { - constructor(private delegate: PackageJsonUpdater) {} +export class ClusterWorkerPackageJsonUpdater implements PackageJsonUpdater { + constructor() { + if (cluster.isMaster) { + throw new Error('Tried to create cluster worker PackageJsonUpdater on the master process.'); + } + } createUpdate(): PackageJsonUpdate { return new PackageJsonUpdate((...args) => this.writeChanges(...args)); } + /** + * Apply the changes in-memory (if necessary) and send a message to the master process. + */ writeChanges( changes: PackageJsonChange[], packageJsonPath: AbsoluteFsPath, preExistingParsedJson?: JsonObject): void { - if (cluster.isMaster) { - // This is the master process: - // Actually apply the changes to the file on disk. - return this.delegate.writeChanges(changes, packageJsonPath, preExistingParsedJson); - } - - // This is a worker process: - // Apply the changes in-memory (if necessary) and send a message to the master process. if (preExistingParsedJson) { for (const [propPath, value] of changes) { if (propPath.length === 0) { diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/utils.ts b/packages/compiler-cli/ngcc/src/execution/cluster/utils.ts index 351cf58e73063..b9ca051299dcb 100644 --- a/packages/compiler-cli/ngcc/src/execution/cluster/utils.ts +++ b/packages/compiler-cli/ngcc/src/execution/cluster/utils.ts @@ -23,14 +23,14 @@ export class Deferred<T> { * * @param value The value to resolve the promise with. */ - resolve !: (value: T) => void; + resolve!: (value: T) => void; /** * Rejects the associated promise with the specified reason. * * @param reason The rejection reason. */ - reject !: (reason: any) => void; + reject!: (reason: any) => void; /** The `Promise` instance associated with this deferred. */ promise = new Promise<T>((resolve, reject) => { diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/worker.ts b/packages/compiler-cli/ngcc/src/execution/cluster/worker.ts index 24b6a3745ca51..ca0e57aec520a 100644 --- a/packages/compiler-cli/ngcc/src/execution/cluster/worker.ts +++ b/packages/compiler-cli/ngcc/src/execution/cluster/worker.ts @@ -5,58 +5,86 @@ * 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 */ - /// <reference types="node" /> import * as cluster from 'cluster'; -import {Logger} from '../../logging/logger'; -import {CompileFn, CreateCompileFn} from '../api'; +import {parseCommandLineOptions} from '../../command_line_options'; +import {ConsoleLogger} from '../../logging/console_logger'; +import {Logger, LogLevel} from '../../logging/logger'; +import {getSharedSetup} from '../../ngcc_options'; +import {CreateCompileFn} from '../api'; +import {getCreateCompileFn} from '../create_compile_function'; import {stringifyTask} from '../tasks/utils'; import {MessageToWorker} from './api'; +import {ClusterWorkerPackageJsonUpdater} from './package_json_updater'; import {sendMessageToMaster} from './utils'; +// Cluster worker entry point +if (require.main === module) { + (async () => { + process.title = 'ngcc (worker)'; -/** - * A cluster worker is responsible for processing one task (i.e. one format property for a specific - * entry-point) at a time and reporting results back to the cluster master. - */ -export class ClusterWorker { - private compile: CompileFn; + try { + const { + createNewEntryPointFormats = false, + logger = new ConsoleLogger(LogLevel.info), + pathMappings, + errorOnFailedEntryPoint = false, + enableI18nLegacyMessageIdFormat = true, + fileSystem, + tsConfig + } = getSharedSetup(parseCommandLineOptions(process.argv.slice(2))); - constructor(private logger: Logger, createCompileFn: CreateCompileFn) { - if (cluster.isMaster) { - throw new Error('Tried to instantiate `ClusterWorker` on the master process.'); + // NOTE: To avoid file corruption, `ngcc` invocation only creates _one_ instance of + // `PackageJsonUpdater` that actually writes to disk (across all processes). + // In cluster workers we use a `PackageJsonUpdater` that delegates to the cluster master. + const pkgJsonUpdater = new ClusterWorkerPackageJsonUpdater(); + + // The function for creating the `compile()` function. + const createCompileFn = getCreateCompileFn( + fileSystem, logger, pkgJsonUpdater, createNewEntryPointFormats, errorOnFailedEntryPoint, + enableI18nLegacyMessageIdFormat, tsConfig, pathMappings); + + await startWorker(logger, createCompileFn); + process.exitCode = 0; + } catch (e) { + console.error(e.stack || e.message); + process.exit(1); } + })(); +} - this.compile = createCompileFn( - (_task, outcome, message) => - sendMessageToMaster({type: 'task-completed', outcome, message})); +export async function startWorker(logger: Logger, createCompileFn: CreateCompileFn): Promise<void> { + if (cluster.isMaster) { + throw new Error('Tried to run cluster worker on the master process.'); } - run(): Promise<void> { - // Listen for `ProcessTaskMessage`s and process tasks. - cluster.worker.on('message', (msg: MessageToWorker) => { - try { - switch (msg.type) { - case 'process-task': - this.logger.debug( - `[Worker #${cluster.worker.id}] Processing task: ${stringifyTask(msg.task)}`); - return this.compile(msg.task); - default: - throw new Error( - `[Worker #${cluster.worker.id}] Invalid message received: ${JSON.stringify(msg)}`); - } - } catch (err) { - sendMessageToMaster({ - type: 'error', - error: (err instanceof Error) ? (err.stack || err.message) : err, - }); + const compile = createCompileFn( + (_task, outcome, message) => sendMessageToMaster({type: 'task-completed', outcome, message})); + + + // Listen for `ProcessTaskMessage`s and process tasks. + cluster.worker.on('message', (msg: MessageToWorker) => { + try { + switch (msg.type) { + case 'process-task': + logger.debug( + `[Worker #${cluster.worker.id}] Processing task: ${stringifyTask(msg.task)}`); + return compile(msg.task); + default: + throw new Error( + `[Worker #${cluster.worker.id}] Invalid message received: ${JSON.stringify(msg)}`); } - }); + } catch (err) { + sendMessageToMaster({ + type: 'error', + error: (err instanceof Error) ? (err.stack || err.message) : err, + }); + } + }); - // Return a promise that is never resolved. - return new Promise(() => undefined); - } -} + // Return a promise that is never resolved. + return new Promise(() => undefined); +} \ No newline at end of file diff --git a/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts b/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts new file mode 100644 index 0000000000000..3eab5c296f761 --- /dev/null +++ b/packages/compiler-cli/ngcc/src/execution/create_compile_function.ts @@ -0,0 +1,92 @@ + +/** + * @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 * as ts from 'typescript'; + +import {replaceTsWithNgInErrors} from '../../../src/ngtsc/diagnostics'; +import {FileSystem} from '../../../src/ngtsc/file_system'; +import {ParsedConfiguration} from '../../../src/perform_compile'; +import {Logger} from '../logging/logger'; +import {PathMappings} from '../ngcc_options'; +import {getEntryPointFormat} from '../packages/entry_point'; +import {makeEntryPointBundle} from '../packages/entry_point_bundle'; +import {FileWriter} from '../writing/file_writer'; +import {InPlaceFileWriter} from '../writing/in_place_file_writer'; +import {NewEntryPointFileWriter} from '../writing/new_entry_point_file_writer'; +import {PackageJsonUpdater} from '../writing/package_json_updater'; + +import {CreateCompileFn} from './api'; +import {Task, TaskProcessingOutcome} from './tasks/api'; + +/** + * The function for creating the `compile()` function. + */ +export function getCreateCompileFn( + fileSystem: FileSystem, logger: Logger, pkgJsonUpdater: PackageJsonUpdater, + createNewEntryPointFormats: boolean, errorOnFailedEntryPoint: boolean, + enableI18nLegacyMessageIdFormat: boolean, tsConfig: ParsedConfiguration|null, + pathMappings: PathMappings|undefined): CreateCompileFn { + return onTaskCompleted => { + const fileWriter = getFileWriter( + fileSystem, logger, pkgJsonUpdater, createNewEntryPointFormats, errorOnFailedEntryPoint); + const {Transformer} = require('../packages/transformer'); + const transformer = new Transformer(fileSystem, logger, tsConfig); + + return (task: Task) => { + const {entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts} = task; + + const isCore = entryPoint.name === '@angular/core'; // Are we compiling the Angular core? + const packageJson = entryPoint.packageJson; + const formatPath = packageJson[formatProperty]; + const format = getEntryPointFormat(fileSystem, entryPoint, formatProperty); + + // All properties listed in `propertiesToProcess` are guaranteed to point to a format-path + // (i.e. they are defined in `entryPoint.packageJson`). Furthermore, they are also guaranteed + // to be among `SUPPORTED_FORMAT_PROPERTIES`. + // Based on the above, `formatPath` should always be defined and `getEntryPointFormat()` + // should always return a format here (and not `undefined`). + if (!formatPath || !format) { + // This should never happen. + throw new Error( + `Invariant violated: No format-path or format for ${entryPoint.path} : ` + + `${formatProperty} (formatPath: ${formatPath} | format: ${format})`); + } + + const bundle = makeEntryPointBundle( + fileSystem, entryPoint, formatPath, isCore, format, processDts, pathMappings, true, + enableI18nLegacyMessageIdFormat); + + logger.info(`Compiling ${entryPoint.name} : ${formatProperty} as ${format}`); + + const result = transformer.transform(bundle); + if (result.success) { + if (result.diagnostics.length > 0) { + logger.warn(replaceTsWithNgInErrors( + ts.formatDiagnosticsWithColorAndContext(result.diagnostics, bundle.src.host))); + } + fileWriter.writeBundle(bundle, result.transformedFiles, formatPropertiesToMarkAsProcessed); + + logger.debug(` Successfully compiled ${entryPoint.name} : ${formatProperty}`); + + onTaskCompleted(task, TaskProcessingOutcome.Processed, null); + } else { + const errors = replaceTsWithNgInErrors( + ts.formatDiagnosticsWithColorAndContext(result.diagnostics, bundle.src.host)); + onTaskCompleted(task, TaskProcessingOutcome.Failed, `compilation errors:\n${errors}`); + } + }; + }; +} + +function getFileWriter( + fs: FileSystem, logger: Logger, pkgJsonUpdater: PackageJsonUpdater, + createNewEntryPointFormats: boolean, errorOnFailedEntryPoint: boolean): FileWriter { + return createNewEntryPointFormats ? + new NewEntryPointFileWriter(fs, logger, errorOnFailedEntryPoint, pkgJsonUpdater) : + new InPlaceFileWriter(fs, logger, errorOnFailedEntryPoint); +} diff --git a/packages/compiler-cli/ngcc/src/execution/single_process_executor.ts b/packages/compiler-cli/ngcc/src/execution/single_process_executor.ts index 6d6dbf0553609..20f494af1998c 100644 --- a/packages/compiler-cli/ngcc/src/execution/single_process_executor.ts +++ b/packages/compiler-cli/ngcc/src/execution/single_process_executor.ts @@ -30,7 +30,7 @@ export abstract class SingleProcessorExecutorBase { const startTime = Date.now(); while (!taskQueue.allTasksCompleted) { - const task = taskQueue.getNextTask() !; + const task = taskQueue.getNextTask()!; compile(task); taskQueue.markTaskCompleted(task); } @@ -65,6 +65,6 @@ export class SingleProcessExecutorAsync extends SingleProcessorExecutorBase impl } async execute(analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn): Promise<void> { - await this.lockFile.lock(async() => this.doExecute(analyzeEntryPoints, createCompileFn)); + await this.lockFile.lock(async () => this.doExecute(analyzeEntryPoints, createCompileFn)); } } diff --git a/packages/compiler-cli/ngcc/src/execution/tasks/api.ts b/packages/compiler-cli/ngcc/src/execution/tasks/api.ts index ed2fd2165b6d9..0516b66db8271 100644 --- a/packages/compiler-cli/ngcc/src/execution/tasks/api.ts +++ b/packages/compiler-cli/ngcc/src/execution/tasks/api.ts @@ -66,7 +66,7 @@ export type CreateTaskCompletedCallback = (taskQueue: TaskQueue) => TaskComplete * A function to be called once a task has been processed. */ export type TaskCompletedCallback = - (task: Task, outcome: TaskProcessingOutcome, message: string | null) => void; + (task: Task, outcome: TaskProcessingOutcome, message: string|null) => void; /** * Represents the outcome of processing a `Task`. diff --git a/packages/compiler-cli/ngcc/src/execution/tasks/completion.ts b/packages/compiler-cli/ngcc/src/execution/tasks/completion.ts index 5a8111fa9a642..88f2879aabda2 100644 --- a/packages/compiler-cli/ngcc/src/execution/tasks/completion.ts +++ b/packages/compiler-cli/ngcc/src/execution/tasks/completion.ts @@ -8,8 +8,9 @@ import {FileSystem, resolve} from '../../../../src/ngtsc/file_system'; import {Logger} from '../../logging/logger'; import {markAsProcessed} from '../../packages/build_marker'; -import {PackageJsonFormatProperties, getEntryPointFormat} from '../../packages/entry_point'; +import {getEntryPointFormat, PackageJsonFormatProperties} from '../../packages/entry_point'; import {PackageJsonUpdater} from '../../writing/package_json_updater'; + import {Task, TaskCompletedCallback, TaskProcessingOutcome, TaskQueue} from './api'; /** @@ -18,7 +19,7 @@ import {Task, TaskCompletedCallback, TaskProcessingOutcome, TaskQueue} from './a * These functions can be composed using the `composeTaskCompletedCallbacks()` * to create a `TaskCompletedCallback` function that can be passed to an `Executor`. */ -export type TaskCompletedHandler = (task: Task, message: string | null) => void; +export type TaskCompletedHandler = (task: Task, message: string|null) => void; /** * Compose a group of TaskCompletedHandlers into a single TaskCompletedCallback. @@ -30,11 +31,11 @@ export type TaskCompletedHandler = (task: Task, message: string | null) => void; */ export function composeTaskCompletedCallbacks( callbacks: Record<TaskProcessingOutcome, TaskCompletedHandler>): TaskCompletedCallback { - return (task: Task, outcome: TaskProcessingOutcome, message: string | null): void => { + return (task: Task, outcome: TaskProcessingOutcome, message: string|null): void => { const callback = callbacks[outcome]; if (callback === undefined) { - throw new Error( - `Unknown task outcome: "${outcome}" - supported outcomes: ${JSON.stringify(Object.keys(callbacks))}`); + throw new Error(`Unknown task outcome: "${outcome}" - supported outcomes: ${ + JSON.stringify(Object.keys(callbacks))}`); } callback(task, message); }; @@ -64,10 +65,11 @@ export function createMarkAsProcessedHandler(pkgJsonUpdater: PackageJsonUpdater) * Create a handler that will throw an error. */ export function createThrowErrorHandler(fs: FileSystem): TaskCompletedHandler { - return (task: Task, message: string | null): void => { + return (task: Task, message: string|null): void => { const format = getEntryPointFormat(fs, task.entryPoint, task.formatProperty); throw new Error( - `Failed to compile entry-point ${task.entryPoint.name} (${task.formatProperty} as ${format})` + + `Failed to compile entry-point ${task.entryPoint.name} (${task.formatProperty} as ${ + format})` + (message !== null ? ` due to ${message}` : '')); }; } @@ -77,11 +79,12 @@ export function createThrowErrorHandler(fs: FileSystem): TaskCompletedHandler { */ export function createLogErrorHandler( logger: Logger, fs: FileSystem, taskQueue: TaskQueue): TaskCompletedHandler { - return (task: Task, message: string | null): void => { + return (task: Task, message: string|null): void => { taskQueue.markAsFailed(task); const format = getEntryPointFormat(fs, task.entryPoint, task.formatProperty); logger.error( - `Failed to compile entry-point ${task.entryPoint.name} (${task.formatProperty} as ${format})` + + `Failed to compile entry-point ${task.entryPoint.name} (${task.formatProperty} as ${ + format})` + (message !== null ? ` due to ${message}` : '')); }; } diff --git a/packages/compiler-cli/ngcc/src/execution/tasks/queues/base_task_queue.ts b/packages/compiler-cli/ngcc/src/execution/tasks/queues/base_task_queue.ts index de1497fb66d42..cd709f1a1cd72 100644 --- a/packages/compiler-cli/ngcc/src/execution/tasks/queues/base_task_queue.ts +++ b/packages/compiler-cli/ngcc/src/execution/tasks/queues/base_task_queue.ts @@ -38,9 +38,9 @@ export abstract class BaseTaskQueue implements TaskQueue { } // We are skipping this task so mark it as complete this.markTaskCompleted(nextTask); - const failedTask = this.tasksToSkip.get(nextTask) !; - this.logger.warn( - `Skipping processing of ${nextTask.entryPoint.name} because its dependency ${failedTask.entryPoint.name} failed to compile.`); + const failedTask = this.tasksToSkip.get(nextTask)!; + this.logger.warn(`Skipping processing of ${nextTask.entryPoint.name} because its dependency ${ + failedTask.entryPoint.name} failed to compile.`); nextTask = this.computeNextTask(); } return nextTask; @@ -48,7 +48,7 @@ export abstract class BaseTaskQueue implements TaskQueue { markAsFailed(task: Task) { if (this.dependencies.has(task)) { - for (const dependentTask of this.dependencies.get(task) !) { + for (const dependentTask of this.dependencies.get(task)!) { this.skipDependentTasks(dependentTask, task); } } @@ -81,7 +81,7 @@ export abstract class BaseTaskQueue implements TaskQueue { protected skipDependentTasks(task: Task, failedTask: Task) { this.tasksToSkip.set(task, failedTask); if (this.dependencies.has(task)) { - for (const dependentTask of this.dependencies.get(task) !) { + for (const dependentTask of this.dependencies.get(task)!) { this.skipDependentTasks(dependentTask, failedTask); } } diff --git a/packages/compiler-cli/ngcc/src/execution/tasks/queues/parallel_task_queue.ts b/packages/compiler-cli/ngcc/src/execution/tasks/queues/parallel_task_queue.ts index b7f7ef9fc241a..2718a0ee05133 100644 --- a/packages/compiler-cli/ngcc/src/execution/tasks/queues/parallel_task_queue.ts +++ b/packages/compiler-cli/ngcc/src/execution/tasks/queues/parallel_task_queue.ts @@ -49,9 +49,9 @@ export class ParallelTaskQueue extends BaseTaskQueue { } // Unblock the tasks that are dependent upon `task` - for (const dependentTask of this.dependencies.get(task) !) { + for (const dependentTask of this.dependencies.get(task)!) { if (this.blockedTasks.has(dependentTask)) { - const blockingTasks = this.blockedTasks.get(dependentTask) !; + const blockingTasks = this.blockedTasks.get(dependentTask)!; // Remove the completed task from the lists of tasks blocking other tasks. blockingTasks.delete(task); if (blockingTasks.size === 0) { diff --git a/packages/compiler-cli/ngcc/src/execution/tasks/utils.ts b/packages/compiler-cli/ngcc/src/execution/tasks/utils.ts index 26682a82acbcf..22846a361e688 100644 --- a/packages/compiler-cli/ngcc/src/execution/tasks/utils.ts +++ b/packages/compiler-cli/ngcc/src/execution/tasks/utils.ts @@ -10,8 +10,8 @@ import {EntryPoint} from '../../packages/entry_point'; import {PartiallyOrderedTasks, Task, TaskDependencies} from './api'; /** Stringify a task for debugging purposes. */ -export const stringifyTask = (task: Task): string => - `{entryPoint: ${task.entryPoint.name}, formatProperty: ${task.formatProperty}, processDts: ${task.processDts}}`; +export const stringifyTask = (task: Task): string => `{entryPoint: ${ + task.entryPoint.name}, formatProperty: ${task.formatProperty}, processDts: ${task.processDts}}`; /** * Compute a mapping of tasks to the tasks that are dependent on them (if any). @@ -45,7 +45,7 @@ export function computeTaskDependencies( // Find the earlier tasks (`candidateDependencies`) that this task depends upon. const deps = graph.dependenciesOf(entryPointPath); const taskDependencies = deps.filter(dep => candidateDependencies.has(dep)) - .map(dep => candidateDependencies.get(dep) !); + .map(dep => candidateDependencies.get(dep)!); // If this task has dependencies, add it to the dependencies and dependents maps. if (taskDependencies.length > 0) { @@ -61,7 +61,7 @@ export function computeTaskDependencies( // dependency of other tasks), so the following should theoretically never happen, but check // just in case. if (candidateDependencies.has(entryPointPath)) { - const otherTask = candidateDependencies.get(entryPointPath) !; + const otherTask = candidateDependencies.get(entryPointPath)!; throw new Error( 'Invariant violated: Multiple tasks are assigned generating typings for ' + `'${entryPointPath}':\n - ${stringifyTask(otherTask)}\n - ${stringifyTask(task)}`); @@ -73,7 +73,7 @@ export function computeTaskDependencies( // This task is not generating typings so we need to add it to the dependents of the task that // does generate typings, if that exists if (candidateDependencies.has(entryPointPath)) { - const typingsTask = candidateDependencies.get(entryPointPath) !; + const typingsTask = candidateDependencies.get(entryPointPath)!; const typingsTaskDependents = getDependentsSet(dependencies, typingsTask); typingsTaskDependents.add(task); } @@ -87,7 +87,7 @@ export function getDependentsSet(map: TaskDependencies, task: Task): Set<Task> { if (!map.has(task)) { map.set(task, new Set()); } - return map.get(task) !; + return map.get(task)!; } /** @@ -125,13 +125,13 @@ export function sortTasksByPriority( tasks: PartiallyOrderedTasks, dependencies: TaskDependencies): PartiallyOrderedTasks { const priorityPerTask = new Map<Task, [number, number]>(); const computePriority = (task: Task, idx: number): - [number, number] => [dependencies.has(task) ? dependencies.get(task) !.size : 0, idx]; + [number, number] => [dependencies.has(task) ? dependencies.get(task)!.size : 0, idx]; tasks.forEach((task, i) => priorityPerTask.set(task, computePriority(task, i))); return tasks.slice().sort((task1, task2) => { - const [p1, idx1] = priorityPerTask.get(task1) !; - const [p2, idx2] = priorityPerTask.get(task2) !; + const [p1, idx1] = priorityPerTask.get(task1)!; + const [p2, idx2] = priorityPerTask.get(task2)!; return (p2 - p1) || (idx1 - idx2); }); diff --git a/packages/compiler-cli/ngcc/src/host/commonjs_host.ts b/packages/compiler-cli/ngcc/src/host/commonjs_host.ts index 00b4bf4f5bccc..a2ba25cfbf059 100644 --- a/packages/compiler-cli/ngcc/src/host/commonjs_host.ts +++ b/packages/compiler-cli/ngcc/src/host/commonjs_host.ts @@ -7,13 +7,14 @@ */ import * as ts from 'typescript'; + import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {Declaration, Import} from '../../../src/ngtsc/reflection'; import {Logger} from '../logging/logger'; import {BundleProgram} from '../packages/bundle_program'; import {FactoryMap, getTsHelperFnFromIdentifier, isDefined, stripExtension} from '../utils'; -import {ExportDeclaration, ExportStatement, ReexportStatement, RequireCall, findNamespaceOfIdentifier, findRequireCallReference, isExportStatement, isReexportStatement, isRequireCall} from './commonjs_umd_utils'; +import {ExportDeclaration, ExportStatement, findNamespaceOfIdentifier, findRequireCallReference, isExportStatement, isReexportStatement, isRequireCall, ReexportStatement, RequireCall} from './commonjs_umd_utils'; import {Esm5ReflectionHost} from './esm5_host'; import {NgccClassSymbol} from './ngcc_host'; diff --git a/packages/compiler-cli/ngcc/src/host/commonjs_umd_utils.ts b/packages/compiler-cli/ngcc/src/host/commonjs_umd_utils.ts index 86ddc9550354e..e871034f99aed 100644 --- a/packages/compiler-cli/ngcc/src/host/commonjs_umd_utils.ts +++ b/packages/compiler-cli/ngcc/src/host/commonjs_umd_utils.ts @@ -16,7 +16,12 @@ export interface ExportDeclaration { } export interface ExportStatement extends ts.ExpressionStatement { - expression: ts.BinaryExpression&{left: ts.PropertyAccessExpression & {expression: ts.Identifier}}; + expression: ts.BinaryExpression&{ + left: ts.PropertyAccessExpression & + { + expression: ts.Identifier + } + }; } /** @@ -34,7 +39,9 @@ export interface ExportStatement extends ts.ExpressionStatement { * expression and can be either a `require('...')` call or an identifier (initialized via a * `require('...')` call). */ -export interface ReexportStatement extends ts.ExpressionStatement { expression: ts.CallExpression; } +export interface ReexportStatement extends ts.ExpressionStatement { + expression: ts.CallExpression; +} export interface RequireCall extends ts.CallExpression { arguments: ts.CallExpression['arguments']&[ts.StringLiteral]; diff --git a/packages/compiler-cli/ngcc/src/host/delegating_host.ts b/packages/compiler-cli/ngcc/src/host/delegating_host.ts index 3bef3e432dec0..d4d36fc96f38f 100644 --- a/packages/compiler-cli/ngcc/src/host/delegating_host.ts +++ b/packages/compiler-cli/ngcc/src/host/delegating_host.ts @@ -32,7 +32,7 @@ export class DelegatingReflectionHost implements NgccReflectionHost { getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null { if (isFromDtsFile(id)) { - return this.tsHost.getDeclarationOfIdentifier(id); + return this.detectKnownDeclaration(this.tsHost.getDeclarationOfIdentifier(id)); } return this.ngccHost.getDeclarationOfIdentifier(id); } @@ -60,7 +60,13 @@ export class DelegatingReflectionHost implements NgccReflectionHost { getExportsOfModule(module: ts.Node): Map<string, Declaration>|null { if (isFromDtsFile(module)) { - return this.tsHost.getExportsOfModule(module); + const exportMap = this.tsHost.getExportsOfModule(module); + + if (exportMap !== null) { + exportMap.forEach(decl => this.detectKnownDeclaration(decl)); + } + + return exportMap; } return this.ngccHost.getExportsOfModule(module); } @@ -154,4 +160,11 @@ export class DelegatingReflectionHost implements NgccReflectionHost { getEndOfClass(classSymbol: NgccClassSymbol): ts.Node { return this.ngccHost.getEndOfClass(classSymbol); } + + detectKnownDeclaration(decl: null): null; + detectKnownDeclaration<T extends Declaration>(decl: T): T; + detectKnownDeclaration<T extends Declaration>(decl: T|null): T|null; + detectKnownDeclaration<T extends Declaration>(decl: T|null): T|null { + return this.ngccHost.detectKnownDeclaration(decl); + } } diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index d7b873752b618..23fdfeacb0952 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -8,13 +8,13 @@ import * as ts from 'typescript'; -import {ClassDeclaration, ClassMember, ClassMemberKind, ConcreteDeclaration, CtorParameter, Declaration, Decorator, KnownDeclaration, TypeScriptReflectionHost, TypeValueReference, isDecoratorIdentifier, reflectObjectLiteral,} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, ClassMember, ClassMemberKind, ConcreteDeclaration, CtorParameter, Declaration, Decorator, isDecoratorIdentifier, KnownDeclaration, reflectObjectLiteral, TypeScriptReflectionHost, TypeValueReference,} from '../../../src/ngtsc/reflection'; import {isWithinPackage} from '../analysis/util'; import {Logger} from '../logging/logger'; import {BundleProgram} from '../packages/bundle_program'; import {findAll, getNameText, hasNameIdentifier, isDefined, stripDollarSuffix} from '../utils'; -import {ClassSymbol, ModuleWithProvidersFunction, NgccClassSymbol, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host'; +import {ClassSymbol, isSwitchableVariableDeclaration, ModuleWithProvidersFunction, NgccClassSymbol, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration} from './ngcc_host'; export const DECORATORS = 'decorators' as ts.__String; export const PROP_DECORATORS = 'propDecorators' as ts.__String; @@ -346,24 +346,14 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N // The identifier may have been of an additional class assignment such as `MyClass_1` that was // present as alias for `MyClass`. If so, resolve such aliases to their original declaration. - if (superDeclaration !== null && superDeclaration.node !== null) { + if (superDeclaration !== null && superDeclaration.node !== null && + superDeclaration.known === null) { const aliasedIdentifier = this.resolveAliasedClassIdentifier(superDeclaration.node); if (aliasedIdentifier !== null) { return this.getDeclarationOfIdentifier(aliasedIdentifier); } } - // If the identifier resolves to the global JavaScript `Object`, return a - // declaration that denotes it as the known `JsGlobalObject` declaration. - if (superDeclaration !== null && this.isJavaScriptObjectDeclaration(superDeclaration)) { - return { - known: KnownDeclaration.JsGlobalObject, - expression: id, - viaModule: null, - node: null, - }; - } - return superDeclaration; } @@ -511,8 +501,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N return null; } if (!isNamedDeclaration(declaration)) { - throw new Error( - `Cannot get the dts file for a declaration that has no name: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`); + throw new Error(`Cannot get the dts file for a declaration that has no name: ${ + declaration.getText()} in ${declaration.getSourceFile().fileName}`); } // Try to retrieve the dts declaration from the public map @@ -520,7 +510,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N this.publicDtsDeclarationMap = this.computePublicDtsDeclarationMap(this.src, this.dts); } if (this.publicDtsDeclarationMap.has(declaration)) { - return this.publicDtsDeclarationMap.get(declaration) !; + return this.publicDtsDeclarationMap.get(declaration)!; } // No public export, try the private map @@ -528,7 +518,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N this.privateDtsDeclarationMap = this.computePrivateDtsDeclarationMap(this.src, this.dts); } if (this.privateDtsDeclarationMap.has(declaration)) { - return this.privateDtsDeclarationMap.get(declaration) !; + return this.privateDtsDeclarationMap.get(declaration)!; } // No declaration found at all @@ -602,8 +592,38 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N return last; } + /** + * Check whether a `Declaration` corresponds with a known declaration, such as `Object`, and set + * its `known` property to the appropriate `KnownDeclaration`. + * + * @param decl The `Declaration` to check. + * @return The passed in `Declaration` (potentially enhanced with a `KnownDeclaration`). + */ + detectKnownDeclaration(decl: null): null; + detectKnownDeclaration<T extends Declaration>(decl: T): T; + detectKnownDeclaration<T extends Declaration>(decl: T|null): T|null; + detectKnownDeclaration<T extends Declaration>(decl: T|null): T|null { + if (decl !== null && decl.known === null && this.isJavaScriptObjectDeclaration(decl)) { + // If the identifier resolves to the global JavaScript `Object`, update the declaration to + // denote it as the known `JsGlobalObject` declaration. + decl.known = KnownDeclaration.JsGlobalObject; + } + + return decl; + } + + ///////////// Protected Helpers ///////////// + /** + * Resolve a `ts.Symbol` to its declaration and detect whether it corresponds with a known + * declaration. + */ + protected getDeclarationOfSymbol(symbol: ts.Symbol, originalId: ts.Identifier|null): Declaration + |null { + return this.detectKnownDeclaration(super.getDeclarationOfSymbol(symbol, originalId)); + } + /** * Finds the identifier of the actual class declaration for a potentially aliased declaration of a * class. @@ -618,7 +638,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N protected resolveAliasedClassIdentifier(declaration: ts.Declaration): ts.Identifier|null { this.ensurePreprocessed(declaration.getSourceFile()); return this.aliasedClassDeclarations.has(declaration) ? - this.aliasedClassDeclarations.get(declaration) ! : + this.aliasedClassDeclarations.get(declaration)! : null; } @@ -674,7 +694,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N this.aliasedClassDeclarations.set(aliasedDeclaration.node, declaration.name); } - /** Get the top level statements for a module. + /** + * Get the top level statements for a module. * * In ES5 and ES2015 this is just the top level statements of the file. * @param sourceFile The module whose statements we want. @@ -728,7 +749,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N protected acquireDecoratorInfo(classSymbol: NgccClassSymbol): DecoratorInfo { const decl = classSymbol.declaration.valueDeclaration; if (this.decoratorCache.has(decl)) { - return this.decoratorCache.get(decl) !; + return this.decoratorCache.get(decl)!; } // Extract decorators from static properties and `__decorate` helper calls, then merge them @@ -757,7 +778,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * none of the static properties exist. */ protected computeDecoratorInfoFromStaticProperties(classSymbol: NgccClassSymbol): { - classDecorators: Decorator[] | null; memberDecorators: Map<string, Decorator[]>| null; + classDecorators: Decorator[]|null; memberDecorators: Map<string, Decorator[]>| null; constructorParamInfo: ParamInfo[] | null; } { let classDecorators: Decorator[]|null = null; @@ -1026,7 +1047,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N // The helper arg was reflected to represent an actual decorator. if (this.isFromCore(entry.decorator)) { const decorators = - memberDecorators.has(memberName) ? memberDecorators.get(memberName) ! : []; + memberDecorators.has(memberName) ? memberDecorators.get(memberName)! : []; decorators.push(entry.decorator); memberDecorators.set(memberName, decorators); } @@ -1193,7 +1214,6 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N if (ts.isArrayLiteralExpression(decoratorsArray)) { // Add each decorator that is imported from `@angular/core` into the `decorators` array decoratorsArray.elements.forEach(node => { - // If the decorator is not an object literal expression then we are not interested if (ts.isObjectLiteralExpression(node)) { // We are only interested in objects of the form: `{ type: DecoratorType, args: [...] }` @@ -1201,14 +1221,15 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N // Is the value of the `type` property an identifier? if (decorator.has('type')) { - let decoratorType = decorator.get('type') !; + let decoratorType = decorator.get('type')!; if (isDecoratorIdentifier(decoratorType)) { const decoratorIdentifier = ts.isIdentifier(decoratorType) ? decoratorType : decoratorType.name; decorators.push({ name: decoratorIdentifier.text, identifier: decoratorType, - import: this.getImportOfIdentifier(decoratorIdentifier), node, + import: this.getImportOfIdentifier(decoratorIdentifier), + node, args: getDecoratorArgs(node), }); } @@ -1347,7 +1368,13 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N const type: ts.TypeNode = (node as any).type || null; return { node, - implementation: node, kind, type, name, nameNode, value, isStatic, + implementation: node, + kind, + type, + name, + nameNode, + value, + isStatic, decorators: decorators || [] }; } @@ -1362,7 +1389,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N ts.ParameterDeclaration[]|null { const members = classSymbol.implementation.members; if (members && members.has(CONSTRUCTOR)) { - const constructorSymbol = members.get(CONSTRUCTOR) !; + const constructorSymbol = members.get(CONSTRUCTOR)!; // For some reason the constructor does not have a `valueDeclaration` ?!? const constructor = constructorSymbol.declarations && constructorSymbol.declarations[0] as ts.ConstructorDeclaration | undefined; @@ -1409,7 +1436,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N local: false, valueDeclaration: decl.node, moduleName: decl.viaModule, - name: decl.node.name.text, + importedName: decl.node.name.text, + nestedPath: null, }; } else { typeValueReference = { @@ -1424,7 +1452,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N name: getNameText(nameNode), nameNode, typeValueReference, - typeNode: null, decorators + typeNode: null, + decorators }; }); } @@ -1473,9 +1502,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N ts.isObjectLiteralExpression(element) ? reflectObjectLiteral(element) : null) .map(paramInfo => { const typeExpression = - paramInfo && paramInfo.has('type') ? paramInfo.get('type') ! : null; + paramInfo && paramInfo.has('type') ? paramInfo.get('type')! : null; const decoratorInfo = - paramInfo && paramInfo.has('decorators') ? paramInfo.get('decorators') ! : null; + paramInfo && paramInfo.has('decorators') ? paramInfo.get('decorators')! : null; const decorators = decoratorInfo && this.reflectDecorators(decoratorInfo) .filter(decorator => this.isFromCore(decorator)); @@ -1619,7 +1648,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N if (fileExports !== null) { for (const [exportName, {node: declaration}] of fileExports) { if (declaration !== null && dtsDeclarationMap.has(exportName)) { - declarationMap.set(declaration, dtsDeclarationMap.get(exportName) !); + declarationMap.set(declaration, dtsDeclarationMap.get(exportName)!); } } } @@ -1670,15 +1699,17 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N const ngModuleDeclaration = this.getDeclarationOfExpression(ngModuleValue); if (!ngModuleDeclaration || ngModuleDeclaration.node === null) { - throw new Error( - `Cannot find a declaration for NgModule ${ngModuleValue.getText()} referenced in "${declaration!.getText()}"`); + throw new Error(`Cannot find a declaration for NgModule ${ + ngModuleValue.getText()} referenced in "${declaration!.getText()}"`); } if (!hasNameIdentifier(ngModuleDeclaration.node)) { return null; } return { name, - ngModule: ngModuleDeclaration as ConcreteDeclaration<ClassDeclaration>, declaration, container + ngModule: ngModuleDeclaration as ConcreteDeclaration<ClassDeclaration>, + declaration, + container }; } @@ -1705,7 +1736,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N return null; } - const exportDecl = namespaceExports.get(expression.name.text) !; + const exportDecl = namespaceExports.get(expression.name.text)!; return {...exportDecl, viaModule: namespaceDecl.viaModule}; } @@ -1737,8 +1768,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N ///////////// Exported Helpers ///////////// export type ParamInfo = { - decorators: Decorator[] | null, - typeExpression: ts.Expression | null + decorators: Decorator[]|null, + typeExpression: ts.Expression|null }; /** @@ -1782,7 +1813,7 @@ export interface DecoratorCall { * ], SomeDirective); * ``` */ -export type DecorateHelperEntry = ParameterTypes | ParameterDecorators | DecoratorCall; +export type DecorateHelperEntry = ParameterTypes|ParameterDecorators|DecoratorCall; /** * The recorded decorator information of a single class. This information is cached in the host. @@ -1811,7 +1842,7 @@ interface DecoratorInfo { * A statement node that represents an assignment. */ export type AssignmentStatement = - ts.ExpressionStatement & {expression: {left: ts.Identifier, right: ts.Expression}}; + ts.ExpressionStatement&{expression: {left: ts.Identifier, right: ts.Expression}}; /** * Test whether a statement node is an assignment statement. diff --git a/packages/compiler-cli/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/ngcc/src/host/esm5_host.ts index 4c8bb221260fc..17fd2134c83ad 100644 --- a/packages/compiler-cli/ngcc/src/host/esm5_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm5_host.ts @@ -8,10 +8,10 @@ import * as ts from 'typescript'; -import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, Parameter, isNamedVariableDeclaration, reflectObjectLiteral} from '../../../src/ngtsc/reflection'; -import {getNameText, getTsHelperFnFromDeclaration, hasNameIdentifier} from '../utils'; +import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, isNamedVariableDeclaration, Parameter, reflectObjectLiteral} from '../../../src/ngtsc/reflection'; +import {getNameText, getTsHelperFnFromDeclaration, getTsHelperFnFromIdentifier, hasNameIdentifier} from '../utils'; -import {Esm2015ReflectionHost, ParamInfo, getPropertyValueFromSymbol, isAssignment, isAssignmentStatement} from './esm2015_host'; +import {Esm2015ReflectionHost, getPropertyValueFromSymbol, isAssignment, isAssignmentStatement, ParamInfo} from './esm2015_host'; import {NgccClassSymbol} from './ngcc_host'; @@ -81,12 +81,13 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { getInternalNameOfClass(clazz: ClassDeclaration): ts.Identifier { const innerClass = this.getInnerFunctionDeclarationFromClassDeclaration(clazz); if (innerClass === undefined) { - throw new Error( - `getInternalNameOfClass() called on a non-ES5 class: expected ${clazz.name.text} to have an inner class declaration`); + throw new Error(`getInternalNameOfClass() called on a non-ES5 class: expected ${ + clazz.name.text} to have an inner class declaration`); } if (innerClass.name === undefined) { throw new Error( - `getInternalNameOfClass() called on a class with an anonymous inner declaration: expected a name on:\n${innerClass.getText()}`); + `getInternalNameOfClass() called on a class with an anonymous inner declaration: expected a name on:\n${ + innerClass.getText()}`); } return innerClass.name; } @@ -98,14 +99,15 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { getEndOfClass(classSymbol: NgccClassSymbol): ts.Node { const iifeBody = getIifeBody(classSymbol.declaration.valueDeclaration); if (!iifeBody) { - throw new Error( - `Compiled class declaration is not inside an IIFE: ${classSymbol.name} in ${classSymbol.declaration.valueDeclaration.getSourceFile().fileName}`); + throw new Error(`Compiled class declaration is not inside an IIFE: ${classSymbol.name} in ${ + classSymbol.declaration.valueDeclaration.getSourceFile().fileName}`); } const returnStatementIndex = iifeBody.statements.findIndex(ts.isReturnStatement); if (returnStatementIndex === -1) { throw new Error( - `Compiled class wrapper IIFE does not have a return statement: ${classSymbol.name} in ${classSymbol.declaration.valueDeclaration.getSourceFile().fileName}`); + `Compiled class wrapper IIFE does not have a return statement: ${classSymbol.name} in ${ + classSymbol.declaration.valueDeclaration.getSourceFile().fileName}`); } // Return the statement before the IIFE return statement @@ -190,7 +192,24 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null { const superDeclaration = super.getDeclarationOfIdentifier(id); - if (superDeclaration === null || superDeclaration.node === null) { + if (superDeclaration === null) { + const nonEmittedNorImportedTsHelperDeclaration = getTsHelperFnFromIdentifier(id); + if (nonEmittedNorImportedTsHelperDeclaration !== null) { + // No declaration could be found for this identifier and its name matches a known TS helper + // function. This can happen if a package is compiled with `noEmitHelpers: true` and + // `importHelpers: false` (the default). This is, for example, the case with + // `@nativescript/angular@9.0.0-next-2019-11-12-155500-01`. + return { + expression: id, + known: nonEmittedNorImportedTsHelperDeclaration, + node: null, + viaModule: null, + }; + } + } + + if (superDeclaration === null || superDeclaration.node === null || + superDeclaration.known !== null) { return superDeclaration; } @@ -200,7 +219,7 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { super.getDeclarationOfIdentifier(outerClassNode.name) : superDeclaration; - if (!declaration || declaration.node === null) { + if (declaration === null || declaration.node === null || declaration.known !== null) { return declaration; } @@ -257,24 +276,29 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { return {node, body: statements || null, parameters}; } - - ///////////// Protected Helpers ///////////// /** - * Resolve a `ts.Symbol` to its declaration and detect whether it corresponds with a known - * TypeScript helper function. + * Check whether a `Declaration` corresponds with a known declaration, such as a TypeScript helper + * function, and set its `known` property to the appropriate `KnownDeclaration`. + * + * @param decl The `Declaration` to check. + * @return The passed in `Declaration` (potentially enhanced with a `KnownDeclaration`). */ - protected getDeclarationOfSymbol(symbol: ts.Symbol, originalId: ts.Identifier|null): Declaration - |null { - const superDeclaration = super.getDeclarationOfSymbol(symbol, originalId); - - if (superDeclaration !== null && superDeclaration.node !== null && - superDeclaration.known === null) { - superDeclaration.known = getTsHelperFnFromDeclaration(superDeclaration.node); + detectKnownDeclaration(decl: null): null; + detectKnownDeclaration<T extends Declaration>(decl: T): T; + detectKnownDeclaration<T extends Declaration>(decl: T|null): T|null; + detectKnownDeclaration<T extends Declaration>(decl: T|null): T|null { + decl = super.detectKnownDeclaration(decl); + + if (decl !== null && decl.known === null && decl.node !== null) { + decl.known = getTsHelperFnFromDeclaration(decl.node); } - return superDeclaration; + return decl; } + + ///////////// Protected Helpers ///////////// + /** * Get the inner function declaration of an ES5-style class. * @@ -368,9 +392,9 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { if (expression && ts.isArrayLiteralExpression(expression)) { const elements = expression.elements; return elements.map(reflectArrayElement).map(paramInfo => { - const typeExpression = paramInfo && paramInfo.has('type') ? paramInfo.get('type') ! : null; + const typeExpression = paramInfo && paramInfo.has('type') ? paramInfo.get('type')! : null; const decoratorInfo = - paramInfo && paramInfo.has('decorators') ? paramInfo.get('decorators') ! : null; + paramInfo && paramInfo.has('decorators') ? paramInfo.get('decorators')! : null; const decorators = decoratorInfo && this.reflectDecorators(decoratorInfo); return {typeExpression, decorators}; }); @@ -634,7 +658,7 @@ function getReturnIdentifier(body: ts.Block): ts.Identifier|undefined { return undefined; } -function getReturnStatement(declaration: ts.Expression | undefined): ts.ReturnStatement|undefined { +function getReturnStatement(declaration: ts.Expression|undefined): ts.ReturnStatement|undefined { return declaration && ts.isFunctionExpression(declaration) ? declaration.body.statements.find(ts.isReturnStatement) : undefined; diff --git a/packages/compiler-cli/ngcc/src/host/ngcc_host.ts b/packages/compiler-cli/ngcc/src/host/ngcc_host.ts index e1f3c07fded3f..eb16f662ea888 100644 --- a/packages/compiler-cli/ngcc/src/host/ngcc_host.ts +++ b/packages/compiler-cli/ngcc/src/host/ngcc_host.ts @@ -7,12 +7,12 @@ */ import * as ts from 'typescript'; -import {ClassDeclaration, ConcreteDeclaration, Decorator, ReflectionHost} from '../../../src/ngtsc/reflection'; +import {ClassDeclaration, ConcreteDeclaration, Declaration, Decorator, ReflectionHost} from '../../../src/ngtsc/reflection'; export const PRE_R3_MARKER = '__PRE_R3__'; export const POST_R3_MARKER = '__POST_R3__'; -export type SwitchableVariableDeclaration = ts.VariableDeclaration & {initializer: ts.Identifier}; +export type SwitchableVariableDeclaration = ts.VariableDeclaration&{initializer: ts.Identifier}; export function isSwitchableVariableDeclaration(node: ts.Node): node is SwitchableVariableDeclaration { return ts.isVariableDeclaration(node) && !!node.initializer && @@ -47,7 +47,7 @@ export interface ModuleWithProvidersFunction { * The symbol corresponding to a "class" declaration. I.e. a `ts.Symbol` whose `valueDeclaration` is * a `ClassDeclaration`. */ -export type ClassSymbol = ts.Symbol & {valueDeclaration: ClassDeclaration}; +export type ClassSymbol = ts.Symbol&{valueDeclaration: ClassDeclaration}; /** * A representation of a class that accounts for the potential existence of two `ClassSymbol`s for a @@ -128,4 +128,13 @@ export interface NgccReflectionHost extends ReflectionHost { * @param classSymbol The class whose statements we want. */ getEndOfClass(classSymbol: NgccClassSymbol): ts.Node; + + /** + * Check whether a `Declaration` corresponds with a known declaration and set its `known` property + * to the appropriate `KnownDeclaration`. + * + * @param decl The `Declaration` to check or `null` if there is no declaration. + * @return The passed in `Declaration` (potentially enhanced with a `KnownDeclaration`). + */ + detectKnownDeclaration<T extends Declaration>(decl: T|null): T|null; } diff --git a/packages/compiler-cli/ngcc/src/host/umd_host.ts b/packages/compiler-cli/ngcc/src/host/umd_host.ts index b053820c77621..a9ef6bb56547e 100644 --- a/packages/compiler-cli/ngcc/src/host/umd_host.ts +++ b/packages/compiler-cli/ngcc/src/host/umd_host.ts @@ -13,7 +13,8 @@ import {Declaration, Import} from '../../../src/ngtsc/reflection'; import {Logger} from '../logging/logger'; import {BundleProgram} from '../packages/bundle_program'; import {FactoryMap, getTsHelperFnFromIdentifier, stripExtension} from '../utils'; -import {ExportDeclaration, ExportStatement, ReexportStatement, findNamespaceOfIdentifier, findRequireCallReference, isExportStatement, isReexportStatement, isRequireCall} from './commonjs_umd_utils'; + +import {ExportDeclaration, ExportStatement, findNamespaceOfIdentifier, findRequireCallReference, isExportStatement, isReexportStatement, isRequireCall, ReexportStatement} from './commonjs_umd_utils'; import {Esm5ReflectionHost, stripParentheses} from './esm5_host'; export class UmdReflectionHost extends Esm5ReflectionHost { @@ -61,7 +62,8 @@ export class UmdReflectionHost extends Esm5ReflectionHost { return this.umdImportPaths.get(importParameter); } - /** Get the top level statements for a module. + /** + * Get the top level statements for a module. * * In UMD modules these are the body of the UMD factory function. * diff --git a/packages/compiler-cli/ngcc/src/locking/async_locker.ts b/packages/compiler-cli/ngcc/src/locking/async_locker.ts index 56bc531357dcd..f0ee1b7393d46 100644 --- a/packages/compiler-cli/ngcc/src/locking/async_locker.ts +++ b/packages/compiler-cli/ngcc/src/locking/async_locker.ts @@ -52,7 +52,7 @@ export class AsyncLocker { if (attempts === 0) { this.logger.info( `Another process, with id ${pid}, is currently running ngcc.\n` + - `Waiting up to ${this.retryDelay*this.retryAttempts/1000}s for it to finish.`); + `Waiting up to ${this.retryDelay * this.retryAttempts / 1000}s for it to finish.`); } // The file is still locked by another process so wait for a bit and retry await new Promise(resolve => setTimeout(resolve, this.retryDelay)); @@ -60,7 +60,10 @@ export class AsyncLocker { } // If we fall out of the loop then we ran out of rety attempts throw new Error( - `Timed out waiting ${this.retryAttempts * this.retryDelay/1000}s for another ngcc process, with id ${pid}, to complete.\n` + - `(If you are sure no ngcc process is running then you should delete the lock-file at ${this.lockFile.path}.)`); + `Timed out waiting ${ + this.retryAttempts * this.retryDelay / + 1000}s for another ngcc process, with id ${pid}, to complete.\n` + + `(If you are sure no ngcc process is running then you should delete the lock-file at ${ + this.lockFile.path}.)`); } } diff --git a/packages/compiler-cli/ngcc/src/locking/lock_file_with_child_process/index.ts b/packages/compiler-cli/ngcc/src/locking/lock_file_with_child_process/index.ts index 8cde3e462b547..ba2aaf487af4a 100644 --- a/packages/compiler-cli/ngcc/src/locking/lock_file_with_child_process/index.ts +++ b/packages/compiler-cli/ngcc/src/locking/lock_file_with_child_process/index.ts @@ -7,9 +7,9 @@ */ import {ChildProcess, fork} from 'child_process'; -import {AbsoluteFsPath, CachedFileSystem, FileSystem} from '../../../../src/ngtsc/file_system'; -import {LogLevel, Logger} from '../../logging/logger'; -import {LockFile, getLockFilePath} from '../lock_file'; +import {AbsoluteFsPath, FileSystem} from '../../../../src/ngtsc/file_system'; +import {Logger, LogLevel} from '../../logging/logger'; +import {getLockFilePath, LockFile} from '../lock_file'; import {removeLockFile} from './util'; @@ -57,11 +57,6 @@ export class LockFileWithChildProcess implements LockFile { read(): string { try { - if (this.fs instanceof CachedFileSystem) { - // The lock-file file is "volatile", it might be changed by an external process, - // so we must not rely upon the cached value when reading it. - this.fs.invalidateCaches(this.path); - } return this.fs.readFile(this.path); } catch { return '{unknown}'; @@ -81,6 +76,14 @@ export class LockFileWithChildProcess implements LockFile { this.logger.debug('Forking unlocker child-process'); const logLevel = this.logger.level !== undefined ? this.logger.level.toString() : LogLevel.info.toString(); - return fork(this.fs.resolve(__dirname, './unlocker.js'), [path, logLevel], {detached: true}); + const isWindows = process.platform === 'win32'; + const unlocker = fork( + this.fs.resolve(__dirname, './unlocker.js'), [path, logLevel], + {detached: true, stdio: isWindows ? 'pipe' : 'inherit'}); + if (isWindows) { + unlocker.stdout?.on('data', process.stdout.write.bind(process.stdout)); + unlocker.stderr?.on('data', process.stderr.write.bind(process.stderr)); + } + return unlocker; } } diff --git a/packages/compiler-cli/ngcc/src/locking/lock_file_with_child_process/unlocker.ts b/packages/compiler-cli/ngcc/src/locking/lock_file_with_child_process/unlocker.ts index 798613541a725..b31f21f966625 100644 --- a/packages/compiler-cli/ngcc/src/locking/lock_file_with_child_process/unlocker.ts +++ b/packages/compiler-cli/ngcc/src/locking/lock_file_with_child_process/unlocker.ts @@ -20,14 +20,14 @@ const fs = new NodeJSFileSystem(); // We create a logger that has the same logging level as the parent process, since it should have // been passed through as one of the args -const logLevel = parseInt(process.argv.pop() !, 10); +const logLevel = parseInt(process.argv.pop()!, 10); const logger = new ConsoleLogger(logLevel); // We must store the parent PID now as it changes if the parent process is killed early const ppid = process.ppid.toString(); // The path to the lock-file to remove should have been passed as one of the args -const lockFilePath = fs.resolve(process.argv.pop() !); +const lockFilePath = fs.resolve(process.argv.pop()!); logger.debug(`Starting unlocker at process ${process.pid} on behalf of process ${ppid}`); logger.debug(`The lock-file path is ${lockFilePath}`); @@ -36,4 +36,6 @@ logger.debug(`The lock-file path is ${lockFilePath}`); * When the parent process exits (for whatever reason) remove the loc-file if it exists and as long * as it was one that was created by the parent process. */ -process.on('disconnect', () => { removeLockFile(fs, logger, lockFilePath, ppid); }); +process.on('disconnect', () => { + removeLockFile(fs, logger, lockFilePath, ppid); +}); diff --git a/packages/compiler-cli/ngcc/src/locking/sync_locker.ts b/packages/compiler-cli/ngcc/src/locking/sync_locker.ts index 391ca822e3663..6c58cbe41da98 100644 --- a/packages/compiler-cli/ngcc/src/locking/sync_locker.ts +++ b/packages/compiler-cli/ngcc/src/locking/sync_locker.ts @@ -57,6 +57,7 @@ export class SyncLocker { `ngcc is already running at process with id ${pid}.\n` + `If you are running multiple builds in parallel then you should pre-process your node_modules via the command line ngcc tool before starting the builds;\n` + `See https://v9.angular.io/guide/ivy#speeding-up-ngcc-compilation.\n` + - `(If you are sure no ngcc process is running then you should delete the lock-file at ${this.lockFile.path}.)`); + `(If you are sure no ngcc process is running then you should delete the lock-file at ${ + this.lockFile.path}.)`); } } diff --git a/packages/compiler-cli/ngcc/src/logging/console_logger.ts b/packages/compiler-cli/ngcc/src/logging/console_logger.ts index 040bce95134af..66acf79b8a9a6 100644 --- a/packages/compiler-cli/ngcc/src/logging/console_logger.ts +++ b/packages/compiler-cli/ngcc/src/logging/console_logger.ts @@ -5,7 +5,7 @@ * 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 {LogLevel, Logger} from './logger'; +import {Logger, LogLevel} from './logger'; const RESET = '\x1b[0m'; const RED = '\x1b[31m'; diff --git a/packages/compiler-cli/ngcc/src/main.ts b/packages/compiler-cli/ngcc/src/main.ts index c22839a5395a0..02ddbf7ab27c7 100644 --- a/packages/compiler-cli/ngcc/src/main.ts +++ b/packages/compiler-cli/ngcc/src/main.ts @@ -8,15 +8,12 @@ /// <reference types="node" /> -import {DepGraph} from 'dependency-graph'; import * as os from 'os'; -import * as ts from 'typescript'; -import {replaceTsWithNgInErrors} from '../../src/ngtsc/diagnostics'; -import {AbsoluteFsPath, FileSystem, absoluteFrom, dirname, getFileSystem, resolve} from '../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, resolve} from '../../src/ngtsc/file_system'; import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host'; -import {DependencyResolver, InvalidEntryPoint} from './dependencies/dependency_resolver'; +import {DependencyResolver} from './dependencies/dependency_resolver'; import {DtsDependencyHost} from './dependencies/dts_dependency_host'; import {EsmDependencyHost} from './dependencies/esm_dependency_host'; import {ModuleResolver} from './dependencies/module_resolver'; @@ -24,137 +21,23 @@ import {UmdDependencyHost} from './dependencies/umd_dependency_host'; import {DirectoryWalkerEntryPointFinder} from './entry_point_finder/directory_walker_entry_point_finder'; import {EntryPointFinder} from './entry_point_finder/interface'; import {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder'; -import {AnalyzeEntryPointsFn, CreateCompileFn, Executor} from './execution/api'; +import {getAnalyzeEntryPointsFn} from './execution/analyze_entry_points'; +import {Executor} from './execution/api'; import {ClusterExecutor} from './execution/cluster/executor'; -import {ClusterPackageJsonUpdater} from './execution/cluster/package_json_updater'; +import {getCreateCompileFn} from './execution/create_compile_function'; import {SingleProcessExecutorAsync, SingleProcessExecutorSync} from './execution/single_process_executor'; -import {CreateTaskCompletedCallback, PartiallyOrderedTasks, Task, TaskProcessingOutcome, TaskQueue} from './execution/tasks/api'; +import {CreateTaskCompletedCallback, TaskProcessingOutcome} from './execution/tasks/api'; import {composeTaskCompletedCallbacks, createLogErrorHandler, createMarkAsProcessedHandler, createThrowErrorHandler} from './execution/tasks/completion'; -import {ParallelTaskQueue} from './execution/tasks/queues/parallel_task_queue'; -import {SerialTaskQueue} from './execution/tasks/queues/serial_task_queue'; -import {computeTaskDependencies} from './execution/tasks/utils'; import {AsyncLocker} from './locking/async_locker'; import {LockFileWithChildProcess} from './locking/lock_file_with_child_process'; import {SyncLocker} from './locking/sync_locker'; -import {ConsoleLogger} from './logging/console_logger'; -import {LogLevel, Logger} from './logging/logger'; -import {hasBeenProcessed} from './packages/build_marker'; +import {Logger} from './logging/logger'; +import {AsyncNgccOptions, getSharedSetup, NgccOptions, PathMappings, SyncNgccOptions} from './ngcc_options'; import {NgccConfiguration} from './packages/configuration'; -import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point'; -import {makeEntryPointBundle} from './packages/entry_point_bundle'; +import {EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES} from './packages/entry_point'; import {EntryPointManifest, InvalidatingEntryPointManifest} from './packages/entry_point_manifest'; -import {Transformer} from './packages/transformer'; -import {PathMappings} from './utils'; -import {cleanOutdatedPackages} from './writing/cleaning/package_cleaner'; -import {FileWriter} from './writing/file_writer'; -import {InPlaceFileWriter} from './writing/in_place_file_writer'; -import {NewEntryPointFileWriter} from './writing/new_entry_point_file_writer'; import {DirectPackageJsonUpdater, PackageJsonUpdater} from './writing/package_json_updater'; -/** - * The options to configure the ngcc compiler for synchronous execution. - */ -export interface SyncNgccOptions { - /** The absolute path to the `node_modules` folder that contains the packages to process. */ - basePath: string; - - /** - * The path to the primary package to be processed. If not absolute then it must be relative to - * `basePath`. - * - * All its dependencies will need to be processed too. - * - * If this property is provided then `errorOnFailedEntryPoint` is forced to true. - */ - targetEntryPointPath?: string; - - /** - * Which entry-point properties in the package.json to consider when processing an entry-point. - * Each property should hold a path to the particular bundle format for the entry-point. - * Defaults to all the properties in the package.json. - */ - propertiesToConsider?: string[]; - - /** - * Whether to process all formats specified by (`propertiesToConsider`) or to stop processing - * this entry-point at the first matching format. Defaults to `true`. - */ - compileAllFormats?: boolean; - - /** - * Whether to create new entry-points bundles rather than overwriting the original files. - */ - createNewEntryPointFormats?: boolean; - - /** - * Provide a logger that will be called with log messages. - */ - logger?: Logger; - - /** - * Paths mapping configuration (`paths` and `baseUrl`), as found in `ts.CompilerOptions`. - * These are used to resolve paths to locally built Angular libraries. - */ - pathMappings?: PathMappings; - - /** - * Provide a file-system service that will be used by ngcc for all file interactions. - */ - fileSystem?: FileSystem; - - /** - * Whether the compilation should run and return asynchronously. Allowing asynchronous execution - * may speed up the compilation by utilizing multiple CPU cores (if available). - * - * Default: `false` (i.e. run synchronously) - */ - async?: false; - - /** - * Set to true in order to terminate immediately with an error code if an entry-point fails to be - * processed. - * - * If `targetEntryPointPath` is provided then this property is always true and cannot be - * changed. Otherwise the default is false. - * - * When set to false, ngcc will continue to process entry-points after a failure. In which case it - * will log an error and resume processing other entry-points. - */ - errorOnFailedEntryPoint?: boolean; - - /** - * Render `$localize` messages with legacy format ids. - * - * The default value is `true`. Only set this to `false` if you do not want legacy message ids to - * be rendered. For example, if you are not using legacy message ids in your translation files - * AND are not doing compile-time inlining of translations, in which case the extra message ids - * would add unwanted size to the final source bundle. - * - * It is safe to leave this set to true if you are doing compile-time inlining because the extra - * legacy message ids will all be stripped during translation. - */ - enableI18nLegacyMessageIdFormat?: boolean; - - /** - * Whether to invalidate any entry-point manifest file that is on disk. Instead, walk the - * directory tree looking for entry-points, and then write a new entry-point manifest, if - * possible. - * - * Default: `false` (i.e. the manifest will be used if available) - */ - invalidateEntryPointManifest?: boolean; -} - -/** - * The options to configure the ngcc compiler for asynchronous execution. - */ -export type AsyncNgccOptions = Omit<SyncNgccOptions, 'async'>& {async: true}; - -/** - * The options to configure the ngcc compiler. - */ -export type NgccOptions = AsyncNgccOptions | SyncNgccOptions; - /** * This is the main entry-point into ngcc (aNGular Compatibility Compiler). * @@ -165,26 +48,26 @@ export type NgccOptions = AsyncNgccOptions | SyncNgccOptions; */ export function mainNgcc(options: AsyncNgccOptions): Promise<void>; export function mainNgcc(options: SyncNgccOptions): void; -export function mainNgcc({basePath, targetEntryPointPath, - propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES, - compileAllFormats = true, createNewEntryPointFormats = false, - logger = new ConsoleLogger(LogLevel.info), pathMappings, async = false, - errorOnFailedEntryPoint = false, enableI18nLegacyMessageIdFormat = true, - invalidateEntryPointManifest = false}: NgccOptions): void|Promise<void> { - if (!!targetEntryPointPath) { - // targetEntryPointPath forces us to error if an entry-point fails. - errorOnFailedEntryPoint = true; - } - - // Execute in parallel, if async execution is acceptable and there are more than 1 CPU cores. - const inParallel = async && (os.cpus().length > 1); - - // Instantiate common utilities that are always used. - // NOTE: Avoid eagerly instantiating anything that might not be used when running sync/async or in - // master/worker process. - const fileSystem = getFileSystem(); - const absBasePath = absoluteFrom(basePath); - const config = new NgccConfiguration(fileSystem, dirname(absBasePath)); +export function mainNgcc(options: NgccOptions): void|Promise<void> { + const { + basePath, + targetEntryPointPath, + propertiesToConsider, + compileAllFormats, + createNewEntryPointFormats, + logger, + pathMappings, + async, + errorOnFailedEntryPoint, + enableI18nLegacyMessageIdFormat, + invalidateEntryPointManifest, + fileSystem, + absBasePath, + projectPath, + tsConfig + } = getSharedSetup(options); + + const config = new NgccConfiguration(fileSystem, projectPath); const dependencyResolver = getDependencyResolver(fileSystem, logger, config, pathMappings); const entryPointManifest = invalidateEntryPointManifest ? new InvalidatingEntryPointManifest(fileSystem, config, logger) : @@ -203,128 +86,20 @@ export function mainNgcc({basePath, targetEntryPointPath, return; } - // NOTE: To avoid file corruption, ensure that each `ngcc` invocation only creates _one_ instance - // of `PackageJsonUpdater` that actually writes to disk (across all processes). - // This is hard to enforce automatically, when running on multiple processes, so needs to be - // enforced manually. - const pkgJsonUpdater = getPackageJsonUpdater(inParallel, fileSystem); - - // The function for performing the analysis. - const analyzeEntryPoints: AnalyzeEntryPointsFn = () => { - logger.debug('Analyzing entry-points...'); - const startTime = Date.now(); - - let entryPointInfo = finder.findEntryPoints(); - const cleaned = cleanOutdatedPackages(fileSystem, entryPointInfo.entryPoints); - if (cleaned) { - // If we had to clean up one or more packages then we must read in the entry-points again. - entryPointInfo = finder.findEntryPoints(); - } - - const {entryPoints, invalidEntryPoints, graph} = entryPointInfo; - logInvalidEntryPoints(logger, invalidEntryPoints); - - const unprocessableEntryPointPaths: string[] = []; - // The tasks are partially ordered by virtue of the entry-points being partially ordered too. - const tasks: PartiallyOrderedTasks = [] as any; - - for (const entryPoint of entryPoints) { - const packageJson = entryPoint.packageJson; - const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings'); - const {propertiesToProcess, equivalentPropertiesMap} = - getPropertiesToProcess(packageJson, supportedPropertiesToConsider, compileAllFormats); - let processDts = !hasProcessedTypings; - - if (propertiesToProcess.length === 0) { - // This entry-point is unprocessable (i.e. there is no format property that is of interest - // and can be processed). This will result in an error, but continue looping over - // entry-points in order to collect all unprocessable ones and display a more informative - // error. - unprocessableEntryPointPaths.push(entryPoint.path); - continue; - } - - for (const formatProperty of propertiesToProcess) { - if (hasBeenProcessed(entryPoint.packageJson, formatProperty)) { - // The format-path which the property maps to is already processed - nothing to do. - logger.debug(`Skipping ${entryPoint.name} : ${formatProperty} (already compiled).`); - continue; - } - - const formatPropertiesToMarkAsProcessed = equivalentPropertiesMap.get(formatProperty) !; - tasks.push({entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts}); - - // Only process typings for the first property (if not already processed). - processDts = false; - } - } - - // Check for entry-points for which we could not process any format at all. - if (unprocessableEntryPointPaths.length > 0) { - throw new Error( - 'Unable to process any formats for the following entry-points (tried ' + - `${propertiesToConsider.join(', ')}): ` + - unprocessableEntryPointPaths.map(path => `\n - ${path}`).join('')); - } + // Execute in parallel, if async execution is acceptable and there are more than 1 CPU cores. + const inParallel = async && (os.cpus().length > 1); - const duration = Math.round((Date.now() - startTime) / 100) / 10; - logger.debug( - `Analyzed ${entryPoints.length} entry-points in ${duration}s. ` + - `(Total tasks: ${tasks.length})`); + const analyzeEntryPoints = getAnalyzeEntryPointsFn( + logger, finder, fileSystem, supportedPropertiesToConsider, compileAllFormats, + propertiesToConsider, inParallel); - return getTaskQueue(logger, inParallel, tasks, graph); - }; + // Create an updater that will actually write to disk. In + const pkgJsonUpdater = new DirectPackageJsonUpdater(fileSystem); // The function for creating the `compile()` function. - const createCompileFn: CreateCompileFn = onTaskCompleted => { - const fileWriter = getFileWriter( - fileSystem, logger, pkgJsonUpdater, createNewEntryPointFormats, errorOnFailedEntryPoint); - const transformer = new Transformer(fileSystem, logger); - - return (task: Task) => { - const {entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts} = task; - - const isCore = entryPoint.name === '@angular/core'; // Are we compiling the Angular core? - const packageJson = entryPoint.packageJson; - const formatPath = packageJson[formatProperty]; - const format = getEntryPointFormat(fileSystem, entryPoint, formatProperty); - - // All properties listed in `propertiesToProcess` are guaranteed to point to a format-path - // (i.e. they are defined in `entryPoint.packageJson`). Furthermore, they are also guaranteed - // to be among `SUPPORTED_FORMAT_PROPERTIES`. - // Based on the above, `formatPath` should always be defined and `getEntryPointFormat()` - // should always return a format here (and not `undefined`). - if (!formatPath || !format) { - // This should never happen. - throw new Error( - `Invariant violated: No format-path or format for ${entryPoint.path} : ` + - `${formatProperty} (formatPath: ${formatPath} | format: ${format})`); - } - - const bundle = makeEntryPointBundle( - fileSystem, entryPoint, formatPath, isCore, format, processDts, pathMappings, true, - enableI18nLegacyMessageIdFormat); - - logger.info(`Compiling ${entryPoint.name} : ${formatProperty} as ${format}`); - - const result = transformer.transform(bundle); - if (result.success) { - if (result.diagnostics.length > 0) { - logger.warn(replaceTsWithNgInErrors( - ts.formatDiagnosticsWithColorAndContext(result.diagnostics, bundle.src.host))); - } - fileWriter.writeBundle(bundle, result.transformedFiles, formatPropertiesToMarkAsProcessed); - - logger.debug(` Successfully compiled ${entryPoint.name} : ${formatProperty}`); - - onTaskCompleted(task, TaskProcessingOutcome.Processed, null); - } else { - const errors = replaceTsWithNgInErrors( - ts.formatDiagnosticsWithColorAndContext(result.diagnostics, bundle.src.host)); - onTaskCompleted(task, TaskProcessingOutcome.Failed, `compilation errors:\n${errors}`); - } - }; - }; + const createCompileFn = getCreateCompileFn( + fileSystem, logger, pkgJsonUpdater, createNewEntryPointFormats, errorOnFailedEntryPoint, + enableI18nLegacyMessageIdFormat, tsConfig, pathMappings); // The executor for actually planning and getting the work done. const createTaskCompletedCallback = @@ -357,27 +132,6 @@ function ensureSupportedProperties(properties: string[]): EntryPointJsonProperty return supportedProperties; } -function getPackageJsonUpdater(inParallel: boolean, fs: FileSystem): PackageJsonUpdater { - const directPkgJsonUpdater = new DirectPackageJsonUpdater(fs); - return inParallel ? new ClusterPackageJsonUpdater(directPkgJsonUpdater) : directPkgJsonUpdater; -} - -function getFileWriter( - fs: FileSystem, logger: Logger, pkgJsonUpdater: PackageJsonUpdater, - createNewEntryPointFormats: boolean, errorOnFailedEntryPoint: boolean): FileWriter { - return createNewEntryPointFormats ? - new NewEntryPointFileWriter(fs, logger, errorOnFailedEntryPoint, pkgJsonUpdater) : - new InPlaceFileWriter(fs, logger, errorOnFailedEntryPoint); -} - -function getTaskQueue( - logger: Logger, inParallel: boolean, tasks: PartiallyOrderedTasks, - graph: DepGraph<EntryPoint>): TaskQueue { - const dependencies = computeTaskDependencies(tasks, graph); - return inParallel ? new ParallelTaskQueue(logger, tasks, dependencies) : - new SerialTaskQueue(logger, tasks, dependencies); -} - function getCreateTaskCompletedCallback( pkgJsonUpdater: PackageJsonUpdater, errorOnFailedEntryPoint: boolean, logger: Logger, fileSystem: FileSystem): CreateTaskCompletedCallback { @@ -400,7 +154,7 @@ function getExecutor( // Execute in parallel. Use up to 8 CPU cores for workers, always reserving one for master. const workerCount = Math.min(8, os.cpus().length - 1); return new ClusterExecutor( - workerCount, logger, pkgJsonUpdater, locker, createTaskCompletedCallback); + workerCount, fileSystem, logger, pkgJsonUpdater, locker, createTaskCompletedCallback); } else { // Execute serially, on a single thread (async). return new SingleProcessExecutorAsync(logger, locker, createTaskCompletedCallback); @@ -414,7 +168,7 @@ function getExecutor( function getDependencyResolver( fileSystem: FileSystem, logger: Logger, config: NgccConfiguration, - pathMappings: PathMappings | undefined): DependencyResolver { + pathMappings: PathMappings|undefined): DependencyResolver { const moduleResolver = new ModuleResolver(fileSystem, pathMappings); const esmDependencyHost = new EsmDependencyHost(fileSystem, moduleResolver); const umdDependencyHost = new UmdDependencyHost(fileSystem, moduleResolver); @@ -433,8 +187,8 @@ function getDependencyResolver( function getEntryPointFinder( fs: FileSystem, logger: Logger, resolver: DependencyResolver, config: NgccConfiguration, entryPointManifest: EntryPointManifest, basePath: AbsoluteFsPath, - absoluteTargetEntryPointPath: AbsoluteFsPath | null, - pathMappings: PathMappings | undefined): EntryPointFinder { + absoluteTargetEntryPointPath: AbsoluteFsPath|null, + pathMappings: PathMappings|undefined): EntryPointFinder { if (absoluteTargetEntryPointPath !== null) { return new TargetedEntryPointFinder( fs, config, logger, resolver, basePath, absoluteTargetEntryPointPath, pathMappings); @@ -443,72 +197,3 @@ function getEntryPointFinder( fs, config, logger, resolver, entryPointManifest, basePath, pathMappings); } } - -function logInvalidEntryPoints(logger: Logger, invalidEntryPoints: InvalidEntryPoint[]): void { - invalidEntryPoints.forEach(invalidEntryPoint => { - logger.debug( - `Invalid entry-point ${invalidEntryPoint.entryPoint.path}.`, - `It is missing required dependencies:\n` + - invalidEntryPoint.missingDependencies.map(dep => ` - ${dep}`).join('\n')); - }); -} - -/** - * This function computes and returns the following: - * - `propertiesToProcess`: An (ordered) list of properties that exist and need to be processed, - * based on the provided `propertiesToConsider`, the properties in `package.json` and their - * corresponding format-paths. NOTE: Only one property per format-path needs to be processed. - * - `equivalentPropertiesMap`: A mapping from each property in `propertiesToProcess` to the list of - * other format properties in `package.json` that need to be marked as processed as soon as the - * former has been processed. - */ -function getPropertiesToProcess( - packageJson: EntryPointPackageJson, propertiesToConsider: EntryPointJsonProperty[], - compileAllFormats: boolean): { - propertiesToProcess: EntryPointJsonProperty[]; - equivalentPropertiesMap: Map<EntryPointJsonProperty, EntryPointJsonProperty[]>; -} { - const formatPathsToConsider = new Set<string>(); - - const propertiesToProcess: EntryPointJsonProperty[] = []; - for (const prop of propertiesToConsider) { - const formatPath = packageJson[prop]; - - // Ignore properties that are not defined in `package.json`. - if (typeof formatPath !== 'string') continue; - - // Ignore properties that map to the same format-path as a preceding property. - if (formatPathsToConsider.has(formatPath)) continue; - - // Process this property, because it is the first one to map to this format-path. - formatPathsToConsider.add(formatPath); - propertiesToProcess.push(prop); - - // If we only need one format processed, there is no need to process any more properties. - if (!compileAllFormats) break; - } - - const formatPathToProperties: {[formatPath: string]: EntryPointJsonProperty[]} = {}; - for (const prop of SUPPORTED_FORMAT_PROPERTIES) { - const formatPath = packageJson[prop]; - - // Ignore properties that are not defined in `package.json`. - if (typeof formatPath !== 'string') continue; - - // Ignore properties that do not map to a format-path that will be considered. - if (!formatPathsToConsider.has(formatPath)) continue; - - // Add this property to the map. - const list = formatPathToProperties[formatPath] || (formatPathToProperties[formatPath] = []); - list.push(prop); - } - - const equivalentPropertiesMap = new Map<EntryPointJsonProperty, EntryPointJsonProperty[]>(); - for (const prop of propertiesToConsider) { - const formatPath = packageJson[prop] !; - const equivalentProperties = formatPathToProperties[formatPath]; - equivalentPropertiesMap.set(prop, equivalentProperties); - } - - return {propertiesToProcess, equivalentPropertiesMap}; -} diff --git a/packages/compiler-cli/ngcc/src/migrations/missing_injectable_migration.ts b/packages/compiler-cli/ngcc/src/migrations/missing_injectable_migration.ts index 8c590a08878d1..1affab1b795c8 100644 --- a/packages/compiler-cli/ngcc/src/migrations/missing_injectable_migration.ts +++ b/packages/compiler-cli/ngcc/src/migrations/missing_injectable_migration.ts @@ -102,7 +102,7 @@ function migrateProviders(metadata: ResolvedValueMap, field: string, host: Migra if (!metadata.has(field)) { return; } - const providers = metadata.get(field) !; + const providers = metadata.get(field)!; if (!Array.isArray(providers)) { return; } @@ -127,10 +127,10 @@ function migrateProvider(provider: ResolvedValue, host: MigrationHost): void { // as the provider itself configures 'deps'. Only if 'deps' is missing will this require a // factory to exist on SomeClass. if (!provider.has('deps')) { - migrateProviderClass(provider.get('useClass') !, host); + migrateProviderClass(provider.get('useClass')!, host); } } else { - migrateProviderClass(provider.get('provide') !, host); + migrateProviderClass(provider.get('provide')!, host); } } else if (Array.isArray(provider)) { for (const v of provider) { diff --git a/packages/compiler-cli/ngcc/src/migrations/utils.ts b/packages/compiler-cli/ngcc/src/migrations/utils.ts index c51b95014b617..1437f46b35801 100644 --- a/packages/compiler-cli/ngcc/src/migrations/utils.ts +++ b/packages/compiler-cli/ngcc/src/migrations/utils.ts @@ -43,7 +43,7 @@ export function hasConstructor(host: MigrationHost, clazz: ClassDeclaration): bo */ export function createDirectiveDecorator( clazz: ClassDeclaration, - metadata?: {selector: string | null, exportAs: string[] | null}): Decorator { + metadata?: {selector: string|null, exportAs: string[]|null}): Decorator { const args: ts.Expression[] = []; if (metadata !== undefined) { const metaArgs: ts.PropertyAssignment[] = []; @@ -60,7 +60,8 @@ export function createDirectiveDecorator( identifier: null, import: {name: 'Directive', from: '@angular/core'}, node: null, - synthesizedFor: clazz.name, args, + synthesizedFor: clazz.name, + args, }; } @@ -69,7 +70,7 @@ export function createDirectiveDecorator( */ export function createComponentDecorator( clazz: ClassDeclaration, - metadata: {selector: string | null, exportAs: string[] | null}): Decorator { + metadata: {selector: string|null, exportAs: string[]|null}): Decorator { const metaArgs: ts.PropertyAssignment[] = [ property('template', ''), ]; @@ -127,5 +128,5 @@ function reifySourceFile(expr: ts.Expression): ts.Expression { if (!ts.isVariableStatement(stmt)) { throw new Error(`Expected VariableStatement, got ${ts.SyntaxKind[stmt.kind]}`); } - return stmt.declarationList.declarations[0].initializer !; + return stmt.declarationList.declarations[0].initializer!; } diff --git a/packages/compiler-cli/ngcc/src/ngcc_options.ts b/packages/compiler-cli/ngcc/src/ngcc_options.ts new file mode 100644 index 0000000000000..36120276ca490 --- /dev/null +++ b/packages/compiler-cli/ngcc/src/ngcc_options.ts @@ -0,0 +1,214 @@ +/** + * @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 {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, resolve} from '../../src/ngtsc/file_system'; +import {ParsedConfiguration, readConfiguration} from '../../src/perform_compile'; + +import {ConsoleLogger} from './logging/console_logger'; +import {Logger, LogLevel} from './logging/logger'; +import {SUPPORTED_FORMAT_PROPERTIES} from './packages/entry_point'; + +/** + * The options to configure the ngcc compiler for synchronous execution. + */ +export interface SyncNgccOptions { + /** The absolute path to the `node_modules` folder that contains the packages to process. */ + basePath: string; + + /** + * The path to the primary package to be processed. If not absolute then it must be relative to + * `basePath`. + * + * All its dependencies will need to be processed too. + * + * If this property is provided then `errorOnFailedEntryPoint` is forced to true. + */ + targetEntryPointPath?: string; + + /** + * Which entry-point properties in the package.json to consider when processing an entry-point. + * Each property should hold a path to the particular bundle format for the entry-point. + * Defaults to all the properties in the package.json. + */ + propertiesToConsider?: string[]; + + /** + * Whether to process all formats specified by (`propertiesToConsider`) or to stop processing + * this entry-point at the first matching format. Defaults to `true`. + */ + compileAllFormats?: boolean; + + /** + * Whether to create new entry-points bundles rather than overwriting the original files. + */ + createNewEntryPointFormats?: boolean; + + /** + * Provide a logger that will be called with log messages. + */ + logger?: Logger; + + /** + * Paths mapping configuration (`paths` and `baseUrl`), as found in `ts.CompilerOptions`. + * These are used to resolve paths to locally built Angular libraries. + * + * Note that `pathMappings` specified here take precedence over any `pathMappings` loaded from a + * TS config file. + */ + pathMappings?: PathMappings; + + /** + * Provide a file-system service that will be used by ngcc for all file interactions. + */ + fileSystem?: FileSystem; + + /** + * Whether the compilation should run and return asynchronously. Allowing asynchronous execution + * may speed up the compilation by utilizing multiple CPU cores (if available). + * + * Default: `false` (i.e. run synchronously) + */ + async?: false; + + /** + * Set to true in order to terminate immediately with an error code if an entry-point fails to be + * processed. + * + * If `targetEntryPointPath` is provided then this property is always true and cannot be + * changed. Otherwise the default is false. + * + * When set to false, ngcc will continue to process entry-points after a failure. In which case it + * will log an error and resume processing other entry-points. + */ + errorOnFailedEntryPoint?: boolean; + + /** + * Render `$localize` messages with legacy format ids. + * + * The default value is `true`. Only set this to `false` if you do not want legacy message ids to + * be rendered. For example, if you are not using legacy message ids in your translation files + * AND are not doing compile-time inlining of translations, in which case the extra message ids + * would add unwanted size to the final source bundle. + * + * It is safe to leave this set to true if you are doing compile-time inlining because the extra + * legacy message ids will all be stripped during translation. + */ + enableI18nLegacyMessageIdFormat?: boolean; + + /** + * Whether to invalidate any entry-point manifest file that is on disk. Instead, walk the + * directory tree looking for entry-points, and then write a new entry-point manifest, if + * possible. + * + * Default: `false` (i.e. the manifest will be used if available) + */ + invalidateEntryPointManifest?: boolean; + + /** + * An absolute path to a TS config file (e.g. `tsconfig.json`) or a directory containing one, that + * will be used to configure module resolution with things like path mappings, if not specified + * explicitly via the `pathMappings` property to `mainNgcc`. + * + * If `undefined`, ngcc will attempt to load a `tsconfig.json` file from the directory above the + * `basePath`. + * + * If `null`, ngcc will not attempt to load any TS config file at all. + */ + tsConfigPath?: string|null; +} + +/** + * The options to configure the ngcc compiler for asynchronous execution. + */ +export type AsyncNgccOptions = Omit<SyncNgccOptions, 'async'>&{async: true}; + +/** + * The options to configure the ngcc compiler. + */ +export type NgccOptions = AsyncNgccOptions|SyncNgccOptions; + +export type PathMappings = { + baseUrl: string, + paths: {[key: string]: string[]} +}; + +/** + * If `pathMappings` is not provided directly, then try getting it from `tsConfig`, if available. + */ +export function getPathMappingsFromTsConfig( + tsConfig: ParsedConfiguration|null, projectPath: AbsoluteFsPath): PathMappings|undefined { + if (tsConfig !== null && tsConfig.options.baseUrl !== undefined && + tsConfig.options.paths !== undefined) { + return { + baseUrl: resolve(projectPath, tsConfig.options.baseUrl), + paths: tsConfig.options.paths, + }; + } +} + +export type OptionalNgccOptionKeys = 'targetEntryPointPath'|'tsConfigPath'|'pathMappings'; +export type RequiredNgccOptions = Required<Omit<NgccOptions, OptionalNgccOptionKeys>>; +export type OptionalNgccOptions = Pick<NgccOptions, OptionalNgccOptionKeys>; +export type SharedSetup = { + fileSystem: FileSystem; absBasePath: AbsoluteFsPath; projectPath: AbsoluteFsPath; + tsConfig: ParsedConfiguration | null; +}; + +/** + * Instantiate common utilities that are always used and fix up options with defaults, as necessary. + * + * NOTE: Avoid eagerly instantiating anything that might not be used when running sync/async. + */ +export function getSharedSetup(options: NgccOptions): SharedSetup&RequiredNgccOptions& + OptionalNgccOptions { + const fileSystem = getFileSystem(); + const absBasePath = absoluteFrom(options.basePath); + const projectPath = fileSystem.dirname(absBasePath); + const tsConfig = + options.tsConfigPath !== null ? readConfiguration(options.tsConfigPath || projectPath) : null; + + let { + basePath, + targetEntryPointPath, + propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES, + compileAllFormats = true, + createNewEntryPointFormats = false, + logger = new ConsoleLogger(LogLevel.info), + pathMappings, + async = false, + errorOnFailedEntryPoint = false, + enableI18nLegacyMessageIdFormat = true, + invalidateEntryPointManifest = false, + tsConfigPath, + } = options; + + pathMappings = options.pathMappings || getPathMappingsFromTsConfig(tsConfig, projectPath); + + if (!!options.targetEntryPointPath) { + // targetEntryPointPath forces us to error if an entry-point fails. + errorOnFailedEntryPoint = true; + } + + return { + basePath, + targetEntryPointPath, + propertiesToConsider, + compileAllFormats, + createNewEntryPointFormats, + logger, + pathMappings, + async, + errorOnFailedEntryPoint, + enableI18nLegacyMessageIdFormat, + invalidateEntryPointManifest, + tsConfigPath, + fileSystem, + absBasePath, + projectPath, + tsConfig, + }; +} diff --git a/packages/compiler-cli/ngcc/src/packages/bundle_program.ts b/packages/compiler-cli/ngcc/src/packages/bundle_program.ts index cbea96d34bad0..a094a03c07082 100644 --- a/packages/compiler-cli/ngcc/src/packages/bundle_program.ts +++ b/packages/compiler-cli/ngcc/src/packages/bundle_program.ts @@ -6,17 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {AbsoluteFsPath, FileSystem, dirname, resolve} from '../../../src/ngtsc/file_system'; + +import {AbsoluteFsPath, dirname, FileSystem, resolve} from '../../../src/ngtsc/file_system'; + import {patchTsGetExpandoInitializer, restoreGetExpandoInitializer} from './patch_ts_expando_initializer'; /** -* An entry point bundle contains one or two programs, e.g. `src` and `dts`, -* that are compiled via TypeScript. -* -* To aid with processing the program, this interface exposes the program itself, -* as well as path and TS file of the entry-point to the program and the r3Symbols -* file, if appropriate. -*/ + * An entry point bundle contains one or two programs, e.g. `src` and `dts`, + * that are compiled via TypeScript. + * + * To aid with processing the program, this interface exposes the program itself, + * as well as path and TS file of the entry-point to the program and the r3Symbols + * file, if appropriate. + */ export interface BundleProgram { program: ts.Program; options: ts.CompilerOptions; @@ -46,7 +48,7 @@ export function makeBundleProgram( program.getTypeChecker(); restoreGetExpandoInitializer(originalGetExpandoInitializer); - const file = program.getSourceFile(path) !; + const file = program.getSourceFile(path)!; const r3SymbolsFile = r3SymbolsPath && program.getSourceFile(r3SymbolsPath) || null; return {program, options, host, package: pkg, path, file, r3SymbolsPath, r3SymbolsFile}; diff --git a/packages/compiler-cli/ngcc/src/packages/configuration.ts b/packages/compiler-cli/ngcc/src/packages/configuration.ts index 133d1ad2a8ffe..5b718f7ca20bc 100644 --- a/packages/compiler-cli/ngcc/src/packages/configuration.ts +++ b/packages/compiler-cli/ngcc/src/packages/configuration.ts @@ -8,7 +8,9 @@ import {createHash} from 'crypto'; import {satisfies} from 'semver'; import * as vm from 'vm'; -import {AbsoluteFsPath, FileSystem, dirname, join, resolve} from '../../../src/ngtsc/file_system'; + +import {AbsoluteFsPath, dirname, FileSystem, join, resolve} from '../../../src/ngtsc/file_system'; + import {PackageJsonFormatPropertiesMap} from './entry_point'; /** @@ -178,7 +180,7 @@ export class NgccConfiguration { getConfig(packagePath: AbsoluteFsPath, version: string|null): VersionedPackageConfig { const cacheKey = packagePath + (version !== null ? `@${version}` : ''); if (this.cache.has(cacheKey)) { - return this.cache.get(cacheKey) !; + return this.cache.get(cacheKey)!; } const projectLevelConfig = @@ -239,9 +241,11 @@ export class NgccConfiguration { const configFilePath = join(packagePath, NGCC_CONFIG_FILENAME); if (this.fs.exists(configFilePath)) { try { + const packageConfig = this.evalSrcFile(configFilePath); return { + ...packageConfig, versionRange: version || '*', - entryPoints: this.processEntryPoints(packagePath, this.evalSrcFile(configFilePath)), + entryPoints: this.processEntryPoints(packagePath, packageConfig), }; } catch (e) { throw new Error(`Invalid package configuration file at "${configFilePath}": ` + e.message); @@ -256,7 +260,8 @@ export class NgccConfiguration { const theExports = {}; const sandbox = { module: {exports: theExports}, - exports: theExports, require, + exports: theExports, + require, __dirname: dirname(srcPath), __filename: srcPath }; @@ -292,9 +297,8 @@ export class NgccConfiguration { } } -function findSatisfactoryVersion( - configs: VersionedPackageConfig[] | undefined, version: string | null): VersionedPackageConfig| - null { +function findSatisfactoryVersion(configs: VersionedPackageConfig[]|undefined, version: string|null): + VersionedPackageConfig|null { if (configs === undefined) { return null; } @@ -304,5 +308,7 @@ function findSatisfactoryVersion( // So just return the first config that matches the package name. return configs[0]; } - return configs.find(config => satisfies(version, config.versionRange)) || null; + return configs.find( + config => satisfies(version, config.versionRange, {includePrerelease: true})) || + null; } diff --git a/packages/compiler-cli/ngcc/src/packages/entry_point.ts b/packages/compiler-cli/ngcc/src/packages/entry_point.ts index ea64e05ed2058..1c6e56fa4fe51 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point.ts @@ -17,7 +17,7 @@ import {NgccConfiguration, NgccEntryPointConfig} from './configuration'; /** * The possible values for the format of an entry-point. */ -export type EntryPointFormat = 'esm5' | 'esm2015' | 'umd' | 'commonjs'; +export type EntryPointFormat = 'esm5'|'esm2015'|'umd'|'commonjs'; /** * An object containing information about an entry-point, including paths @@ -42,12 +42,15 @@ export interface EntryPoint extends JsonObject { generateDeepReexports: boolean; } -export type JsonPrimitive = string | number | boolean | null; -export type JsonValue = JsonPrimitive | JsonArray | JsonObject | undefined; +export type JsonPrimitive = string|number|boolean|null; +export type JsonValue = JsonPrimitive|JsonArray|JsonObject|undefined; export interface JsonArray extends Array<JsonValue> {} -export interface JsonObject { [key: string]: JsonValue; } +export interface JsonObject { + [key: string]: JsonValue; +} export interface PackageJsonFormatPropertiesMap { + browser?: string; fesm2015?: string; fesm5?: string; es2015?: string; // if exists then it is actually FESM2015 @@ -73,7 +76,7 @@ export interface EntryPointPackageJson extends JsonObject, PackageJsonFormatProp export type EntryPointJsonProperty = Exclude<PackageJsonFormatProperties, 'types'|'typings'>; // We need to keep the elements of this const and the `EntryPointJsonProperty` type in sync. export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] = - ['fesm2015', 'fesm5', 'es2015', 'esm2015', 'esm5', 'main', 'module']; + ['fesm2015', 'fesm5', 'es2015', 'esm2015', 'esm5', 'main', 'module', 'browser']; /** @@ -86,7 +89,7 @@ export const NO_ENTRY_POINT = 'no-entry-point'; /** * The path has a package.json, but it is not a valid entry-point for ngcc processing. */ -export const INVALID_ENTRY_POINT = 'invalid-entry-point'; +export const INCOMPATIBLE_ENTRY_POINT = 'incompatible-entry-point'; /** * The result of calling `getEntryPointInfo()`. @@ -94,10 +97,10 @@ export const INVALID_ENTRY_POINT = 'invalid-entry-point'; * This will be an `EntryPoint` object if an Angular entry-point was identified; * Otherwise it will be a flag indicating one of: * * NO_ENTRY_POINT - the path is not an entry-point or ngcc is configured to ignore it - * * INVALID_ENTRY_POINT - the path was a non-processable entry-point that should be searched + * * INCOMPATIBLE_ENTRY_POINT - the path was a non-processable entry-point that should be searched * for sub-entry-points */ -export type GetEntryPointResult = EntryPoint | typeof INVALID_ENTRY_POINT | typeof NO_ENTRY_POINT; +export type GetEntryPointResult = EntryPoint|typeof INCOMPATIBLE_ENTRY_POINT|typeof NO_ENTRY_POINT; /** @@ -107,9 +110,10 @@ export type GetEntryPointResult = EntryPoint | typeof INVALID_ENTRY_POINT | type * @param entryPointPath the absolute path to the potential entry-point. * @returns * - An entry-point if it is valid. - * - `undefined` when there is no package.json at the path and there is no config to force an + * - `NO_ENTRY_POINT` when there is no package.json at the path and there is no config to force an * entry-point or the entrypoint is `ignored`. - * - `null` there is a package.json but it is not a valid Angular compiled entry-point. + * - `INCOMPATIBLE_ENTRY_POINT` there is a package.json but it is not a valid Angular compiled + * entry-point. */ export function getEntryPointInfo( fs: FileSystem, config: NgccConfiguration, logger: Logger, packagePath: AbsoluteFsPath, @@ -138,14 +142,14 @@ export function getEntryPointInfo( if (entryPointPackageJson === null) { // package.json exists but could not be parsed and there was no redeeming config - return INVALID_ENTRY_POINT; + return INCOMPATIBLE_ENTRY_POINT; } const typings = entryPointPackageJson.typings || entryPointPackageJson.types || guessTypingsFromPackageJson(fs, entryPointPath, entryPointPackageJson); if (typeof typings !== 'string') { // Missing the required `typings` property - return INVALID_ENTRY_POINT; + return INCOMPATIBLE_ENTRY_POINT; } // An entry-point is assumed to be compiled by Angular if there is either: @@ -159,7 +163,8 @@ export function getEntryPointInfo( packageJson: entryPointPackageJson, package: packagePath, path: entryPointPath, - typings: resolve(entryPointPath, typings), compiledByAngular, + typings: resolve(entryPointPath, typings), + compiledByAngular, ignoreMissingDependencies: entryPointConfig !== undefined ? !!entryPointConfig.ignoreMissingDependencies : false, generateDeepReexports: @@ -189,13 +194,18 @@ export function getEntryPointFormat( return 'esm2015'; case 'esm5': return 'esm5'; + case 'browser': + const browserFile = entryPoint.packageJson['browser']; + if (typeof browserFile !== 'string') { + return undefined; + } + return sniffModuleFormat(fs, join(entryPoint.path, browserFile)); case 'main': const mainFile = entryPoint.packageJson['main']; if (mainFile === undefined) { return undefined; } - const pathToMain = join(entryPoint.path, mainFile); - return isUmdModule(fs, pathToMain) ? 'umd' : 'commonjs'; + return sniffModuleFormat(fs, join(entryPoint.path, mainFile)); case 'module': return 'esm5'; default: @@ -222,19 +232,29 @@ function loadEntryPointPackage( } } -function isUmdModule(fs: FileSystem, sourceFilePath: AbsoluteFsPath): boolean { +function sniffModuleFormat(fs: FileSystem, sourceFilePath: AbsoluteFsPath): EntryPointFormat| + undefined { const resolvedPath = resolveFileWithPostfixes(fs, sourceFilePath, ['', '.js', '/index.js']); if (resolvedPath === null) { - return false; + return undefined; } + const sourceFile = ts.createSourceFile(sourceFilePath, fs.readFile(resolvedPath), ts.ScriptTarget.ES5); - return sourceFile.statements.length > 0 && - parseStatementForUmdModule(sourceFile.statements[0]) !== null; + if (sourceFile.statements.length === 0) { + return undefined; + } + if (ts.isExternalModule(sourceFile)) { + return 'esm5'; + } else if (parseStatementForUmdModule(sourceFile.statements[0]) !== null) { + return 'umd'; + } else { + return 'commonjs'; + } } function mergeConfigAndPackageJson( - entryPointPackageJson: EntryPointPackageJson | null, entryPointConfig: NgccEntryPointConfig, + entryPointPackageJson: EntryPointPackageJson|null, entryPointConfig: NgccEntryPointConfig, packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath): EntryPointPackageJson { if (entryPointPackageJson !== null) { return {...entryPointPackageJson, ...entryPointConfig.override}; diff --git a/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts b/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts index 89d28e2398c0f..62259c1b4ca81 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point_bundle.ts @@ -7,7 +7,7 @@ */ import * as ts from 'typescript'; import {AbsoluteFsPath, FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system'; -import {PathMappings} from '../utils'; +import {PathMappings} from '../ngcc_options'; import {BundleProgram, makeBundleProgram} from './bundle_program'; import {EntryPoint, EntryPointFormat} from './entry_point'; import {NgccSourcesCompilerHost} from './ngcc_compiler_host'; @@ -48,10 +48,8 @@ export function makeEntryPointBundle( enableI18nLegacyMessageIdFormat: boolean = true): EntryPointBundle { // Create the TS program and necessary helpers. const rootDir = entryPoint.package; - const options: ts.CompilerOptions = { - allowJs: true, - maxNodeModuleJsDepth: Infinity, rootDir, ...pathMappings - }; + const options: ts + .CompilerOptions = {allowJs: true, maxNodeModuleJsDepth: Infinity, rootDir, ...pathMappings}; const srcHost = new NgccSourcesCompilerHost(fs, options, entryPoint.path); const dtsHost = new NgtscCompilerHost(fs, options); @@ -72,7 +70,12 @@ export function makeEntryPointBundle( return { entryPoint, format, - rootDirs: [rootDir], isCore, isFlatCore, src, dts, enableI18nLegacyMessageIdFormat + rootDirs: [rootDir], + isCore, + isFlatCore, + src, + dts, + enableI18nLegacyMessageIdFormat }; } diff --git a/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts b/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts index 8df1af790027b..9321ad594001c 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts @@ -7,12 +7,13 @@ */ import {createHash} from 'crypto'; -import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {Logger} from '../logging/logger'; import {NGCC_VERSION} from './build_marker'; import {NgccConfiguration} from './configuration'; -import {EntryPoint, INVALID_ENTRY_POINT, NO_ENTRY_POINT, getEntryPointInfo} from './entry_point'; +import {getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from './entry_point'; /** * Manages reading and writing a manifest file that contains a list of all the entry-points that @@ -40,7 +41,7 @@ export class EntryPointManifest { * @returns an array of entry-point information for all entry-points found below the given * `basePath` or `null` if the manifest was out of date. */ - readEntryPointsUsingManifest(basePath: AbsoluteFsPath): EntryPoint[]|null { + readEntryPointsUsingManifest(basePath: AbsoluteFsPath): EntryPointWithDependencies[]|null { try { if (this.fs.basename(basePath) !== 'node_modules') { return null; @@ -63,19 +64,30 @@ export class EntryPointManifest { return null; } - this.logger.debug( - `Entry-point manifest found for ${basePath} so loading entry-point information directly.`); + this.logger.debug(`Entry-point manifest found for ${ + basePath} so loading entry-point information directly.`); const startTime = Date.now(); - const entryPoints: EntryPoint[] = []; - for (const [packagePath, entryPointPath] of entryPointPaths) { - const result = - getEntryPointInfo(this.fs, this.config, this.logger, packagePath, entryPointPath); - if (result === NO_ENTRY_POINT || result === INVALID_ENTRY_POINT) { - throw new Error( - `The entry-point manifest at ${manifestPath} contained an invalid pair of package paths: [${packagePath}, ${entryPointPath}]`); + const entryPoints: EntryPointWithDependencies[] = []; + for (const + [packagePath, entryPointPath, dependencyPaths = [], missingPaths = [], + deepImportPaths = []] of entryPointPaths) { + const result = getEntryPointInfo( + this.fs, this.config, this.logger, this.fs.resolve(basePath, packagePath), + this.fs.resolve(basePath, entryPointPath)); + if (result === NO_ENTRY_POINT || result === INCOMPATIBLE_ENTRY_POINT) { + throw new Error(`The entry-point manifest at ${ + manifestPath} contained an invalid pair of package paths: [${packagePath}, ${ + entryPointPath}]`); } else { - entryPoints.push(result); + entryPoints.push({ + entryPoint: result, + depInfo: { + dependencies: new Set(dependencyPaths), + missing: new Set(missingPaths), + deepImports: new Set(deepImportPaths), + } + }); } } const duration = Math.round((Date.now() - startTime) / 100) / 10; @@ -98,7 +110,12 @@ export class EntryPointManifest { * @param basePath The path where the manifest file is to be written. * @param entryPoints A collection of entry-points to record in the manifest. */ - writeEntryPointManifest(basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): void { + writeEntryPointManifest(basePath: AbsoluteFsPath, entryPoints: EntryPointWithDependencies[]): + void { + if (this.fs.basename(basePath) !== 'node_modules') { + return; + } + const lockFileHash = this.computeLockFileHash(basePath); if (lockFileHash === null) { return; @@ -107,7 +124,27 @@ export class EntryPointManifest { ngccVersion: NGCC_VERSION, configFileHash: this.config.hash, lockFileHash: lockFileHash, - entryPointPaths: entryPoints.map(entryPoint => [entryPoint.package, entryPoint.path]), + entryPointPaths: entryPoints.map(e => { + const entryPointPaths: EntryPointPaths = [ + this.fs.relative(basePath, e.entryPoint.package), + this.fs.relative(basePath, e.entryPoint.path), + ]; + // Only add depInfo arrays if needed. + if (e.depInfo.dependencies.size > 0) { + entryPointPaths[2] = Array.from(e.depInfo.dependencies); + } else if (e.depInfo.missing.size > 0 || e.depInfo.deepImports.size > 0) { + entryPointPaths[2] = []; + } + if (e.depInfo.missing.size > 0) { + entryPointPaths[3] = Array.from(e.depInfo.missing); + } else if (e.depInfo.deepImports.size > 0) { + entryPointPaths[3] = []; + } + if (e.depInfo.deepImports.size > 0) { + entryPointPaths[4] = Array.from(e.depInfo.deepImports); + } + return entryPointPaths; + }), }; this.fs.writeFile(this.getEntryPointManifestPath(basePath), JSON.stringify(manifest)); } @@ -134,13 +171,23 @@ export class EntryPointManifest { * current manifest file. * * It always returns `null` from the `readEntryPointsUsingManifest()` method, which forces a new - * manifest to be created, which will overwrite the current file when `writeEntryPointManifest()` is - * called. + * manifest to be created, which will overwrite the current file when `writeEntryPointManifest()` + * is called. */ export class InvalidatingEntryPointManifest extends EntryPointManifest { - readEntryPointsUsingManifest(basePath: AbsoluteFsPath): EntryPoint[]|null { return null; } + readEntryPointsUsingManifest(_basePath: AbsoluteFsPath): EntryPointWithDependencies[]|null { + return null; + } } +export type EntryPointPaths = [ + string, + string, + Array<AbsoluteFsPath>?, + Array<AbsoluteFsPath|PathSegment>?, + Array<AbsoluteFsPath>?, +]; + /** * The JSON format of the manifest file that is written to disk. */ @@ -148,5 +195,5 @@ export interface EntryPointManifestFile { ngccVersion: string; configFileHash: string; lockFileHash: string; - entryPointPaths: Array<[AbsoluteFsPath, AbsoluteFsPath]>; + entryPointPaths: EntryPointPaths[]; } diff --git a/packages/compiler-cli/ngcc/src/packages/patch_ts_expando_initializer.ts b/packages/compiler-cli/ngcc/src/packages/patch_ts_expando_initializer.ts index 0d6a20b0395c1..b2d5428d2f49f 100644 --- a/packages/compiler-cli/ngcc/src/packages/patch_ts_expando_initializer.ts +++ b/packages/compiler-cli/ngcc/src/packages/patch_ts_expando_initializer.ts @@ -58,17 +58,16 @@ export function patchTsGetExpandoInitializer(): unknown { } // Override the function to add support for recognizing the IIFE structure used in ES5 bundles. - (ts as any).getExpandoInitializer = - (initializer: ts.Node, isPrototypeAssignment: boolean): ts.Expression | undefined => { - // If the initializer is a call expression within parenthesis, unwrap the parenthesis - // upfront such that unsupported IIFE syntax `(function(){}())` becomes `function(){}()`, - // which is supported. - if (ts.isParenthesizedExpression(initializer) && - ts.isCallExpression(initializer.expression)) { - initializer = initializer.expression; - } - return originalGetExpandoInitializer(initializer, isPrototypeAssignment); - }; + (ts as any).getExpandoInitializer = (initializer: ts.Node, + isPrototypeAssignment: boolean): ts.Expression|undefined => { + // If the initializer is a call expression within parenthesis, unwrap the parenthesis + // upfront such that unsupported IIFE syntax `(function(){}())` becomes `function(){}()`, + // which is supported. + if (ts.isParenthesizedExpression(initializer) && ts.isCallExpression(initializer.expression)) { + initializer = initializer.expression; + } + return originalGetExpandoInitializer(initializer, isPrototypeAssignment); + }; return originalGetExpandoInitializer; } @@ -125,16 +124,36 @@ function checkIfExpandoPropertyIsPresent(): boolean { const sourceFile = ts.createSourceFile('test.js', sourceText, ts.ScriptTarget.ES5, true, ts.ScriptKind.JS); const host: ts.CompilerHost = { - getSourceFile(): ts.SourceFile | undefined{return sourceFile;}, - fileExists(): boolean{return true;}, - readFile(): string | undefined{return '';}, + getSourceFile(): ts.SourceFile | + undefined { + return sourceFile; + }, + fileExists(): boolean { + return true; + }, + readFile(): string | + undefined { + return ''; + }, writeFile() {}, - getDefaultLibFileName(): string{return '';}, - getCurrentDirectory(): string{return '';}, - getDirectories(): string[]{return [];}, - getCanonicalFileName(fileName: string): string{return fileName;}, - useCaseSensitiveFileNames(): boolean{return true;}, - getNewLine(): string{return '\n';}, + getDefaultLibFileName(): string { + return ''; + }, + getCurrentDirectory(): string { + return ''; + }, + getDirectories(): string[] { + return []; + }, + getCanonicalFileName(fileName: string): string { + return fileName; + }, + useCaseSensitiveFileNames(): boolean { + return true; + }, + getNewLine(): string { + return '\n'; + }, }; const options = {noResolve: true, noLib: true, noEmit: true, allowJs: true}; const program = ts.createProgram(['test.js'], options, host); diff --git a/packages/compiler-cli/ngcc/src/packages/transformer.ts b/packages/compiler-cli/ngcc/src/packages/transformer.ts index 6ae9785929eef..005482f3f7002 100644 --- a/packages/compiler-cli/ngcc/src/packages/transformer.ts +++ b/packages/compiler-cli/ngcc/src/packages/transformer.ts @@ -7,6 +7,7 @@ */ import * as ts from 'typescript'; +import {ParsedConfiguration} from '../../..'; import {FileSystem} from '../../../src/ngtsc/file_system'; import {TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {DecorationAnalyzer} from '../analysis/decoration_analyzer'; @@ -35,8 +36,7 @@ import {EntryPointBundle} from './entry_point_bundle'; export type TransformResult = { success: true; diagnostics: ts.Diagnostic[]; transformedFiles: FileToWrite[]; -} | -{ +}|{ success: false; diagnostics: ts.Diagnostic[]; }; @@ -63,7 +63,9 @@ export type TransformResult = { * - Some formats may contain multiple "modules" in a single file. */ export class Transformer { - constructor(private fs: FileSystem, private logger: Logger) {} + constructor( + private fs: FileSystem, private logger: Logger, + private tsConfig: ParsedConfiguration|null = null) {} /** * Transform the source (and typings) files of a bundle. @@ -76,8 +78,13 @@ export class Transformer { const reflectionHost = new DelegatingReflectionHost(tsReflectionHost, ngccReflectionHost); // Parse and analyze the files. - const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - moduleWithProvidersAnalyses, diagnostics} = this.analyzeProgram(reflectionHost, bundle); + const { + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + moduleWithProvidersAnalyses, + diagnostics + } = this.analyzeProgram(reflectionHost, bundle); // Bail if the analysis produced any errors. if (hasErrors(diagnostics)) { @@ -146,7 +153,7 @@ export class Transformer { const diagnostics: ts.Diagnostic[] = []; const decorationAnalyzer = new DecorationAnalyzer( this.fs, bundle, reflectionHost, referencesRegistry, - diagnostic => diagnostics.push(diagnostic)); + diagnostic => diagnostics.push(diagnostic), this.tsConfig); const decorationAnalyses = decorationAnalyzer.analyzeProgram(); const moduleWithProvidersAnalyzer = @@ -159,8 +166,13 @@ export class Transformer { const privateDeclarationsAnalyses = privateDeclarationsAnalyzer.analyzeProgram(bundle.src.program); - return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - moduleWithProvidersAnalyses, diagnostics}; + return { + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + moduleWithProvidersAnalyses, + diagnostics + }; } } diff --git a/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts index bdac597093ee2..1ddfeb64fe564 100644 --- a/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/commonjs_rendering_formatter.ts @@ -6,13 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ import {dirname, relative} from 'canonical-path'; -import * as ts from 'typescript'; import MagicString from 'magic-string'; +import * as ts from 'typescript'; + import {Reexport} from '../../../src/ngtsc/imports'; import {Import, ImportManager} from '../../../src/ngtsc/translator'; import {ExportInfo} from '../analysis/private_declarations_analyzer'; import {isRequireCall} from '../host/commonjs_umd_utils'; import {NgccReflectionHost} from '../host/ngcc_host'; + import {Esm5RenderingFormatter} from './esm5_rendering_formatter'; import {stripExtension} from './utils'; diff --git a/packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts b/packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts index 746145f6893c6..91ab5ea398971 100644 --- a/packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts +++ b/packages/compiler-cli/ngcc/src/rendering/dts_renderer.ts @@ -7,20 +7,22 @@ */ import MagicString from 'magic-string'; import * as ts from 'typescript'; + import {FileSystem} from '../../../src/ngtsc/file_system'; import {Reexport} from '../../../src/ngtsc/imports'; import {CompileResult} from '../../../src/ngtsc/transform'; -import {translateType, ImportManager} from '../../../src/ngtsc/translator'; +import {ImportManager, translateType} from '../../../src/ngtsc/translator'; +import {ModuleWithProvidersAnalyses, ModuleWithProvidersInfo} from '../analysis/module_with_providers_analyzer'; +import {ExportInfo, PrivateDeclarationsAnalyses} from '../analysis/private_declarations_analyzer'; import {DecorationAnalyses} from '../analysis/types'; -import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer'; -import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer'; import {IMPORT_PREFIX} from '../constants'; import {NgccReflectionHost} from '../host/ngcc_host'; -import {EntryPointBundle} from '../packages/entry_point_bundle'; import {Logger} from '../logging/logger'; -import {FileToWrite, getImportRewriter} from './utils'; +import {EntryPointBundle} from '../packages/entry_point_bundle'; + import {RenderingFormatter} from './rendering_formatter'; import {renderSourceAndMap} from './source_maps'; +import {FileToWrite, getImportRewriter} from './utils'; /** * A structure that captures information about what needs to be rendered @@ -84,13 +86,14 @@ export class DtsRenderer { const outputText = new MagicString(dtsFile.text); const printer = ts.createPrinter(); const importManager = new ImportManager( - getImportRewriter(this.bundle.dts !.r3SymbolsFile, this.bundle.isCore, false), + getImportRewriter(this.bundle.dts!.r3SymbolsFile, this.bundle.isCore, false), IMPORT_PREFIX); renderInfo.classInfo.forEach(dtsClass => { const endOfClass = dtsClass.dtsDeclaration.getEnd(); dtsClass.compilation.forEach(declaration => { const type = translateType(declaration.type, importManager); + markForEmitAsSingleLine(type); const typeStr = printer.printNode(ts.EmitHint.Unspecified, type, dtsFile); const newStatement = ` static ${declaration.name}: ${typeStr};\n`; outputText.appendRight(endOfClass - 1, newStatement); @@ -128,7 +131,7 @@ export class DtsRenderer { const dtsDeclaration = this.host.getDtsDeclaration(compiledClass.declaration); if (dtsDeclaration) { const dtsFile = dtsDeclaration.getSourceFile(); - const renderInfo = dtsMap.has(dtsFile) ? dtsMap.get(dtsFile) ! : new DtsRenderInfo(); + const renderInfo = dtsMap.has(dtsFile) ? dtsMap.get(dtsFile)! : new DtsRenderInfo(); renderInfo.classInfo.push({dtsDeclaration, compilation: compiledClass.compilation}); // Only add re-exports if the .d.ts tree is overlayed with the .js tree, as re-exports in // ngcc are only used to support deep imports into e.g. commonjs code. For a deep import @@ -149,7 +152,7 @@ export class DtsRenderer { // Capture the ModuleWithProviders functions/methods that need updating if (moduleWithProvidersAnalyses !== null) { moduleWithProvidersAnalyses.forEach((moduleWithProvidersToFix, dtsFile) => { - const renderInfo = dtsMap.has(dtsFile) ? dtsMap.get(dtsFile) ! : new DtsRenderInfo(); + const renderInfo = dtsMap.has(dtsFile) ? dtsMap.get(dtsFile)! : new DtsRenderInfo(); renderInfo.moduleWithProviders = moduleWithProvidersToFix; dtsMap.set(dtsFile, renderInfo); }); @@ -166,9 +169,9 @@ export class DtsRenderer { `The simplest fix for this is to ensure that this class is exported from the package's entry-point.`); } }); - const dtsEntryPoint = this.bundle.dts !.file; + const dtsEntryPoint = this.bundle.dts!.file; const renderInfo = - dtsMap.has(dtsEntryPoint) ? dtsMap.get(dtsEntryPoint) ! : new DtsRenderInfo(); + dtsMap.has(dtsEntryPoint) ? dtsMap.get(dtsEntryPoint)! : new DtsRenderInfo(); renderInfo.privateExports = privateDeclarationsAnalyses; dtsMap.set(dtsEntryPoint, renderInfo); } @@ -176,3 +179,8 @@ export class DtsRenderer { return dtsMap; } } + +function markForEmitAsSingleLine(node: ts.Node) { + ts.setEmitFlags(node, ts.EmitFlags.SingleLine); + ts.forEachChild(node, markForEmitAsSingleLine); +} diff --git a/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts index 0feb106de085a..40a347457858d 100644 --- a/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/esm5_rendering_formatter.ts @@ -25,14 +25,14 @@ export class Esm5RenderingFormatter extends EsmRenderingFormatter { addDefinitions(output: MagicString, compiledClass: CompiledClass, definitions: string): void { const iifeBody = getIifeBody(compiledClass.declaration); if (!iifeBody) { - throw new Error( - `Compiled class declaration is not inside an IIFE: ${compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`); + throw new Error(`Compiled class declaration is not inside an IIFE: ${compiledClass.name} in ${ + compiledClass.declaration.getSourceFile().fileName}`); } const returnStatement = iifeBody.statements.find(ts.isReturnStatement); if (!returnStatement) { - throw new Error( - `Compiled class wrapper IIFE does not have a return statement: ${compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`); + throw new Error(`Compiled class wrapper IIFE does not have a return statement: ${ + compiledClass.name} in ${compiledClass.declaration.getSourceFile().fileName}`); } const insertionPoint = returnStatement.getFullStart(); diff --git a/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts index d2b42d552274d..d2b1cae500f56 100644 --- a/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts @@ -8,17 +8,19 @@ import {Statement} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {relative, dirname, AbsoluteFsPath, absoluteFromSourceFile} from '../../../src/ngtsc/file_system'; + +import {absoluteFromSourceFile, AbsoluteFsPath, dirname, relative} from '../../../src/ngtsc/file_system'; import {NOOP_DEFAULT_IMPORT_RECORDER, Reexport} from '../../../src/ngtsc/imports'; import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator'; import {isDtsPath} from '../../../src/ngtsc/util/src/typescript'; -import {CompiledClass} from '../analysis/types'; -import {NgccReflectionHost, POST_R3_MARKER, PRE_R3_MARKER, SwitchableVariableDeclaration} from '../host/ngcc_host'; import {ModuleWithProvidersInfo} from '../analysis/module_with_providers_analyzer'; import {ExportInfo} from '../analysis/private_declarations_analyzer'; -import {RenderingFormatter, RedundantDecoratorMap} from './rendering_formatter'; -import {stripExtension} from './utils'; +import {CompiledClass} from '../analysis/types'; import {isAssignment} from '../host/esm2015_host'; +import {NgccReflectionHost, POST_R3_MARKER, PRE_R3_MARKER, SwitchableVariableDeclaration} from '../host/ngcc_host'; + +import {RedundantDecoratorMap, RenderingFormatter} from './rendering_formatter'; +import {stripExtension} from './utils'; /** * A RenderingFormatter that works with ECMAScript Module import and export statements. @@ -226,7 +228,8 @@ export class EsmRenderingFormatter implements RenderingFormatter { info.declaration.getEnd(); outputText.appendLeft( insertPoint, - `: ${generateImportString(importManager, '@angular/core', 'ModuleWithProviders')}<${ngModule}>`); + `: ${generateImportString(importManager, '@angular/core', 'ModuleWithProviders')}<${ + ngModule}>`); } }); } @@ -296,7 +299,7 @@ function findStatement(node: ts.Node): ts.Statement|undefined { } function generateImportString( - importManager: ImportManager, importPath: string | null, importName: string) { + importManager: ImportManager, importPath: string|null, importName: string) { const importAs = importPath ? importManager.generateNamedImport(importPath, importName) : null; return importAs ? `${importAs.moduleImport}.${importAs.symbol}` : `${importName}`; } diff --git a/packages/compiler-cli/ngcc/src/rendering/renderer.ts b/packages/compiler-cli/ngcc/src/rendering/renderer.ts index 54480b7df7a4c..033d05530ca71 100644 --- a/packages/compiler-cli/ngcc/src/rendering/renderer.ts +++ b/packages/compiler-cli/ngcc/src/rendering/renderer.ts @@ -8,16 +8,18 @@ import {ConstantPool, Expression, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; + +import {FileSystem} from '../../../src/ngtsc/file_system'; import {ImportManager} from '../../../src/ngtsc/translator'; -import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/types'; import {PrivateDeclarationsAnalyses} from '../analysis/private_declarations_analyzer'; import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer'; +import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/types'; import {IMPORT_PREFIX} from '../constants'; -import {FileSystem} from '../../../src/ngtsc/file_system'; import {NgccReflectionHost} from '../host/ngcc_host'; import {Logger} from '../logging/logger'; import {EntryPointBundle} from '../packages/entry_point_bundle'; -import {RenderingFormatter, RedundantDecoratorMap} from './rendering_formatter'; + +import {RedundantDecoratorMap, RenderingFormatter} from './rendering_formatter'; import {renderSourceAndMap} from './source_maps'; import {FileToWrite, getImportRewriter, stripExtension} from './utils'; @@ -138,11 +140,11 @@ export class Renderer { if (dec.node === null) { return; } - const decoratorArray = dec.node.parent !; + const decoratorArray = dec.node.parent!; if (!decoratorsToRemove.has(decoratorArray)) { decoratorsToRemove.set(decoratorArray, [dec.node]); } else { - decoratorsToRemove.get(decoratorArray) !.push(dec.node); + decoratorsToRemove.get(decoratorArray)!.push(dec.node); } }); }); @@ -160,8 +162,9 @@ export class Renderer { private renderDefinitions( sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager): string { const name = this.host.getInternalNameOfClass(compiledClass.declaration); - const statements: Statement[] = compiledClass.compilation.map( - c => { return createAssignmentStatement(name, c.name, c.initializer); }); + const statements: Statement[] = compiledClass.compilation.map(c => { + return createAssignmentStatement(name, c.name, c.initializer); + }); return this.renderStatements(sourceFile, statements, imports); } diff --git a/packages/compiler-cli/ngcc/src/rendering/rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/rendering_formatter.ts index e97b1065682a4..4cfeadeb05931 100644 --- a/packages/compiler-cli/ngcc/src/rendering/rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/rendering_formatter.ts @@ -8,12 +8,13 @@ import {Statement} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; + import {Reexport} from '../../../src/ngtsc/imports'; import {Import, ImportManager} from '../../../src/ngtsc/translator'; +import {ModuleWithProvidersInfo} from '../analysis/module_with_providers_analyzer'; import {ExportInfo} from '../analysis/private_declarations_analyzer'; import {CompiledClass} from '../analysis/types'; import {SwitchableVariableDeclaration} from '../host/ngcc_host'; -import {ModuleWithProvidersInfo} from '../analysis/module_with_providers_analyzer'; /** * The collected decorators that have become redundant after the compilation diff --git a/packages/compiler-cli/ngcc/src/rendering/source_maps.ts b/packages/compiler-cli/ngcc/src/rendering/source_maps.ts index ff836bdc600d7..3106a8e517bfc 100644 --- a/packages/compiler-cli/ngcc/src/rendering/source_maps.ts +++ b/packages/compiler-cli/ngcc/src/rendering/source_maps.ts @@ -5,14 +5,16 @@ * 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 {SourceMapConverter, fromObject, generateMapFileComment} from 'convert-source-map'; +import {fromObject, generateMapFileComment, SourceMapConverter} from 'convert-source-map'; import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {FileSystem, absoluteFromSourceFile, basename, absoluteFrom} from '../../../src/ngtsc/file_system'; -import {FileToWrite} from './utils'; -import {SourceFileLoader} from '../sourcemaps/source_file_loader'; -import {RawSourceMap} from '../sourcemaps/raw_source_map'; + +import {absoluteFrom, absoluteFromSourceFile, basename, FileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../logging/logger'; +import {RawSourceMap} from '../sourcemaps/raw_source_map'; +import {SourceFileLoader} from '../sourcemaps/source_file_loader'; + +import {FileToWrite} from './utils'; export interface SourceMapInfo { source: string; @@ -34,7 +36,7 @@ export function renderSourceAndMap( {file: generatedPath, source: generatedPath, includeContent: true}); try { - const loader = new SourceFileLoader(fs); + const loader = new SourceFileLoader(fs, logger); const generatedFile = loader.loadSourceFile( generatedPath, generatedContent, {map: generatedMap, mapPath: generatedMapPath}); @@ -53,8 +55,8 @@ export function renderSourceAndMap( ]; } } catch (e) { - logger.error( - `Error when flattening the source-map "${generatedMapPath}" for "${generatedPath}": ${e.toString()}`); + logger.error(`Error when flattening the source-map "${generatedMapPath}" for "${ + generatedPath}": ${e.toString()}`); return [ {path: generatedPath, contents: generatedContent}, {path: generatedMapPath, contents: fromObject(generatedMap).toJSON()}, diff --git a/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts index 4860cda6b86e5..8051f79c15ba0 100644 --- a/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/umd_rendering_formatter.ts @@ -6,17 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ import {dirname, relative} from 'canonical-path'; -import * as ts from 'typescript'; import MagicString from 'magic-string'; +import * as ts from 'typescript'; + +import {Reexport} from '../../../src/ngtsc/imports'; import {Import, ImportManager} from '../../../src/ngtsc/translator'; import {ExportInfo} from '../analysis/private_declarations_analyzer'; import {UmdReflectionHost} from '../host/umd_host'; + import {Esm5RenderingFormatter} from './esm5_rendering_formatter'; import {stripExtension} from './utils'; -import {Reexport} from '../../../src/ngtsc/imports'; -type CommonJsConditional = ts.ConditionalExpression & {whenTrue: ts.CallExpression}; -type AmdConditional = ts.ConditionalExpression & {whenTrue: ts.CallExpression}; +type CommonJsConditional = ts.ConditionalExpression&{whenTrue: ts.CallExpression}; +type AmdConditional = ts.ConditionalExpression&{whenTrue: ts.CallExpression}; /** * A RenderingFormatter that works with UMD files, instead of `import` and `export` statements @@ -24,7 +26,9 @@ type AmdConditional = ts.ConditionalExpression & {whenTrue: ts.CallExpression}; * wrapper function for AMD, CommonJS and global module formats. */ export class UmdRenderingFormatter extends Esm5RenderingFormatter { - constructor(protected umdHost: UmdReflectionHost, isCore: boolean) { super(umdHost, isCore); } + constructor(protected umdHost: UmdReflectionHost, isCore: boolean) { + super(umdHost, isCore); + } /** * Add the imports to the UMD module IIFE. diff --git a/packages/compiler-cli/ngcc/src/rendering/utils.ts b/packages/compiler-cli/ngcc/src/rendering/utils.ts index 29cafaa78599b..9b9d25303ec42 100644 --- a/packages/compiler-cli/ngcc/src/rendering/utils.ts +++ b/packages/compiler-cli/ngcc/src/rendering/utils.ts @@ -24,11 +24,11 @@ export interface FileToWrite { * Create an appropriate ImportRewriter given the parameters. */ export function getImportRewriter( - r3SymbolsFile: ts.SourceFile | null, isCore: boolean, isFlat: boolean): ImportRewriter { + r3SymbolsFile: ts.SourceFile|null, isCore: boolean, isFlat: boolean): ImportRewriter { if (isCore && isFlat) { return new NgccFlatImportRewriter(); } else if (isCore) { - return new R3SymbolsImportRewriter(r3SymbolsFile !.fileName); + return new R3SymbolsImportRewriter(r3SymbolsFile!.fileName); } else { return new NoopImportRewriter(); } diff --git a/packages/compiler-cli/ngcc/src/sourcemaps/segment_marker.ts b/packages/compiler-cli/ngcc/src/sourcemaps/segment_marker.ts index 78d71f1053a0c..2fa738ace33de 100644 --- a/packages/compiler-cli/ngcc/src/sourcemaps/segment_marker.ts +++ b/packages/compiler-cli/ngcc/src/sourcemaps/segment_marker.ts @@ -8,11 +8,11 @@ /** -* A marker that indicates the start of a segment in a mapping. -* -* The end of a segment is indicated by the the first segment-marker of another mapping whose start -* is greater or equal to this one. -*/ + * A marker that indicates the start of a segment in a mapping. + * + * The end of a segment is indicated by the the first segment-marker of another mapping whose start + * is greater or equal to this one. + */ export interface SegmentMarker { readonly line: number; readonly column: number; diff --git a/packages/compiler-cli/ngcc/src/sourcemaps/source_file.ts b/packages/compiler-cli/ngcc/src/sourcemaps/source_file.ts index 839eec56f02fb..f860d162fc726 100644 --- a/packages/compiler-cli/ngcc/src/sourcemaps/source_file.ts +++ b/packages/compiler-cli/ngcc/src/sourcemaps/source_file.ts @@ -6,10 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ import {removeComments, removeMapFileComments} from 'convert-source-map'; -import {SourceMapMappings, SourceMapSegment, decode, encode} from 'sourcemap-codec'; +import {decode, encode, SourceMapMappings, SourceMapSegment} from 'sourcemap-codec'; + import {AbsoluteFsPath, dirname, relative} from '../../../src/ngtsc/file_system'; + import {RawSourceMap} from './raw_source_map'; -import {SegmentMarker, compareSegments, offsetSegment} from './segment_marker'; +import {compareSegments, offsetSegment, SegmentMarker} from './segment_marker'; export function removeSourceMapComments(contents: string): string { return removeMapFileComments(removeComments(contents)).replace(/\n\n$/, '\n'); @@ -77,7 +79,8 @@ export class SourceFile { const sourceMap: RawSourceMap = { version: 3, file: relative(sourcePathDir, this.sourcePath), - sources: sources.map(sf => relative(sourcePathDir, sf.sourcePath)), names, + sources: sources.map(sf => relative(sourcePathDir, sf.sourcePath)), + names, mappings: encode(mappings), sourcesContent: sources.map(sf => sf.contents), }; @@ -302,7 +305,7 @@ export function mergeMappings(generatedSource: SourceFile, ab: Mapping, bc: Mapp * in the `sources` parameter. */ export function parseMappings( - rawMap: RawSourceMap | null, sources: (SourceFile | null)[], + rawMap: RawSourceMap|null, sources: (SourceFile|null)[], generatedSourceStartOfLinePositions: number[]): Mapping[] { if (rawMap === null) { return []; @@ -318,15 +321,15 @@ export function parseMappings( const generatedLineMappings = rawMappings[generatedLine]; for (const rawMapping of generatedLineMappings) { if (rawMapping.length >= 4) { - const originalSource = sources[rawMapping[1] !]; + const originalSource = sources[rawMapping[1]!]; if (originalSource === null || originalSource === undefined) { // the original source is missing so ignore this mapping continue; } const generatedColumn = rawMapping[0]; const name = rawMapping.length === 5 ? rawMap.names[rawMapping[4]] : undefined; - const line = rawMapping[2] !; - const column = rawMapping[3] !; + const line = rawMapping[2]!; + const column = rawMapping[3]!; const generatedSegment: SegmentMarker = { line: generatedLine, column: generatedColumn, @@ -361,7 +364,7 @@ export function extractOriginalSegments(mappings: Mapping[]): Map<SourceFile, Se if (!originalSegments.has(originalSource)) { originalSegments.set(originalSource, []); } - const segments = originalSegments.get(originalSource) !; + const segments = originalSegments.get(originalSource)!; segments.push(mapping.originalSegment); } originalSegments.forEach(segmentMarkers => segmentMarkers.sort(compareSegments)); diff --git a/packages/compiler-cli/ngcc/src/sourcemaps/source_file_loader.ts b/packages/compiler-cli/ngcc/src/sourcemaps/source_file_loader.ts index 76b7966c49f8f..52a581c8bf179 100644 --- a/packages/compiler-cli/ngcc/src/sourcemaps/source_file_loader.ts +++ b/packages/compiler-cli/ngcc/src/sourcemaps/source_file_loader.ts @@ -6,7 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ import {commentRegex, fromComment, mapFileCommentRegex} from 'convert-source-map'; -import {AbsoluteFsPath, FileSystem, absoluteFrom} from '../../../src/ngtsc/file_system'; + +import {absoluteFrom, AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {Logger} from '../logging/logger'; + import {RawSourceMap} from './raw_source_map'; import {SourceFile} from './source_file'; @@ -20,61 +23,70 @@ import {SourceFile} from './source_file'; * mappings to other `SourceFile` objects as necessary. */ export class SourceFileLoader { - constructor(private fs: FileSystem) {} + private currentPaths: AbsoluteFsPath[] = []; + + constructor(private fs: FileSystem, private logger: Logger) {} /** * Load a source file, compute its source map, and recursively load any referenced source files. * * @param sourcePath The path to the source file to load. - * @param contents The contents of the source file to load (if known). - * The contents may be known because the source file was inlined into a source map. + * @param contents The contents of the source file to load. + * @param mapAndPath The raw source-map and the path to the source-map file. + * @returns a SourceFile object created from the `contents` and provided source-map info. + */ + loadSourceFile(sourcePath: AbsoluteFsPath, contents: string, mapAndPath: MapAndPath): SourceFile; + /** + * The overload used internally to load source files referenced in a source-map. + * + * In this case there is no guarantee that it will return a non-null SourceMap. + * + * @param sourcePath The path to the source file to load. + * @param contents The contents of the source file to load, if provided inline. * If it is not known the contents will be read from the file at the `sourcePath`. - * @param mapAndPath The raw source-map and the path to the source-map file, if known. - * @param previousPaths An internal parameter used for cyclic dependency tracking. + * @param mapAndPath The raw source-map and the path to the source-map file. + * * @returns a SourceFile if the content for one was provided or able to be loaded from disk, * `null` otherwise. */ - loadSourceFile(sourcePath: AbsoluteFsPath, contents: string, mapAndPath: MapAndPath): SourceFile; - loadSourceFile(sourcePath: AbsoluteFsPath, contents: string|null): SourceFile|null; - loadSourceFile(sourcePath: AbsoluteFsPath): SourceFile|null; - loadSourceFile( - sourcePath: AbsoluteFsPath, contents: string|null, mapAndPath: null, - previousPaths: AbsoluteFsPath[]): SourceFile|null; + loadSourceFile(sourcePath: AbsoluteFsPath, contents?: string|null, mapAndPath?: null): SourceFile + |null; loadSourceFile( - sourcePath: AbsoluteFsPath, contents: string|null = null, mapAndPath: MapAndPath|null = null, - previousPaths: AbsoluteFsPath[] = []): SourceFile|null { - if (contents === null) { - if (!this.fs.exists(sourcePath)) { - return null; + sourcePath: AbsoluteFsPath, contents: string|null = null, + mapAndPath: MapAndPath|null = null): SourceFile|null { + const previousPaths = this.currentPaths.slice(); + try { + if (contents === null) { + if (!this.fs.exists(sourcePath)) { + return null; + } + contents = this.readSourceFile(sourcePath); } - // Track source file paths if we have loaded them from disk so that we don't get into an - // infinite recursion - if (previousPaths.includes(sourcePath)) { - throw new Error( - `Circular source file mapping dependency: ${previousPaths.join(' -> ')} -> ${sourcePath}`); + // If not provided try to load the source map based on the source itself + if (mapAndPath === null) { + mapAndPath = this.loadSourceMap(sourcePath, contents); } - previousPaths = previousPaths.concat([sourcePath]); - - contents = this.fs.readFile(sourcePath); - } - // If not provided try to load the source map based on the source itself - if (mapAndPath === null) { - mapAndPath = this.loadSourceMap(sourcePath, contents); - } + let map: RawSourceMap|null = null; + let inline = true; + let sources: (SourceFile|null)[] = []; + if (mapAndPath !== null) { + const basePath = mapAndPath.mapPath || sourcePath; + sources = this.processSources(basePath, mapAndPath.map); + map = mapAndPath.map; + inline = mapAndPath.mapPath === null; + } - let map: RawSourceMap|null = null; - let inline = true; - let sources: (SourceFile | null)[] = []; - if (mapAndPath !== null) { - const basePath = mapAndPath.mapPath || sourcePath; - sources = this.processSources(basePath, mapAndPath.map, previousPaths); - map = mapAndPath.map; - inline = mapAndPath.mapPath === null; + return new SourceFile(sourcePath, contents, map, inline, sources); + } catch (e) { + this.logger.warn( + `Unable to fully load ${sourcePath} for source-map flattening: ${e.message}`); + return null; + } finally { + // We are finished with this recursion so revert the paths being tracked + this.currentPaths = previousPaths; } - - return new SourceFile(sourcePath, contents, map, inline, sources); } /** @@ -87,7 +99,7 @@ export class SourceFileLoader { private loadSourceMap(sourcePath: AbsoluteFsPath, contents: string): MapAndPath|null { const inline = commentRegex.exec(contents); if (inline !== null) { - return {map: fromComment(inline.pop() !).sourcemap, mapPath: null}; + return {map: fromComment(inline.pop()!).sourcemap, mapPath: null}; } const external = mapFileCommentRegex.exec(contents); @@ -95,15 +107,17 @@ export class SourceFileLoader { try { const fileName = external[1] || external[2]; const externalMapPath = this.fs.resolve(this.fs.dirname(sourcePath), fileName); - return {map: this.loadRawSourceMap(externalMapPath), mapPath: externalMapPath}; - } catch { + return {map: this.readRawSourceMap(externalMapPath), mapPath: externalMapPath}; + } catch (e) { + this.logger.warn( + `Unable to fully load ${sourcePath} for source-map flattening: ${e.message}`); return null; } } const impliedMapPath = absoluteFrom(sourcePath + '.map'); if (this.fs.exists(impliedMapPath)) { - return {map: this.loadRawSourceMap(impliedMapPath), mapPath: impliedMapPath}; + return {map: this.readRawSourceMap(impliedMapPath), mapPath: impliedMapPath}; } return null; @@ -113,24 +127,47 @@ export class SourceFileLoader { * Iterate over each of the "sources" for this source file's source map, recursively loading each * source file and its associated source map. */ - private processSources( - basePath: AbsoluteFsPath, map: RawSourceMap, - previousPaths: AbsoluteFsPath[]): (SourceFile|null)[] { + private processSources(basePath: AbsoluteFsPath, map: RawSourceMap): (SourceFile|null)[] { const sourceRoot = this.fs.resolve(this.fs.dirname(basePath), map.sourceRoot || ''); return map.sources.map((source, index) => { const path = this.fs.resolve(sourceRoot, source); const content = map.sourcesContent && map.sourcesContent[index] || null; - return this.loadSourceFile(path, content, null, previousPaths); + return this.loadSourceFile(path, content, null); }); } + /** + * Load the contents of the source file from disk. + * + * @param sourcePath The path to the source file. + */ + private readSourceFile(sourcePath: AbsoluteFsPath): string { + this.trackPath(sourcePath); + return this.fs.readFile(sourcePath); + } + /** * Load the source map from the file at `mapPath`, parsing its JSON contents into a `RawSourceMap` * object. + * + * @param mapPath The path to the source-map file. */ - private loadRawSourceMap(mapPath: AbsoluteFsPath): RawSourceMap { + private readRawSourceMap(mapPath: AbsoluteFsPath): RawSourceMap { + this.trackPath(mapPath); return JSON.parse(this.fs.readFile(mapPath)); } + + /** + * Track source file paths if we have loaded them from disk so that we don't get into an infinite + * recursion. + */ + private trackPath(path: AbsoluteFsPath): void { + if (this.currentPaths.includes(path)) { + throw new Error( + `Circular source file mapping dependency: ${this.currentPaths.join(' -> ')} -> ${path}`); + } + this.currentPaths.push(path); + } } /** A small helper structure that is returned from `loadSourceMap()`. */ diff --git a/packages/compiler-cli/ngcc/src/utils.ts b/packages/compiler-cli/ngcc/src/utils.ts index fc633164f8924..507a880ff2b3e 100644 --- a/packages/compiler-cli/ngcc/src/utils.ts +++ b/packages/compiler-cli/ngcc/src/utils.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {AbsoluteFsPath, FileSystem, absoluteFrom} from '../../src/ngtsc/file_system'; + +import {absoluteFrom, AbsoluteFsPath, FileSystem, isRooted} from '../../src/ngtsc/file_system'; import {KnownDeclaration} from '../../src/ngtsc/reflection'; /** @@ -37,11 +38,11 @@ export function getOriginalSymbol(checker: ts.TypeChecker): (symbol: ts.Symbol) }; } -export function isDefined<T>(value: T | undefined | null): value is T { +export function isDefined<T>(value: T|undefined|null): value is T { return (value !== undefined) && (value !== null); } -export function getNameText(name: ts.PropertyName | ts.BindingName): string { +export function getNameText(name: ts.PropertyName|ts.BindingName): string { return ts.isIdentifier(name) || ts.isLiteralExpression(name) ? name.text : name.getText(); } @@ -76,18 +77,14 @@ export function hasNameIdentifier(declaration: ts.Declaration): declaration is t return namedDeclaration.name !== undefined && ts.isIdentifier(namedDeclaration.name); } -export type PathMappings = { - baseUrl: string, - paths: {[key: string]: string[]} -}; - /** * Test whether a path is "relative". * - * Relative paths start with `/`, `./` or `../`; or are simply `.` or `..`. + * Relative paths start with `/`, `./` or `../` (or the Windows equivalents); or are simply `.` or + * `..`. */ export function isRelativePath(path: string): boolean { - return /^\/|^\.\.?($|\/)/.test(path); + return isRooted(path) || /^\.\.?(\/|\\|$)/.test(path); } /** @@ -111,10 +108,12 @@ export class FactoryMap<K, V> { this.internalMap.set(key, this.factory(key)); } - return this.internalMap.get(key) !; + return this.internalMap.get(key)!; } - set(key: K, value: V): void { this.internalMap.set(key, value); } + set(key: K, value: V): void { + this.internalMap.set(key, value); + } } /** diff --git a/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts b/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts index 3bf1861955ea3..12c1ccfcea0d5 100644 --- a/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts +++ b/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts @@ -5,15 +5,16 @@ * 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 {AbsoluteFsPath, FileSystem, PathSegment, absoluteFrom} from '../../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, PathSegment} from '../../../../src/ngtsc/file_system'; import {cleanPackageJson} from '../../packages/build_marker'; import {NGCC_BACKUP_EXTENSION} from '../in_place_file_writer'; import {NGCC_DIRECTORY} from '../new_entry_point_file_writer'; + import {isLocalDirectory} from './utils'; /** -* Implement this interface to extend the cleaning strategies of the `PackageCleaner`. -*/ + * Implement this interface to extend the cleaning strategies of the `PackageCleaner`. + */ export interface CleaningStrategy { canClean(path: AbsoluteFsPath, basename: PathSegment): boolean; clean(path: AbsoluteFsPath, basename: PathSegment): void; @@ -44,7 +45,9 @@ export class NgccDirectoryCleaner implements CleaningStrategy { canClean(path: AbsoluteFsPath, basename: PathSegment): boolean { return basename === NGCC_DIRECTORY && isLocalDirectory(this.fs, path); } - clean(path: AbsoluteFsPath, _basename: PathSegment): void { this.fs.removeDeep(path); } + clean(path: AbsoluteFsPath, _basename: PathSegment): void { + this.fs.removeDeep(path); + } } /** diff --git a/packages/compiler-cli/ngcc/src/writing/in_place_file_writer.ts b/packages/compiler-cli/ngcc/src/writing/in_place_file_writer.ts index ab390f2c6feda..52c6022053ddc 100644 --- a/packages/compiler-cli/ngcc/src/writing/in_place_file_writer.ts +++ b/packages/compiler-cli/ngcc/src/writing/in_place_file_writer.ts @@ -5,11 +5,12 @@ * 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 {FileSystem, absoluteFrom, dirname} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, dirname, FileSystem} from '../../../src/ngtsc/file_system'; import {Logger} from '../logging/logger'; import {EntryPointJsonProperty} from '../packages/entry_point'; import {EntryPointBundle} from '../packages/entry_point_bundle'; import {FileToWrite} from '../rendering/utils'; + import {FileWriter} from './file_writer'; export const NGCC_BACKUP_EXTENSION = '.__ivy_ngcc_bak'; @@ -37,7 +38,9 @@ export class InPlaceFileWriter implements FileWriter { `Tried to overwrite ${backPath} with an ngcc back up file, which is disallowed.`); } else { this.logger.error( - `Tried to write ${backPath} with an ngcc back up file but it already exists so not writing, nor backing up, ${file.path}.\n` + + `Tried to write ${ + backPath} with an ngcc back up file but it already exists so not writing, nor backing up, ${ + file.path}.\n` + `This error may be because two or more entry-points overlap and ngcc has been asked to process some files more than once.\n` + `You should check other entry-points in this package and set up a config to ignore any that you are not using.`); } diff --git a/packages/compiler-cli/ngcc/src/writing/new_entry_point_file_writer.ts b/packages/compiler-cli/ngcc/src/writing/new_entry_point_file_writer.ts index 0d78265f3a0e0..40c8fa2c7335f 100644 --- a/packages/compiler-cli/ngcc/src/writing/new_entry_point_file_writer.ts +++ b/packages/compiler-cli/ngcc/src/writing/new_entry_point_file_writer.ts @@ -6,7 +6,7 @@ * 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 {AbsoluteFsPath, FileSystem, absoluteFromSourceFile, dirname, join, relative} from '../../../src/ngtsc/file_system'; +import {absoluteFromSourceFile, AbsoluteFsPath, dirname, FileSystem, join, relative} from '../../../src/ngtsc/file_system'; import {isDtsPath} from '../../../src/ngtsc/util/src/typescript'; import {Logger} from '../logging/logger'; import {EntryPoint, EntryPointJsonProperty} from '../packages/entry_point'; @@ -83,8 +83,8 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter { const packageJsonPath = join(entryPoint.path, 'package.json'); // All format properties point to the same format-path. - const oldFormatProp = formatProperties[0] !; - const oldFormatPath = packageJson[oldFormatProp] !; + const oldFormatProp = formatProperties[0]!; + const oldFormatPath = packageJson[oldFormatProp]!; const oldAbsFormatPath = join(entryPoint.path, oldFormatPath); const newAbsFormatPath = join(ngccFolder, relative(entryPoint.package, oldAbsFormatPath)); const newFormatPath = relative(entryPoint.path, newAbsFormatPath); diff --git a/packages/compiler-cli/ngcc/src/writing/package_json_updater.ts b/packages/compiler-cli/ngcc/src/writing/package_json_updater.ts index bfd5295ad439e..bf36458300f6a 100644 --- a/packages/compiler-cli/ngcc/src/writing/package_json_updater.ts +++ b/packages/compiler-cli/ngcc/src/writing/package_json_updater.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteFsPath, FileSystem, dirname} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, dirname, FileSystem} from '../../../src/ngtsc/file_system'; import {JsonObject, JsonValue} from '../packages/entry_point'; export type PackageJsonChange = [string[], JsonValue, PackageJsonPropertyPositioning]; -export type PackageJsonPropertyPositioning = 'unimportant' | 'alphabetic' | {before: string}; +export type PackageJsonPropertyPositioning = 'unimportant'|'alphabetic'|{before: string}; export type WritePackageJsonChangesFn = (changes: PackageJsonChange[], packageJsonPath: AbsoluteFsPath, parsedJson?: JsonObject) => void; diff --git a/packages/compiler-cli/ngcc/test/BUILD.bazel b/packages/compiler-cli/ngcc/test/BUILD.bazel index ccafbae4fdd78..320d64646a116 100644 --- a/packages/compiler-cli/ngcc/test/BUILD.bazel +++ b/packages/compiler-cli/ngcc/test/BUILD.bazel @@ -29,6 +29,7 @@ ts_library( "@npm//magic-string", "@npm//sourcemap-codec", "@npm//typescript", + "@npm//yargs", ], ) diff --git a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts index 12b4b62b21807..014eb81d9a26e 100644 --- a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {FatalDiagnosticError, makeDiagnostic} from '../../../src/ngtsc/diagnostics'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; @@ -21,7 +21,7 @@ import {Migration, MigrationHost} from '../../src/migrations/migration'; import {MockLogger} from '../helpers/mock_logger'; import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils'; -type DecoratorHandlerWithResolve = DecoratorHandler<unknown, unknown, unknown>& { +type DecoratorHandlerWithResolve = DecoratorHandler<unknown, unknown, unknown>&{ resolve: NonNullable<DecoratorHandler<unknown, unknown, unknown>['resolve']>; }; @@ -29,7 +29,9 @@ runInEachFileSystem(() => { describe('DecorationAnalyzer', () => { let _: typeof absoluteFrom; - beforeEach(() => { _ = absoluteFrom; }); + beforeEach(() => { + _ = absoluteFrom; + }); describe('analyzeProgram()', () => { let logs: string[]; @@ -50,8 +52,8 @@ runInEachFileSystem(() => { ]); // Only detect the Component and Directive decorators handler.detect.and.callFake( - (node: ts.Declaration, decorators: Decorator[] | null): DetectResult<unknown>| - undefined => { + (node: ts.Declaration, decorators: Decorator[]|null): DetectResult<unknown>| + undefined => { const className = (node as any).name.text; if (decorators === null) { logs.push(`detect: ${className} (no decorators)`); @@ -93,18 +95,18 @@ runInEachFileSystem(() => { }); // The "test" compilation result is just the name of the decorator being compiled // (suffixed with `(compiled)`) - handler.compile.and.callFake((decl: ts.Declaration, analysis: any) => { - logs.push( - `compile: ${(decl as any).name.text}@${analysis.decoratorName} (resolved: ${analysis.resolved})`); + (handler.compile as any).and.callFake((decl: ts.Declaration, analysis: any) => { + logs.push(`compile: ${(decl as any).name.text}@${analysis.decoratorName} (resolved: ${ + analysis.resolved})`); return `@${analysis.decoratorName} (compiled)`; }); return handler; }; - function setUpAnalyzer( - testFiles: TestFile[], - options: {analyzeError: boolean, - resolveError: boolean} = {analyzeError: false, resolveError: false}) { + function setUpAnalyzer(testFiles: TestFile[], options: { + analyzeError: boolean, + resolveError: boolean + } = {analyzeError: false, resolveError: false}) { logs = []; loadTestFiles(testFiles); loadFakeCore(getFileSystem()); @@ -173,15 +175,15 @@ runInEachFileSystem(() => { it('should return an object containing a reference to the original source file', () => { const testFile = getSourceFileOrError(program, _('/node_modules/test-package/test.js')); - expect(result.get(testFile) !.sourceFile).toBe(testFile); + expect(result.get(testFile)!.sourceFile).toBe(testFile); const otherFile = getSourceFileOrError(program, _('/node_modules/test-package/other.js')); - expect(result.get(otherFile) !.sourceFile).toBe(otherFile); + expect(result.get(otherFile)!.sourceFile).toBe(otherFile); }); it('should call detect on the decorator handlers with each class from the parsed file', () => { expect(testHandler.detect).toHaveBeenCalledTimes(5); - expect(testHandler.detect.calls.allArgs().map(args => args[1])).toEqual([ + expect(testHandler.detect.calls.allArgs().map((args: any[]) => args[1])).toEqual([ null, jasmine.arrayContaining([jasmine.objectContaining({name: 'Component'})]), jasmine.arrayContaining([jasmine.objectContaining({name: 'Directive'})]), @@ -192,20 +194,23 @@ runInEachFileSystem(() => { it('should return an object containing the classes that were analyzed', () => { const file1 = getSourceFileOrError(program, _('/node_modules/test-package/test.js')); - const compiledFile1 = result.get(file1) !; + const compiledFile1 = result.get(file1)!; expect(compiledFile1.compiledClasses.length).toEqual(2); expect(compiledFile1.compiledClasses[0]).toEqual(jasmine.objectContaining({ - name: 'MyComponent', compilation: ['@Component (compiled)'], + name: 'MyComponent', + compilation: ['@Component (compiled)'], } as unknown as CompiledClass)); expect(compiledFile1.compiledClasses[1]).toEqual(jasmine.objectContaining({ - name: 'MyDirective', compilation: ['@Directive (compiled)'], + name: 'MyDirective', + compilation: ['@Directive (compiled)'], } as unknown as CompiledClass)); const file2 = getSourceFileOrError(program, _('/node_modules/test-package/other.js')); - const compiledFile2 = result.get(file2) !; + const compiledFile2 = result.get(file2)!; expect(compiledFile2.compiledClasses.length).toEqual(1); expect(compiledFile2.compiledClasses[0]).toEqual(jasmine.objectContaining({ - name: 'MyOtherComponent', compilation: ['@Component (compiled)'], + name: 'MyOtherComponent', + compilation: ['@Component (compiled)'], } as unknown as CompiledClass)); }); @@ -284,18 +289,18 @@ runInEachFileSystem(() => { () => { const file = getSourceFileOrError(program, _('/node_modules/test-package/component.js')); - const analysis = result.get(file) !; + const analysis = result.get(file)!; expect(analysis).toBeDefined(); const ImportedComponent = - analysis.compiledClasses.find(f => f.name === 'ImportedComponent') !; + analysis.compiledClasses.find(f => f.name === 'ImportedComponent')!; expect(ImportedComponent).toBeDefined(); }); it('should analyze an internally defined component, which is not exported at all', () => { const file = getSourceFileOrError(program, _('/node_modules/test-package/entrypoint.js')); - const analysis = result.get(file) !; + const analysis = result.get(file)!; expect(analysis).toBeDefined(); - const LocalComponent = analysis.compiledClasses.find(f => f.name === 'LocalComponent') !; + const LocalComponent = analysis.compiledClasses.find(f => f.name === 'LocalComponent')!; expect(LocalComponent).toBeDefined(); }); }); @@ -308,31 +313,39 @@ runInEachFileSystem(() => { contents: ` import {Component, NgModule} from '@angular/core'; import {ImportedComponent} from 'other/component'; + import {NestedDependencyComponent} from 'nested/component'; export class LocalComponent {} LocalComponent.decorators = [{type: Component}]; export class MyModule {} MyModule.decorators = [{type: NgModule, args: [{ - declarations: [ImportedComponent, LocalComponent], - exports: [ImportedComponent, LocalComponent], + declarations: [ImportedComponent, NestedDependencyComponent, LocalComponent], + exports: [ImportedComponent, NestedDependencyComponent, LocalComponent], },] }]; ` }, + // Do not define a `.d.ts` file to ensure that the `.js` file will be part of the TS + // program. { - name: _('/node_modules/other/component.js'), + name: _('/node_modules/test-package/node_modules/nested/component.js'), contents: ` import {Component} from '@angular/core'; - export class ImportedComponent {} - ImportedComponent.decorators = [{type: Component}]; + export class NestedDependencyComponent {} + NestedDependencyComponent.decorators = [{type: Component}]; `, isRoot: false, }, + // Do not define a `.d.ts` file to ensure that the `.js` file will be part of the TS + // program. { - name: _('/node_modules/other/component.d.ts'), + name: _('/node_modules/other/component.js'), contents: ` import {Component} from '@angular/core'; - export class ImportedComponent {}` + export class ImportedComponent {} + ImportedComponent.decorators = [{type: Component}]; + `, + isRoot: false, }, ]; @@ -341,7 +354,13 @@ runInEachFileSystem(() => { }); it('should ignore classes from an externally imported file', () => { - const file = program.getSourceFile(_('/node_modules/other/component.js')) !; + const file = program.getSourceFile(_('/node_modules/other/component.js'))!; + expect(result.has(file)).toBe(false); + }); + + it('should ignore classes from a file imported from a nested `node_modules/`', () => { + const file = program.getSourceFile( + _('/node_modules/test-package/node_modules/nested/component.js'))!; expect(result.has(file)).toBe(false); }); }); @@ -427,11 +446,15 @@ runInEachFileSystem(() => { name = 'FakeDecoratorHandler'; precedence = HandlerPrecedence.PRIMARY; - detect(): undefined { throw new Error('detect should not have been called'); } + detect(): undefined { + throw new Error('detect should not have been called'); + } analyze(): AnalysisOutput<unknown> { throw new Error('analyze should not have been called'); } - compile(): CompileResult { throw new Error('compile should not have been called'); } + compile(): CompileResult { + throw new Error('compile should not have been called'); + } } const analyzer = setUpAnalyzer([{ diff --git a/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts b/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts index bceeadc9f30df..3598496c1198a 100644 --- a/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/migration_host_spec.ts @@ -62,7 +62,7 @@ runInEachFileSystem(() => { const {host, compiler} = createMigrationHost({entryPoint, handlers: [handler]}); host.injectSyntheticDecorator(mockClazz, injectedDecorator); - const record = compiler.recordFor(mockClazz) !; + const record = compiler.recordFor(mockClazz)!; expect(record).toBeDefined(); expect(record.traits.length).toBe(1); expect(record.traits[0].detected.decorator).toBe(injectedDecorator); @@ -77,7 +77,7 @@ runInEachFileSystem(() => { const decorator = createComponentDecorator(mockClazz, {selector: 'comp', exportAs: null}); host.injectSyntheticDecorator(mockClazz, decorator); - const record = compiler.recordFor(mockClazz) !; + const record = compiler.recordFor(mockClazz)!; const migratedTrait = record.traits[0]; if (migratedTrait.state !== TraitState.ERRORED) { return fail('Expected migrated class trait to be in an error state'); @@ -92,8 +92,6 @@ runInEachFileSystem(() => { }); }); - - describe('getAllDecorators', () => { it('should include injected decorators', () => { const directiveHandler = new DetectDecoratorHandler('Directive', HandlerPrecedence.WEAK); @@ -120,7 +118,7 @@ runInEachFileSystem(() => { host.injectSyntheticDecorator(myClass, injectedDecorator); - const decorators = host.getAllDecorators(myClass) !; + const decorators = host.getAllDecorators(myClass)!; expect(decorators.length).toBe(2); expect(decorators[0].name).toBe('Directive'); expect(decorators[1].name).toBe('InjectedDecorator'); @@ -143,7 +141,7 @@ runInEachFileSystem(() => { expect(host.isInScope(internalClass)).toBe(true); }); - it('should be false for nodes outside the entry-point', () => { + it('should be false for nodes outside the entry-point (in sibling package)', () => { loadTestFiles([ {name: _('/node_modules/external/index.js'), contents: `export class ExternalClass {}`}, { @@ -163,6 +161,30 @@ runInEachFileSystem(() => { expect(host.isInScope(externalClass)).toBe(false); }); + + it('should be false for nodes outside the entry-point (in nested `node_modules/`)', () => { + loadTestFiles([ + { + name: _('/node_modules/test/index.js'), + contents: ` + export {NestedDependencyClass} from 'nested'; + export class InternalClass {} + `, + }, + { + name: _('/node_modules/test/node_modules/nested/index.js'), + contents: `export class NestedDependencyClass {}`, + }, + ]); + const entryPoint = + makeTestEntryPointBundle('test', 'esm2015', false, [_('/node_modules/test/index.js')]); + const {host} = createMigrationHost({entryPoint, handlers: []}); + const nestedDepClass = getDeclaration( + entryPoint.src.program, _('/node_modules/test/node_modules/nested/index.js'), + 'NestedDependencyClass', isNamedClassDeclaration); + + expect(host.isInScope(nestedDepClass)).toBe(false); + }); }); }); }); @@ -183,9 +205,13 @@ class DetectDecoratorHandler implements DecoratorHandler<unknown, unknown, unkno return {trigger: node, decorator, metadata: {}}; } - analyze(node: ClassDeclaration): AnalysisOutput<unknown> { return {}; } + analyze(node: ClassDeclaration): AnalysisOutput<unknown> { + return {}; + } - compile(node: ClassDeclaration): CompileResult|CompileResult[] { return []; } + compile(node: ClassDeclaration): CompileResult|CompileResult[] { + return []; + } } class DiagnosticProducingHandler implements DecoratorHandler<unknown, unknown, unknown> { @@ -201,5 +227,7 @@ class DiagnosticProducingHandler implements DecoratorHandler<unknown, unknown, u return {diagnostics: [makeDiagnostic(9999, node, 'test diagnostic')]}; } - compile(node: ClassDeclaration): CompileResult|CompileResult[] { return []; } + compile(node: ClassDeclaration): CompileResult|CompileResult[] { + return []; + } } diff --git a/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts index 87e07e9bc8335..a775dd4cc736b 100644 --- a/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/module_with_providers_analyzer_spec.ts @@ -7,8 +7,8 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadTestFiles} from '../../../test/helpers'; import {ModuleWithProvidersAnalyses, ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer'; @@ -333,7 +333,7 @@ runInEachFileSystem(() => { 'test-package', 'esm2015', false, getRootFiles(TEST_PROGRAM), getRootFiles(TEST_DTS_PROGRAM)); program = bundle.src.program; - dtsProgram = bundle.dts !; + dtsProgram = bundle.dts!; const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle.src, dtsProgram); referencesRegistry = new NgccReferencesRegistry(host); @@ -367,8 +367,8 @@ runInEachFileSystem(() => { const libraryModuleDeclaration = getDeclaration( program, absoluteFrom('/node_modules/some-library/index.d.ts'), 'LibraryModule', ts.isClassDeclaration); - expect(declarations.has(externalModuleDeclaration.name !)).toBe(true); - expect(declarations.has(libraryModuleDeclaration.name !)).toBe(false); + expect(declarations.has(externalModuleDeclaration.name!)).toBe(true); + expect(declarations.has(libraryModuleDeclaration.name!)).toBe(false); }); it('should find declarations that have implicit return types', () => { @@ -414,13 +414,12 @@ runInEachFileSystem(() => { analyses: ModuleWithProvidersAnalyses, fileName: AbsoluteFsPath) { const file = getSourceFileOrError(dtsProgram.program, fileName); const analysis = analyses.get(file); - return analysis ? - analysis.map( - info => - [info.declaration.name !.getText(), - (info.ngModule.node as ts.ClassDeclaration).name !.getText(), - info.ngModule.viaModule]) : - []; + return analysis ? analysis.map( + info => + [info.declaration.name!.getText(), + (info.ngModule.node as ts.ClassDeclaration).name!.getText(), + info.ngModule.viaModule]) : + []; } }); }); @@ -536,7 +535,7 @@ runInEachFileSystem(() => { 'test-package', 'esm2015', false, getRootFiles(TEST_PROGRAM), getRootFiles(TEST_DTS_PROGRAM)); const program = bundle.src.program; - const dtsProgram = bundle.dts !; + const dtsProgram = bundle.dts!; const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle.src, dtsProgram); const referencesRegistry = new NgccReferencesRegistry(host); @@ -558,9 +557,9 @@ runInEachFileSystem(() => { const libraryModuleDeclaration = getDeclaration( program, absoluteFrom('/node_modules/some-library/index.d.ts'), 'LibraryModule', ts.isClassDeclaration); - expect(declarations.has(explicitInternalModuleDeclaration.name !)).toBe(true); - expect(declarations.has(externalModuleDeclaration.name !)).toBe(true); - expect(declarations.has(libraryModuleDeclaration.name !)).toBe(false); + expect(declarations.has(explicitInternalModuleDeclaration.name!)).toBe(true); + expect(declarations.has(externalModuleDeclaration.name!)).toBe(true); + expect(declarations.has(libraryModuleDeclaration.name!)).toBe(false); }); it('should track references even when typings have already been processed', () => { @@ -586,9 +585,9 @@ runInEachFileSystem(() => { const libraryModuleDeclaration = getDeclaration( program, absoluteFrom('/node_modules/some-library/index.d.ts'), 'LibraryModule', ts.isClassDeclaration); - expect(declarations.has(explicitInternalModuleDeclaration.name !)).toBe(true); - expect(declarations.has(externalModuleDeclaration.name !)).toBe(true); - expect(declarations.has(libraryModuleDeclaration.name !)).toBe(false); + expect(declarations.has(explicitInternalModuleDeclaration.name!)).toBe(true); + expect(declarations.has(externalModuleDeclaration.name!)).toBe(true); + expect(declarations.has(libraryModuleDeclaration.name!)).toBe(false); }); }); }); diff --git a/packages/compiler-cli/ngcc/test/analysis/ngcc_trait_compiler_spec.ts b/packages/compiler-cli/ngcc/test/analysis/ngcc_trait_compiler_spec.ts index c164a76a5ea74..776be92f05afe 100644 --- a/packages/compiler-cli/ngcc/test/analysis/ngcc_trait_compiler_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/ngcc_trait_compiler_spec.ts @@ -91,7 +91,7 @@ runInEachFileSystem(() => { const record = compiler.recordFor(mockClazz); expect(record).toBeDefined(); - expect(record !.traits.length).toBe(1); + expect(record!.traits.length).toBe(1); }); it('should add a new trait to an existing class record', () => { @@ -118,11 +118,11 @@ runInEachFileSystem(() => { compiler.analyzeFile(entryPoint.src.file); compiler.injectSyntheticDecorator(myClass, injectedDecorator); - const record = compiler.recordFor(myClass) !; + const record = compiler.recordFor(myClass)!; expect(record).toBeDefined(); expect(record.traits.length).toBe(2); - expect(record.traits[0].detected.decorator !.name).toBe('Directive'); - expect(record.traits[1].detected.decorator !.name).toBe('InjectedDecorator'); + expect(record.traits[0].detected.decorator!.name).toBe('Directive'); + expect(record.traits[1].detected.decorator!.name).toBe('InjectedDecorator'); }); it('should not add a weak handler when a primary handler already exists', () => { @@ -150,10 +150,10 @@ runInEachFileSystem(() => { compiler.injectSyntheticDecorator(myClass, injectedDecorator); - const record = compiler.recordFor(myClass) !; + const record = compiler.recordFor(myClass)!; expect(record).toBeDefined(); expect(record.traits.length).toBe(1); - expect(record.traits[0].detected.decorator !.name).toBe('Directive'); + expect(record.traits[0].detected.decorator!.name).toBe('Directive'); }); it('should replace an existing weak handler when injecting a primary handler', () => { @@ -181,10 +181,10 @@ runInEachFileSystem(() => { compiler.injectSyntheticDecorator(myClass, injectedDecorator); - const record = compiler.recordFor(myClass) !; + const record = compiler.recordFor(myClass)!; expect(record).toBeDefined(); expect(record.traits.length).toBe(1); - expect(record.traits[0].detected.decorator !.name).toBe('InjectedDecorator'); + expect(record.traits[0].detected.decorator!.name).toBe('InjectedDecorator'); }); it('should produce an error when a primary handler is added when a primary handler is already present', @@ -214,12 +214,11 @@ runInEachFileSystem(() => { compiler.injectSyntheticDecorator(myClass, injectedDecorator); - const record = compiler.recordFor(myClass) !; + const record = compiler.recordFor(myClass)!; expect(record).toBeDefined(); expect(record.metaDiagnostics).toBeDefined(); - expect(record.metaDiagnostics !.length).toBe(1); - expect(record.metaDiagnostics ![0].code) - .toBe(ngErrorCode(ErrorCode.DECORATOR_COLLISION)); + expect(record.metaDiagnostics!.length).toBe(1); + expect(record.metaDiagnostics![0].code).toBe(ngErrorCode(ErrorCode.DECORATOR_COLLISION)); expect(record.traits.length).toBe(0); }); @@ -233,7 +232,7 @@ runInEachFileSystem(() => { const decorator = createComponentDecorator(mockClazz, {selector: 'comp', exportAs: null}); compiler.injectSyntheticDecorator(mockClazz, decorator); - const record = compiler.recordFor(mockClazz) !; + const record = compiler.recordFor(mockClazz)!; const migratedTrait = record.traits[0]; if (migratedTrait.state !== TraitState.ERRORED) { return fail('Expected migrated class trait to be in an error state'); @@ -286,13 +285,12 @@ runInEachFileSystem(() => { compiler.injectSyntheticDecorator(myClass, injectedDecorator); - const decorators = compiler.getAllDecorators(myClass) !; + const decorators = compiler.getAllDecorators(myClass)!; expect(decorators.length).toBe(2); expect(decorators[0].name).toBe('Directive'); expect(decorators[1].name).toBe('InjectedDecorator'); }); }); - }); }); @@ -302,7 +300,7 @@ class TestHandler implements DecoratorHandler<unknown, unknown, unknown> { precedence = HandlerPrecedence.PRIMARY; detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<unknown>|undefined { - this.log.push(`${this.name}:detect:${node.name.text}:${decorators !.map(d => d.name)}`); + this.log.push(`${this.name}:detect:${node.name.text}:${decorators!.map(d => d.name)}`); return undefined; } diff --git a/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyzer_spec.ts index f31e9c003c472..ec9ed520bc68f 100644 --- a/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyzer_spec.ts @@ -7,8 +7,8 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {absoluteFrom, AbsoluteFsPath} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {Reference} from '../../../src/ngtsc/imports'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadTestFiles} from '../../../test/helpers/src/mock_file_loading'; @@ -192,6 +192,6 @@ runInEachFileSystem(() => { program: ts.Program, registry: NgccReferencesRegistry, fileName: AbsoluteFsPath, componentName: string) { const declaration = getDeclaration(program, fileName, componentName, ts.isClassDeclaration); - registry.add(null !, new Reference(declaration)); + registry.add(null!, new Reference(declaration)); } }); diff --git a/packages/compiler-cli/ngcc/test/analysis/references_registry_spec.ts b/packages/compiler-cli/ngcc/test/analysis/references_registry_spec.ts index 73e2d542b1bf3..83d56e5a9a8e6 100644 --- a/packages/compiler-cli/ngcc/test/analysis/references_registry_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/references_registry_spec.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; import {absoluteFrom} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {Reference} from '../../../src/ngtsc/imports'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; import {TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; @@ -44,19 +44,19 @@ runInEachFileSystem(() => { getDeclaration(program, indexPath, 'someFunction', ts.isFunctionDeclaration); const someVariableDecl = getDeclaration(program, indexPath, 'someVariable', ts.isVariableDeclaration); - const testArrayExpression = testArrayDeclaration.initializer !; + const testArrayExpression = testArrayDeclaration.initializer!; const reflectionHost = new TypeScriptReflectionHost(checker); const evaluator = new PartialEvaluator(reflectionHost, checker, /* dependencyTracker */ null); const registry = new NgccReferencesRegistry(reflectionHost); const references = (evaluator.evaluate(testArrayExpression) as any[]).filter(isReference); - registry.add(null !, ...references); + registry.add(null!, ...references); const map = registry.getDeclarationMap(); expect(map.size).toEqual(2); - expect(map.get(someClassDecl.name !) !.node).toBe(someClassDecl); - expect(map.get(someFunctionDecl.name !) !.node).toBe(someFunctionDecl); + expect(map.get(someClassDecl.name!)!.node).toBe(someClassDecl); + expect(map.get(someFunctionDecl.name!)!.node).toBe(someFunctionDecl); expect(map.has(someVariableDecl.name as ts.Identifier)).toBe(false); }); }); diff --git a/packages/compiler-cli/ngcc/test/analysis/switch_marker_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/switch_marker_analyzer_spec.ts index b67ab259b8479..403877d50c80e 100644 --- a/packages/compiler-cli/ngcc/test/analysis/switch_marker_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/switch_marker_analyzer_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {absoluteFrom, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; @@ -15,7 +15,6 @@ import {makeTestEntryPointBundle} from '../helpers/utils'; runInEachFileSystem(() => { describe('SwitchMarkerAnalyzer', () => { - let _: typeof absoluteFrom; let TEST_PROGRAM: TestFile[]; @@ -25,45 +24,53 @@ runInEachFileSystem(() => { { name: _('/node_modules/test/entrypoint.js'), contents: ` - import {a} from './a'; - import {b} from './b'; - import {x} from '../other/x'; - ` + import {a} from './a'; + import {b} from './b'; + import {x} from '../other/x'; + import {e} from 'nested/e'; + `, }, { name: _('/node_modules/test/a.js'), contents: ` - import {c} from './c'; - export const a = 1; - ` + import {c} from './c'; + export const a = 1; + `, }, { name: _('/node_modules/test/b.js'), contents: ` - export const b = 42; - var factoryB = factory__PRE_R3__; - ` + export const b = 42; + var factoryB = factory__PRE_R3__; + `, }, { name: _('/node_modules/test/c.js'), contents: ` - export const c = 'So long, and thanks for all the fish!'; - var factoryC = factory__PRE_R3__; - var factoryD = factory__PRE_R3__; - ` + export const c = 'So long, and thanks for all the fish!'; + var factoryC = factory__PRE_R3__; + var factoryD = factory__PRE_R3__; + `, + }, + { + name: _('/node_modules/test/node_modules/nested/e.js'), + contents: ` + export const e = 1337; + var factoryE = factory__PRE_R3__; + `, }, { name: _('/node_modules/other/x.js'), contents: ` - export const x = 3.142; - var factoryX = factory__PRE_R3__; - ` + export const x = 3.142; + var factoryX = factory__PRE_R3__; + `, }, { name: _('/node_modules/other/x.d.ts'), contents: ` - export const x: number; - ` + export const x: number; + `, }, ]; }); @@ -87,14 +94,14 @@ runInEachFileSystem(() => { expect(analysis.has(entrypoint)).toBe(false); expect(analysis.has(a)).toBe(false); expect(analysis.has(b)).toBe(true); - expect(analysis.get(b) !.sourceFile).toBe(b); - expect(analysis.get(b) !.declarations.map(decl => decl.getText())).toEqual([ + expect(analysis.get(b)!.sourceFile).toBe(b); + expect(analysis.get(b)!.declarations.map(decl => decl.getText())).toEqual([ 'factoryB = factory__PRE_R3__' ]); expect(analysis.has(c)).toBe(true); - expect(analysis.get(c) !.sourceFile).toBe(c); - expect(analysis.get(c) !.declarations.map(decl => decl.getText())).toEqual([ + expect(analysis.get(c)!.sourceFile).toBe(c); + expect(analysis.get(c)!.declarations.map(decl => decl.getText())).toEqual([ 'factoryC = factory__PRE_R3__', 'factoryD = factory__PRE_R3__', ]); @@ -112,6 +119,19 @@ runInEachFileSystem(() => { const x = getSourceFileOrError(program, _('/node_modules/other/x.js')); expect(analysis.has(x)).toBe(false); }); + + it('should ignore files that are inside the package\'s `node_modules/`', () => { + loadTestFiles(TEST_PROGRAM); + const bundle = makeTestEntryPointBundle( + 'test', 'esm2015', false, [_('/node_modules/test/entrypoint.js')]); + const program = bundle.src.program; + const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle.src); + const analyzer = new SwitchMarkerAnalyzer(host, bundle.entryPoint.package); + const analysis = analyzer.analyzeProgram(program); + + const x = getSourceFileOrError(program, _('/node_modules/test/node_modules/nested/e.js')); + expect(analysis.has(x)).toBe(false); + }); }); }); }); diff --git a/packages/compiler-cli/ngcc/test/analysis/util_spec.ts b/packages/compiler-cli/ngcc/test/analysis/util_spec.ts index fd6ee82409c33..048deb0efe651 100644 --- a/packages/compiler-cli/ngcc/test/analysis/util_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/util_spec.ts @@ -12,20 +12,36 @@ import {isWithinPackage} from '../../src/analysis/util'; runInEachFileSystem(() => { describe('isWithinPackage', () => { + let _: typeof absoluteFrom; + + beforeEach(() => _ = absoluteFrom); + it('should return true if the source-file is contained in the package', () => { - const _ = absoluteFrom; + const packagePath = _('/node_modules/test'); const file = ts.createSourceFile(_('/node_modules/test/src/index.js'), '', ts.ScriptTarget.ES2015); - const packagePath = _('/node_modules/test'); expect(isWithinPackage(packagePath, file)).toBe(true); }); it('should return false if the source-file is not contained in the package', () => { - const _ = absoluteFrom; + const packagePath = _('/node_modules/test'); const file = ts.createSourceFile(_('/node_modules/other/src/index.js'), '', ts.ScriptTarget.ES2015); - const packagePath = _('/node_modules/test'); expect(isWithinPackage(packagePath, file)).toBe(false); }); + + it('should return false if the source-file is inside the package\'s `node_modules/`', () => { + const packagePath = _('/node_modules/test'); + + // An external file inside the package's `node_modules/`. + const file1 = ts.createSourceFile( + _('/node_modules/test/node_modules/other/src/index.js'), '', ts.ScriptTarget.ES2015); + expect(isWithinPackage(packagePath, file1)).toBe(false); + + // An internal file starting with `node_modules`. + const file2 = ts.createSourceFile( + _('/node_modules/test/node_modules_optimizer.js'), '', ts.ScriptTarget.ES2015); + expect(isWithinPackage(packagePath, file2)).toBe(true); + }); }); }); diff --git a/packages/compiler-cli/ngcc/test/dependencies/commonjs_dependency_host_spec.ts b/packages/compiler-cli/ngcc/test/dependencies/commonjs_dependency_host_spec.ts index e0966c775e459..9244efe96da1b 100644 --- a/packages/compiler-cli/ngcc/test/dependencies/commonjs_dependency_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/dependencies/commonjs_dependency_host_spec.ts @@ -387,7 +387,7 @@ runInEachFileSystem(() => { reExportsWithoutRequire?: string[]; } - function commonJs(importsPerType: ImportsPerType | string[], exportNames: string[] = []): string { + function commonJs(importsPerType: ImportsPerType|string[], exportNames: string[] = []): string { if (Array.isArray(importsPerType)) { importsPerType = {varDeclaration: importsPerType}; } @@ -414,8 +414,9 @@ runInEachFileSystem(() => { } = importsPerType; // var foo = require('...'); - importsOfTypeVarDeclaration.forEach( - p => { importStatements.push(`var ${pathToVarName(p)} = require('${p}');`); }); + importsOfTypeVarDeclaration.forEach(p => { + importStatements.push(`var ${pathToVarName(p)} = require('${p}');`); + }); // var foo = require('...'), bar = require('...'); importsOfTypeVarDeclarations.forEach(pp => { @@ -424,8 +425,9 @@ runInEachFileSystem(() => { }); // exports.foo = require('...'); - importsOfTypePropAssignment.forEach( - p => { importStatements.push(`exports.${pathToVarName(p)} = require('${p}');`); }); + importsOfTypePropAssignment.forEach(p => { + importStatements.push(`exports.${pathToVarName(p)} = require('${p}');`); + }); // module.exports = {foo: require('...')}; const propAssignments = @@ -434,15 +436,19 @@ runInEachFileSystem(() => { importStatements.push(`module.exports = {${propAssignments}\n};`); // require('...'); - importsOfTypeForSideEffects.forEach(p => { importStatements.push(`require('${p}');`); }); + importsOfTypeForSideEffects.forEach(p => { + importStatements.push(`require('${p}');`); + }); // __export(require('...')); - importsOfTypeReExportsWithEmittedHelper.forEach( - p => { importStatements.push(`__export(require('${p}'));`); }); + importsOfTypeReExportsWithEmittedHelper.forEach(p => { + importStatements.push(`__export(require('${p}'));`); + }); // tslib_1.__exportStar(require('...'), exports); - importsOfTypeReExportsWithImportedHelper.forEach( - p => { importStatements.push(`tslib_1.__exportStar(require('${p}'), exports);`); }); + importsOfTypeReExportsWithImportedHelper.forEach(p => { + importStatements.push(`tslib_1.__exportStar(require('${p}'), exports);`); + }); // var foo = require('...'); // __export(foo); diff --git a/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts b/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts index 65ac921e24ade..d418618d191e6 100644 --- a/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts +++ b/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts @@ -8,9 +8,9 @@ import {DepGraph} from 'dependency-graph'; -import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, relativeFrom} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relativeFrom} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {DependencyInfo} from '../../src/dependencies/dependency_host'; +import {DependencyInfo, EntryPointWithDependencies} from '../../src/dependencies/dependency_host'; import {DependencyResolver, SortedEntryPointsInfo} from '../../src/dependencies/dependency_resolver'; import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host'; import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host'; @@ -131,7 +131,8 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third])); expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]); }); @@ -144,7 +145,8 @@ runInEachFileSystem(() => { [_('/first/index.d.ts')]: {resolved: [], missing: [_('/missing')]}, [_('/second/sub/index.d.ts')]: {resolved: [], missing: []}, })); - const result = resolver.sortEntryPointsByDependency([first, second]); + const result = + resolver.sortEntryPointsByDependency(getEntryPointsWithDeps(resolver, [first, second])); expect(result.entryPoints).toEqual([second]); expect(result.invalidEntryPoints).toEqual([ {entryPoint: first, missingDependencies: [_('/missing')]}, @@ -163,7 +165,8 @@ runInEachFileSystem(() => { [_('/third/index.d.ts')]: {resolved: [], missing: []}, })); // Note that we will process `first` before `second`, which has the missing dependency. - const result = resolver.sortEntryPointsByDependency([first, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [first, second, third])); expect(result.entryPoints).toEqual([third]); expect(result.invalidEntryPoints).toEqual([ {entryPoint: second, missingDependencies: [_('/missing')]}, @@ -183,7 +186,8 @@ runInEachFileSystem(() => { [_('/third/index.d.ts')]: {resolved: [], missing: []}, })); // Note that we will process `first` after `second`, which has the missing dependency. - const result = resolver.sortEntryPointsByDependency([second, first, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [second, first, third])); expect(result.entryPoints).toEqual([third]); expect(result.invalidEntryPoints).toEqual([ {entryPoint: second, missingDependencies: [_('/missing')]}, @@ -202,7 +206,8 @@ runInEachFileSystem(() => { [_('/sixth/index.d.ts')]: {resolved: [], missing: [_('/missing')]}, })); // Note that we will process `first` after `second`, which has the missing dependency. - const result = resolver.sortEntryPointsByDependency([sixthIgnoreMissing, first]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [sixthIgnoreMissing, first])); expect(result.entryPoints).toEqual([sixthIgnoreMissing, first]); expect(result.invalidEntryPoints).toEqual([]); }); @@ -218,7 +223,8 @@ runInEachFileSystem(() => { [_('/second/sub/index.d.ts')]: {resolved: [first.path], missing: []}, [_('/sixth/index.d.ts')]: {resolved: [second.path], missing: []}, })); - const result = resolver.sortEntryPointsByDependency([first, second, sixthIgnoreMissing]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [first, second, sixthIgnoreMissing])); // sixth has no missing dependencies, but it has _invalid_ dependencies, so it's not // compiled. expect(result.entryPoints).toEqual([]); @@ -235,7 +241,8 @@ runInEachFileSystem(() => { [_('/second/sub/index.d.ts')]: {resolved: [], missing: [_('/missing2')]}, [_('/third/index.d.ts')]: {resolved: [first.path, second.path], missing: []}, })); - const result = resolver.sortEntryPointsByDependency([first, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [first, second, third])); expect(result.entryPoints).toEqual([]); expect(result.invalidEntryPoints).toEqual([ {entryPoint: first, missingDependencies: [_('/missing1')]}, @@ -251,10 +258,12 @@ runInEachFileSystem(() => { spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({ [_('/first/index.d.ts')]: {resolved: [], missing: []}, })); - const result = resolver.sortEntryPointsByDependency([first]); + const result = + resolver.sortEntryPointsByDependency(getEntryPointsWithDeps(resolver, [first])); expect(result.entryPoints).toEqual([first]); expect(logger.logs.warn).toEqual([[ - `Entry point 'first' contains deep imports into '${_('/deep/one')}'. This is probably not a problem, but may cause the compilation of entry points to be out of order.` + `Entry point 'first' contains deep imports into '${ + _('/deep/one')}'. This is probably not a problem, but may cause the compilation of entry points to be out of order.` ]]); }); @@ -289,25 +298,28 @@ runInEachFileSystem(() => { typings: _('/project/node_modules/test-package/index.d.ts'), } as EntryPoint; - const result = resolver.sortEntryPointsByDependency([testEntryPoint]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [testEntryPoint])); expect(result.entryPoints).toEqual([testEntryPoint]); expect(logger.logs.warn).toEqual([[ - `Entry point 'test-package' contains deep imports into '${_('/project/node_modules/deeper/one')}'. This is probably not a problem, but may cause the compilation of entry points to be out of order.` + `Entry point 'test-package' contains deep imports into '${ + _('/project/node_modules/deeper/one')}'. This is probably not a problem, but may cause the compilation of entry points to be out of order.` ]]); - }); it('should error if the entry point does not have a suitable format', () => { - expect(() => resolver.sortEntryPointsByDependency([ - { path: '/first', packageJson: {}, compiledByAngular: true } as EntryPoint - ])).toThrowError(`There is no appropriate source code format in '/first' entry-point.`); + expect(() => resolver.sortEntryPointsByDependency(getEntryPointsWithDeps(resolver, [ + {path: '/first', packageJson: {}, compiledByAngular: true} as EntryPoint + ]))).toThrowError(`There is no appropriate source code format in '/first' entry-point.`); }); it('should error if there is no appropriate DependencyHost for the given formats', () => { resolver = new DependencyResolver(fs, new MockLogger(), config, {esm2015: host}, host); - expect(() => resolver.sortEntryPointsByDependency([first])) + expect( + () => resolver.sortEntryPointsByDependency(getEntryPointsWithDeps(resolver, [first]))) .toThrowError( - `Could not find a suitable format for computing dependencies of entry-point: '${first.path}'.`); + `Could not find a suitable format for computing dependencies of entry-point: '${ + first.path}'.`); }); it('should capture any dependencies that were ignored', () => { @@ -315,7 +327,8 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third])); expect(result.ignoredDependencies).toEqual([ {entryPoint: first, dependencyPath: _('/ignored-1')}, {entryPoint: third, dependencyPath: _('/ignored-2')}, @@ -327,7 +340,8 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third])); expect(result.graph).toEqual(jasmine.any(DepGraph)); expect(result.graph.size()).toBe(5); @@ -339,7 +353,7 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const entryPoints = [fifth, first, fourth, second, third]; + const entryPoints = getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third]); let sorted: SortedEntryPointsInfo; sorted = resolver.sortEntryPointsByDependency(entryPoints, first); @@ -361,7 +375,7 @@ runInEachFileSystem(() => { spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({ [_('/first/index.d.ts')]: {resolved: [], missing: [_('/missing')]}, })); - const entryPoints = [first]; + const entryPoints = getEntryPointsWithDeps(resolver, [first]); let sorted: SortedEntryPointsInfo; sorted = resolver.sortEntryPointsByDependency(entryPoints, first); @@ -377,7 +391,7 @@ runInEachFileSystem(() => { spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({ [_('/first/index.d.ts')]: {resolved: [], missing: ['fs']}, })); - const entryPoints = [first]; + const entryPoints = getEntryPointsWithDeps(resolver, [first]); let sorted: SortedEntryPointsInfo; sorted = resolver.sortEntryPointsByDependency(entryPoints, first); @@ -398,7 +412,8 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third])); expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]); expect(esm5Host.collectDependencies) @@ -447,7 +462,7 @@ runInEachFileSystem(() => { [_('/second/sub/index.d.ts')]: {resolved: [], missing: [_('/missing2')]}, [_('/third/index.d.ts')]: {resolved: [second.path], missing: []}, })); - const entryPoints = [first, second, third]; + const entryPoints = getEntryPointsWithDeps(resolver, [first, second, third]); const sorted = resolver.sortEntryPointsByDependency(entryPoints); expect(sorted.entryPoints).toEqual([first]); expect(sorted.invalidEntryPoints).toEqual([ @@ -462,11 +477,16 @@ runInEachFileSystem(() => { deps[entryPointPath].missing.forEach( dep => missing.add(fs.isRooted(dep) ? absoluteFrom(dep) : relativeFrom(dep))); if (deps[entryPointPath].deepImports) { - deps[entryPointPath].deepImports !.forEach(dep => deepImports.add(dep)); + deps[entryPointPath].deepImports!.forEach(dep => deepImports.add(dep)); } return {dependencies, missing, deepImports}; }; } + + function getEntryPointsWithDeps( + resolver: DependencyResolver, entryPoints: EntryPoint[]): EntryPointWithDependencies[] { + return entryPoints.map(entryPoint => resolver.getEntryPointWithDependencies(entryPoint)); + } }); }); }); diff --git a/packages/compiler-cli/ngcc/test/dependencies/dts_dependency_host_spec.ts b/packages/compiler-cli/ngcc/test/dependencies/dts_dependency_host_spec.ts index eb7986c8f868b..48a96239345fb 100644 --- a/packages/compiler-cli/ngcc/test/dependencies/dts_dependency_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/dependencies/dts_dependency_host_spec.ts @@ -14,7 +14,6 @@ import {createDependencyInfo} from '../../src/dependencies/dependency_host'; import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host'; runInEachFileSystem(() => { - describe('DtsDependencyHost', () => { let _: typeof absoluteFrom; let host: DtsDependencyHost; diff --git a/packages/compiler-cli/ngcc/test/dependencies/esm_dependency_host_spec.ts b/packages/compiler-cli/ngcc/test/dependencies/esm_dependency_host_spec.ts index 259e8d5cacb0b..b0c79838d8474 100644 --- a/packages/compiler-cli/ngcc/test/dependencies/esm_dependency_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/dependencies/esm_dependency_host_spec.ts @@ -16,7 +16,6 @@ import {EsmDependencyHost, hasImportOrReexportStatements, isStringImportOrReexpo import {ModuleResolver} from '../../src/dependencies/module_resolver'; runInEachFileSystem(() => { - describe('EsmDependencyHost', () => { let _: typeof absoluteFrom; let host: EsmDependencyHost; diff --git a/packages/compiler-cli/ngcc/test/dependencies/module_resolver_spec.ts b/packages/compiler-cli/ngcc/test/dependencies/module_resolver_spec.ts index 2ef140ca71b0b..503c157161051 100644 --- a/packages/compiler-cli/ngcc/test/dependencies/module_resolver_spec.ts +++ b/packages/compiler-cli/ngcc/test/dependencies/module_resolver_spec.ts @@ -67,16 +67,29 @@ runInEachFileSystem(() => { ]); }); - describe('resolveModule()', () => { + describe('resolveModuleImport()', () => { describe('with relative paths', () => { it('should resolve sibling, child and aunt modules', () => { const resolver = new ModuleResolver(getFileSystem()); + + // With relative file paths. expect(resolver.resolveModuleImport('./x', _('/libs/local-package/index.js'))) .toEqual(new ResolvedRelativeModule(_('/libs/local-package/x.js'))); expect(resolver.resolveModuleImport('./sub-folder', _('/libs/local-package/index.js'))) .toEqual(new ResolvedRelativeModule(_('/libs/local-package/sub-folder/index.js'))); expect(resolver.resolveModuleImport('../x', _('/libs/local-package/sub-folder/index.js'))) .toEqual(new ResolvedRelativeModule(_('/libs/local-package/x.js'))); + + // With absolute file paths. + expect(resolver.resolveModuleImport( + _('/libs/local-package/x'), _('/libs/local-package/index.js'))) + .toEqual(new ResolvedRelativeModule(_('/libs/local-package/x.js'))); + expect(resolver.resolveModuleImport( + _('/libs/local-package/sub-folder'), _('/libs/local-package/index.js'))) + .toEqual(new ResolvedRelativeModule(_('/libs/local-package/sub-folder/index.js'))); + expect(resolver.resolveModuleImport( + _('/libs/local-package/x'), _('/libs/local-package/sub-folder/index.js'))) + .toEqual(new ResolvedRelativeModule(_('/libs/local-package/x.js'))); }); it('should return `null` if the resolved module relative module does not exist', () => { diff --git a/packages/compiler-cli/ngcc/test/dependencies/umd_dependency_host_spec.ts b/packages/compiler-cli/ngcc/test/dependencies/umd_dependency_host_spec.ts index f778abfca3558..b736790fddc48 100644 --- a/packages/compiler-cli/ngcc/test/dependencies/umd_dependency_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/dependencies/umd_dependency_host_spec.ts @@ -251,8 +251,10 @@ runInEachFileSystem(() => { exportNames.map(e => ` exports.${e.replace(/.+\./, '')} = ${e};`).join('\n'); return ` (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports${commonJsRequires}) : - typeof define === 'function' && define.amd ? define('${moduleName}', ['exports'${amdDeps}], factory) : + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports${ + commonJsRequires}) : + typeof define === 'function' && define.amd ? define('${moduleName}', ['exports'${ + amdDeps}], factory) : (factory(global.${moduleName}${globalParams})); }(this, (function (exports${params}) { 'use strict'; ${exportStatements} diff --git a/packages/compiler-cli/ngcc/test/entry_point_finder/directory_walker_entry_point_finder_spec.ts b/packages/compiler-cli/ngcc/test/entry_point_finder/directory_walker_entry_point_finder_spec.ts index 9eaf0499d6807..aa3bdd3dc1c64 100644 --- a/packages/compiler-cli/ngcc/test/entry_point_finder/directory_walker_entry_point_finder_spec.ts +++ b/packages/compiler-cli/ngcc/test/entry_point_finder/directory_walker_entry_point_finder_spec.ts @@ -5,18 +5,18 @@ * 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 {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, relative} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relative} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; import {DependencyResolver} from '../../src/dependencies/dependency_resolver'; import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host'; import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host'; import {ModuleResolver} from '../../src/dependencies/module_resolver'; import {DirectoryWalkerEntryPointFinder} from '../../src/entry_point_finder/directory_walker_entry_point_finder'; +import {PathMappings} from '../../src/ngcc_options'; import {NgccConfiguration} from '../../src/packages/configuration'; import {EntryPoint} from '../../src/packages/entry_point'; import {EntryPointManifest, EntryPointManifestFile} from '../../src/packages/entry_point_manifest'; -import {PathMappings} from '../../src/utils'; import {MockLogger} from '../helpers/mock_logger'; runInEachFileSystem(() => { @@ -103,15 +103,37 @@ runInEachFileSystem(() => { expect(entryPoints).toEqual([]); }); - it('should write an entry-point manifest file if none was found', () => { - const basePath = _Abs('/sub_entry_points/node_modules'); + it('should write an entry-point manifest file if none was found and basePath is `node_modules`', + () => { + const basePath = _Abs('/sub_entry_points/node_modules'); + loadTestFiles([ + ...createPackage(basePath, 'common'), + ...createPackage(fs.resolve(basePath, 'common'), 'http', ['common']), + ...createPackage( + fs.resolve(basePath, 'common/http'), 'testing', ['common/http', 'common/testing']), + ...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']), + {name: _Abs('/sub_entry_points/yarn.lock'), contents: 'MOCK LOCK FILE'}, + ]); + spyOn(manifest, 'readEntryPointsUsingManifest').and.callThrough(); + spyOn(manifest, 'writeEntryPointManifest').and.callThrough(); + const finder = new DirectoryWalkerEntryPointFinder( + fs, config, logger, resolver, manifest, basePath, undefined); + finder.findEntryPoints(); + expect(manifest.readEntryPointsUsingManifest).toHaveBeenCalled(); + expect(manifest.writeEntryPointManifest).toHaveBeenCalled(); + expect(fs.exists(_Abs('/sub_entry_points/node_modules/__ngcc_entry_points__.json'))) + .toBe(true); + }); + + it('should not write an entry-point manifest file if basePath is not `node_modules`', () => { + const basePath = _Abs('/sub_entry_points/dist'); loadTestFiles([ ...createPackage(basePath, 'common'), ...createPackage(fs.resolve(basePath, 'common'), 'http', ['common']), ...createPackage( fs.resolve(basePath, 'common/http'), 'testing', ['common/http', 'common/testing']), ...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']), - {name: _Abs('/sub_entry_points/yarn.lock'), contents: 'MOCM LOCK FILE'}, + {name: _Abs('/sub_entry_points/yarn.lock'), contents: 'MOCK LOCK FILE'}, ]); spyOn(manifest, 'readEntryPointsUsingManifest').and.callThrough(); spyOn(manifest, 'writeEntryPointManifest').and.callThrough(); @@ -120,8 +142,7 @@ runInEachFileSystem(() => { finder.findEntryPoints(); expect(manifest.readEntryPointsUsingManifest).toHaveBeenCalled(); expect(manifest.writeEntryPointManifest).toHaveBeenCalled(); - expect(fs.exists(_Abs('/sub_entry_points/node_modules/__ngcc_entry_points__.json'))) - .toBe(true); + expect(fs.exists(_Abs('/sub_entry_points/dist/__ngcc_entry_points__.json'))).toBe(false); }); it('should read from the entry-point manifest file if found', () => { @@ -132,7 +153,7 @@ runInEachFileSystem(() => { ...createPackage( fs.resolve(basePath, 'common/http'), 'testing', ['common/http', 'common/testing']), ...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']), - {name: _Abs('/sub_entry_points/yarn.lock'), contents: 'MOCM LOCK FILE'}, + {name: _Abs('/sub_entry_points/yarn.lock'), contents: 'MOCK LOCK FILE'}, ]); const finder = new DirectoryWalkerEntryPointFinder( fs, config, logger, resolver, manifest, basePath, undefined); @@ -209,7 +230,7 @@ runInEachFileSystem(() => { const finder = new DirectoryWalkerEntryPointFinder( fs, config, logger, resolver, manifest, _Abs('/nested_node_modules/node_modules'), undefined); - const spy = spyOn(finder, 'walkDirectoryForEntryPoints').and.callThrough(); + const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough(); const {entryPoints} = finder.findEntryPoints(); expect(spy.calls.allArgs()).toEqual([ [_Abs(basePath)], @@ -231,7 +252,7 @@ runInEachFileSystem(() => { const finder = new DirectoryWalkerEntryPointFinder( fs, config, logger, resolver, manifest, basePath, undefined); - const spy = spyOn(finder, 'walkDirectoryForEntryPoints').and.callThrough(); + const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough(); const {entryPoints} = finder.findEntryPoints(); expect(spy.calls.allArgs()).toEqual([ [_Abs(basePath)], @@ -255,7 +276,7 @@ runInEachFileSystem(() => { const finder = new DirectoryWalkerEntryPointFinder( fs, config, logger, resolver, manifest, basePath, undefined); - const spy = spyOn(finder, 'walkDirectoryForEntryPoints').and.callThrough(); + const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough(); const {entryPoints} = finder.findEntryPoints(); expect(spy.calls.allArgs()).toEqual([ [_Abs(basePath)], @@ -266,6 +287,22 @@ runInEachFileSystem(() => { expect(entryPoints).toEqual([]); }); + it('should process sub-entry-points within a non-entry-point containing folder in a package', + () => { + const basePath = _Abs('/containing_folders/node_modules'); + loadTestFiles([ + ...createPackage(basePath, 'package'), + ...createPackage(fs.resolve(basePath, 'package/container'), 'entry-point-1'), + ]); + const finder = new DirectoryWalkerEntryPointFinder( + fs, config, logger, resolver, manifest, basePath, undefined); + const {entryPoints} = finder.findEntryPoints(); + expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([ + ['package', 'package'], + ['package', 'package/container/entry-point-1'], + ]); + }); + it('should handle dependencies via pathMappings', () => { const basePath = _Abs('/path_mapped/node_modules'); const pathMappings: PathMappings = { @@ -303,7 +340,7 @@ runInEachFileSystem(() => { const pathMappings: PathMappings = { baseUrl: '/path_mapped/dist', paths: { - '@test': ['pkg2/fesm2015/pkg2.js'], + '@test': ['pkg2/pkg2.d.ts'], '@missing': ['pkg3'], } }; @@ -334,6 +371,10 @@ runInEachFileSystem(() => { fesm2015: `./fesm2015/${packageName}.js`, }) }, + { + name: _Abs(`${basePath}/${packageName}/${packageName}.d.ts`), + contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'), + }, { name: _Abs(`${basePath}/${packageName}/fesm2015/${packageName}.js`), contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'), diff --git a/packages/compiler-cli/ngcc/test/entry_point_finder/targeted_entry_point_finder_spec.ts b/packages/compiler-cli/ngcc/test/entry_point_finder/targeted_entry_point_finder_spec.ts index 63d317fd2d021..dc9af673575bc 100644 --- a/packages/compiler-cli/ngcc/test/entry_point_finder/targeted_entry_point_finder_spec.ts +++ b/packages/compiler-cli/ngcc/test/entry_point_finder/targeted_entry_point_finder_spec.ts @@ -5,18 +5,18 @@ * 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 {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, relative} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relative} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; import {DependencyResolver} from '../../src/dependencies/dependency_resolver'; import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host'; import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host'; import {ModuleResolver} from '../../src/dependencies/module_resolver'; import {TargetedEntryPointFinder} from '../../src/entry_point_finder/targeted_entry_point_finder'; +import {PathMappings} from '../../src/ngcc_options'; import {NGCC_VERSION} from '../../src/packages/build_marker'; import {NgccConfiguration} from '../../src/packages/configuration'; import {EntryPoint} from '../../src/packages/entry_point'; -import {PathMappings} from '../../src/utils'; import {MockLogger} from '../helpers/mock_logger'; runInEachFileSystem(() => { @@ -166,6 +166,81 @@ runInEachFileSystem(() => { ]); }); + it('should handle external node_modules folders (e.g. in a yarn workspace)', () => { + // Note that neither the basePath and targetPath contain each other + const basePath = _Abs('/nested_node_modules/packages/app/node_modules'); + const targetPath = _Abs('/nested_node_modules/node_modules/package/entry-point'); + loadTestFiles([ + ...createPackage(_Abs('/nested_node_modules/node_modules'), 'package'), + ...createPackage(_Abs('/nested_node_modules/node_modules/package'), 'entry-point'), + ]); + const finder = new TargetedEntryPointFinder( + fs, config, logger, resolver, basePath, targetPath, undefined); + const {entryPoints} = finder.findEntryPoints(); + expect(dumpEntryPointPaths(_Abs('/nested_node_modules'), entryPoints)).toEqual([ + ['node_modules/package', 'node_modules/package/entry-point'], + ]); + }); + + it('should handle external node_modules folders (e.g. in a yarn workspace) for dependencies', + () => { + // The application being compiled is at `/project/packages/app` so the basePath sent to + // ngcc is the `node_modules` below it + const basePath = _Abs('/project/packages/app/node_modules'); + // `packages/app` depends upon lib1, which has a private dependency on lib2 in its + // own `node_modules` folder + const lib2 = createPackage( + _Abs('/project/node_modules/lib1/node_modules'), 'lib2', ['lib3/entry-point']); + // `lib2` depends upon `lib3/entry-point` which has been hoisted all the way up to the + // top level `node_modules` + const lib3 = createPackage(_Abs('/project/node_modules'), 'lib3'); + const lib3EntryPoint = createPackage(_Abs('/project/node_modules/lib3'), 'entry-point'); + loadTestFiles([...lib2, ...lib3, ...lib3EntryPoint]); + // The targetPath being processed is `lib2` and we expect it to find the correct + // entry-point info for the `lib3/entry-point` dependency. + const targetPath = _Abs('/project/node_modules/lib1/node_modules/lib2'); + const finder = new TargetedEntryPointFinder( + fs, config, logger, resolver, basePath, targetPath, undefined); + const {entryPoints} = finder.findEntryPoints(); + expect(dumpEntryPointPaths(_Abs('/project/node_modules'), entryPoints)).toEqual([ + ['lib3', 'lib3/entry-point'], + ['lib1/node_modules/lib2', 'lib1/node_modules/lib2'], + ]); + }); + + it('should handle external node_modules folders (e.g. in a yarn workspace) for scoped dependencies', + () => { + // The application being compiled is at `/project/packages/app` so the basePath sent to + // ngcc is the `node_modules` below it + const basePath = _Abs('/project/packages/app/node_modules'); + // `packages/app` depends upon lib1, which has a private dependency on lib2 in its + // own `node_modules` folder + const lib2 = createPackage( + _Abs('/project/node_modules/lib1/node_modules'), 'lib2', + ['@scope/lib3/entry-point']); + // `lib2` depends upon `lib3/entry-point` which has been hoisted all the way up to the + // top level `node_modules` + const lib3 = createPackage(_Abs('/project/node_modules/@scope'), 'lib3'); + const lib3EntryPoint = createPackage( + _Abs('/project/node_modules/@scope/lib3'), 'entry-point', ['lib4/entry-point']); + const lib4 = + createPackage(_Abs('/project/node_modules/@scope/lib3/node_modules'), 'lib4'); + const lib4EntryPoint = createPackage( + _Abs('/project/node_modules/@scope/lib3/node_modules/lib4'), 'entry-point'); + loadTestFiles([...lib2, ...lib3, ...lib3EntryPoint, ...lib4, ...lib4EntryPoint]); + // The targetPath being processed is `lib2` and we expect it to find the correct + // entry-point info for the `lib3/entry-point` dependency. + const targetPath = _Abs('/project/node_modules/lib1/node_modules/lib2'); + const finder = new TargetedEntryPointFinder( + fs, config, logger, resolver, basePath, targetPath, undefined); + const {entryPoints} = finder.findEntryPoints(); + expect(dumpEntryPointPaths(_Abs('/project/node_modules'), entryPoints)).toEqual([ + ['@scope/lib3/node_modules/lib4', '@scope/lib3/node_modules/lib4/entry-point'], + ['@scope/lib3', '@scope/lib3/entry-point'], + ['lib1/node_modules/lib2', 'lib1/node_modules/lib2'], + ]); + }); + it('should handle dependencies via pathMappings', () => { const basePath = _Abs('/path_mapped/node_modules'); const targetPath = _Abs('/path_mapped/node_modules/test'); @@ -290,7 +365,6 @@ runInEachFileSystem(() => { basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] { return entryPoints.map(x => [relative(basePath, x.package), relative(basePath, x.path)]); } - }); describe('targetNeedsProcessingOrCleaning()', () => { diff --git a/packages/compiler-cli/ngcc/test/entry_point_finder/utils_spec.ts b/packages/compiler-cli/ngcc/test/entry_point_finder/utils_spec.ts new file mode 100644 index 0000000000000..41b893c9e2d77 --- /dev/null +++ b/packages/compiler-cli/ngcc/test/entry_point_finder/utils_spec.ts @@ -0,0 +1,145 @@ +/** + * @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 {absoluteFrom, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; + +import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {getBasePaths} from '../../src/entry_point_finder/utils'; +import {MockLogger} from '../helpers/mock_logger'; + +runInEachFileSystem(() => { + let _: typeof absoluteFrom; + let logger: MockLogger; + + beforeEach(() => { + _ = absoluteFrom; + logger = new MockLogger(); + }); + + describe('getBasePaths', () => { + it('should just return the `sourceDirectory if there are no `pathMappings', () => { + const sourceDirectory = _('/path/to/project/node_modules'); + const basePaths = getBasePaths(logger, sourceDirectory, undefined); + expect(basePaths).toEqual([sourceDirectory]); + }); + + it('should use each path mapping prefix and sort in descending order', () => { + const projectDirectory = _('/path/to/project'); + const fs = getFileSystem(); + fs.ensureDir(fs.resolve(projectDirectory, 'dist-1')); + fs.ensureDir(fs.resolve(projectDirectory, 'sub-folder/dist-2')); + fs.ensureDir(fs.resolve(projectDirectory, 'libs')); + + const sourceDirectory = _('/path/to/project/node_modules'); + const pathMappings = { + baseUrl: projectDirectory, + paths: {'@dist': ['dist-1', 'sub-folder/dist-2'], '@lib/*': ['libs/*']} + }; + const basePaths = getBasePaths(logger, sourceDirectory, pathMappings); + expect(basePaths).toEqual([ + fs.resolve(projectDirectory, 'sub-folder/dist-2'), + sourceDirectory, + fs.resolve(projectDirectory, 'libs'), + fs.resolve(projectDirectory, 'dist-1'), + ]); + }); + + it('should discard paths that are already contained by another path', () => { + const projectDirectory = _('/path/to/project'); + const fs = getFileSystem(); + fs.ensureDir(fs.resolve(projectDirectory, 'dist-1')); + fs.ensureDir(fs.resolve(projectDirectory, 'dist-1/sub-folder')); + fs.ensureDir(fs.resolve(projectDirectory, 'node_modules/libs')); + + const sourceDirectory = _('/path/to/project/node_modules'); + const pathMappings = { + baseUrl: projectDirectory, + paths: {'@dist': ['dist-1', 'dist-1/sub-folder'], '@lib/*': ['node_modules/libs/*']} + }; + const basePaths = getBasePaths(logger, sourceDirectory, pathMappings); + expect(basePaths).toEqual([ + sourceDirectory, + fs.resolve(projectDirectory, 'dist-1'), + ]); + }); + + it('should use the containing directory of path mapped files', () => { + const projectDirectory = _('/path/to/project'); + const fs = getFileSystem(); + fs.ensureDir(fs.resolve(projectDirectory, 'dist-1')); + fs.writeFile(fs.resolve(projectDirectory, 'dist-1/file.js'), 'dummy content'); + + const sourceDirectory = _('/path/to/project/node_modules'); + const pathMappings = {baseUrl: projectDirectory, paths: {'@dist': ['dist-1/file.js']}}; + const basePaths = getBasePaths(logger, sourceDirectory, pathMappings); + expect(basePaths).toEqual([ + sourceDirectory, + fs.resolve(projectDirectory, 'dist-1'), + ]); + }); + + it('should always include the `sourceDirectory` if it is a node_modules directory in the returned basePaths, even if it is contained by another basePath', + () => { + const projectDirectory = _('/path/to/project'); + const sourceDirectory = _('/path/to/project/node_modules'); + const fs = getFileSystem(); + fs.ensureDir(fs.resolve(sourceDirectory)); + + const pathMappings = {baseUrl: projectDirectory, paths: {'*': ['./*']}}; + const basePaths = getBasePaths(logger, sourceDirectory, pathMappings); + expect(basePaths).toEqual([ + sourceDirectory, + projectDirectory, + ]); + }); + + it('should log a warning if baseUrl is the root path', () => { + const fs = getFileSystem(); + fs.ensureDir(fs.resolve('/dist')); + + const sourceDirectory = _('/path/to/project/node_modules'); + const pathMappings = {baseUrl: _('/'), paths: {'@dist': ['dist']}}; + const basePaths = getBasePaths(logger, sourceDirectory, pathMappings); + expect(basePaths).toEqual([ + sourceDirectory, + fs.resolve('/dist'), + ]); + expect(logger.logs.warn).toEqual([ + [`The provided pathMappings baseUrl is the root path ${_('/')}.\n` + + `This is likely to mess up how ngcc finds entry-points and is probably not correct.\n` + + `Please check your path mappings configuration such as in the tsconfig.json file.`] + ]); + }); + + it('should discard basePaths that do not exists and log a debug message', () => { + const projectDirectory = _('/path/to/project'); + const fs = getFileSystem(); + fs.ensureDir(fs.resolve(projectDirectory, 'dist-1')); + fs.ensureDir(fs.resolve(projectDirectory, 'sub-folder')); + + const sourceDirectory = _('/path/to/project/node_modules'); + const pathMappings = { + baseUrl: projectDirectory, + paths: {'@dist': ['dist-1', 'sub-folder/dist-2'], '@lib/*': ['libs/*']} + }; + const basePaths = getBasePaths(logger, sourceDirectory, pathMappings); + expect(basePaths).toEqual([ + sourceDirectory, + fs.resolve(projectDirectory, 'dist-1'), + ]); + expect(logger.logs.debug).toEqual([ + [`The basePath "${ + fs.resolve(projectDirectory, 'sub-folder/dist-2')}" computed from baseUrl "${ + projectDirectory}" and path mapping "sub-folder/dist-2" does not exist in the file-system.\n` + + `It will not be scanned for entry-points.`], + [`The basePath "${fs.resolve(projectDirectory, 'libs')}" computed from baseUrl "${ + projectDirectory}" and path mapping "libs/*" does not exist in the file-system.\n` + + `It will not be scanned for entry-points.`], + ]); + }); + }); +}); \ No newline at end of file diff --git a/packages/compiler-cli/ngcc/test/execution/cluster/executor_spec.ts b/packages/compiler-cli/ngcc/test/execution/cluster/executor_spec.ts index 1fc5696786aef..1def235e51fe7 100644 --- a/packages/compiler-cli/ngcc/test/execution/cluster/executor_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/cluster/executor_spec.ts @@ -8,50 +8,47 @@ /// <reference types="node" /> +import {getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import * as cluster from 'cluster'; -import {MockFileSystemNative} from '../../../../src/ngtsc/file_system/testing'; +import {MockFileSystemNative, runInEachFileSystem} from '../../../../src/ngtsc/file_system/testing'; import {ClusterExecutor} from '../../../src/execution/cluster/executor'; import {ClusterMaster} from '../../../src/execution/cluster/master'; -import {ClusterWorker} from '../../../src/execution/cluster/worker'; import {AsyncLocker} from '../../../src/locking/async_locker'; import {PackageJsonUpdater} from '../../../src/writing/package_json_updater'; import {MockLockFile} from '../../helpers/mock_lock_file'; import {MockLogger} from '../../helpers/mock_logger'; import {mockProperty} from '../../helpers/spy_utils'; +runInEachFileSystem(() => { + describe('ClusterExecutor', () => { + const runAsClusterMaster = mockProperty(cluster, 'isMaster'); + let masterRunSpy: jasmine.Spy; + let mockLogger: MockLogger; + let lockFileLog: string[]; + let mockLockFile: MockLockFile; + let locker: AsyncLocker; + let executor: ClusterExecutor; + let createTaskCompletedCallback: jasmine.Spy; + + beforeEach(() => { + masterRunSpy = spyOn(ClusterMaster.prototype, 'run') + .and.returnValue(Promise.resolve('CusterMaster#run()' as any)); + createTaskCompletedCallback = jasmine.createSpy('createTaskCompletedCallback'); + + mockLogger = new MockLogger(); + lockFileLog = []; + mockLockFile = new MockLockFile(new MockFileSystemNative(), lockFileLog); + locker = new AsyncLocker(mockLockFile, mockLogger, 200, 2); + executor = new ClusterExecutor( + 42, getFileSystem(), mockLogger, null as unknown as PackageJsonUpdater, locker, + createTaskCompletedCallback); + }); -describe('ClusterExecutor', () => { - const runAsClusterMaster = mockProperty(cluster, 'isMaster'); - let masterRunSpy: jasmine.Spy; - let workerRunSpy: jasmine.Spy; - let mockLogger: MockLogger; - let lockFileLog: string[]; - let mockLockFile: MockLockFile; - let locker: AsyncLocker; - let executor: ClusterExecutor; - let createTaskCompletedCallback: jasmine.Spy; - - beforeEach(() => { - masterRunSpy = spyOn(ClusterMaster.prototype, 'run') - .and.returnValue(Promise.resolve('CusterMaster#run()')); - workerRunSpy = spyOn(ClusterWorker.prototype, 'run') - .and.returnValue(Promise.resolve('CusterWorker#run()')); - createTaskCompletedCallback = jasmine.createSpy('createTaskCompletedCallback'); - - mockLogger = new MockLogger(); - lockFileLog = []; - mockLockFile = new MockLockFile(new MockFileSystemNative(), lockFileLog); - locker = new AsyncLocker(mockLockFile, mockLogger, 200, 2); - executor = new ClusterExecutor( - 42, mockLogger, null as unknown as PackageJsonUpdater, locker, createTaskCompletedCallback); - }); - - describe('execute()', () => { - describe('(on cluster master)', () => { + describe('execute()', () => { beforeEach(() => runAsClusterMaster(true)); - it('should log debug info about the executor', async() => { + it('should log debug info about the executor', async () => { const anyFn: () => any = () => undefined; await executor.execute(anyFn, anyFn); @@ -60,7 +57,7 @@ describe('ClusterExecutor', () => { ]); }); - it('should delegate to `ClusterMaster#run()`', async() => { + it('should delegate to `ClusterMaster#run()`', async () => { const analyzeEntryPointsSpy = jasmine.createSpy('analyzeEntryPoints'); const createCompilerFnSpy = jasmine.createSpy('createCompilerFn'); @@ -68,20 +65,19 @@ describe('ClusterExecutor', () => { .toBe('CusterMaster#run()' as any); expect(masterRunSpy).toHaveBeenCalledWith(); - expect(workerRunSpy).not.toHaveBeenCalled(); expect(analyzeEntryPointsSpy).toHaveBeenCalledWith(); expect(createCompilerFnSpy).not.toHaveBeenCalled(); }); it('should call LockFile.write() and LockFile.remove() if master runner completes successfully', - async() => { + async () => { const anyFn: () => any = () => undefined; await executor.execute(anyFn, anyFn); expect(lockFileLog).toEqual(['write()', 'remove()']); }); - it('should call LockFile.write() and LockFile.remove() if master runner fails', async() => { + it('should call LockFile.write() and LockFile.remove() if master runner fails', async () => { const anyFn: () => any = () => undefined; masterRunSpy.and.returnValue(Promise.reject(new Error('master runner error'))); let error = ''; @@ -94,7 +90,7 @@ describe('ClusterExecutor', () => { expect(lockFileLog).toEqual(['write()', 'remove()']); }); - it('should not call master runner if LockFile.write() fails', async() => { + it('should not call master runner if LockFile.write() fails', async () => { const anyFn: () => any = () => undefined; spyOn(mockLockFile, 'write').and.callFake(() => { lockFileLog.push('write()'); @@ -102,7 +98,7 @@ describe('ClusterExecutor', () => { }); executor = new ClusterExecutor( - 42, mockLogger, null as unknown as PackageJsonUpdater, locker, + 42, getFileSystem(), mockLogger, null as unknown as PackageJsonUpdater, locker, createTaskCompletedCallback); let error = ''; try { @@ -114,7 +110,7 @@ describe('ClusterExecutor', () => { expect(masterRunSpy).not.toHaveBeenCalled(); }); - it('should fail if LockFile.remove() fails', async() => { + it('should fail if LockFile.remove() fails', async () => { const anyFn: () => any = () => undefined; spyOn(mockLockFile, 'remove').and.callFake(() => { lockFileLog.push('remove()'); @@ -122,7 +118,7 @@ describe('ClusterExecutor', () => { }); executor = new ClusterExecutor( - 42, mockLogger, null as unknown as PackageJsonUpdater, locker, + 42, getFileSystem(), mockLogger, null as unknown as PackageJsonUpdater, locker, createTaskCompletedCallback); let error = ''; try { @@ -135,36 +131,5 @@ describe('ClusterExecutor', () => { expect(masterRunSpy).toHaveBeenCalled(); }); }); - - describe('(on cluster worker)', () => { - beforeEach(() => runAsClusterMaster(false)); - - it('should not log debug info about the executor', async() => { - const anyFn: () => any = () => undefined; - await executor.execute(anyFn, anyFn); - - expect(mockLogger.logs.debug).toEqual([]); - }); - - it('should delegate to `ClusterWorker#run()`', async() => { - const analyzeEntryPointsSpy = jasmine.createSpy('analyzeEntryPoints'); - const createCompilerFnSpy = jasmine.createSpy('createCompilerFn'); - - expect(await executor.execute(analyzeEntryPointsSpy, createCompilerFnSpy)) - .toBe('CusterWorker#run()' as any); - - expect(masterRunSpy).not.toHaveBeenCalledWith(); - expect(workerRunSpy).toHaveBeenCalled(); - - expect(analyzeEntryPointsSpy).not.toHaveBeenCalled(); - expect(createCompilerFnSpy).toHaveBeenCalledWith(jasmine.any(Function)); - }); - - it('should not call LockFile.write() or LockFile.remove()', async() => { - const anyFn: () => any = () => undefined; - await executor.execute(anyFn, anyFn); - expect(lockFileLog).toEqual([]); - }); - }); }); }); diff --git a/packages/compiler-cli/ngcc/test/execution/cluster/package_json_updater_spec.ts b/packages/compiler-cli/ngcc/test/execution/cluster/package_json_updater_spec.ts index d2cea43299719..fe68783bc0610 100644 --- a/packages/compiler-cli/ngcc/test/execution/cluster/package_json_updater_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/cluster/package_json_updater_spec.ts @@ -12,7 +12,7 @@ import * as cluster from 'cluster'; import {absoluteFrom as _} from '../../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../../src/ngtsc/file_system/testing'; -import {ClusterPackageJsonUpdater} from '../../../src/execution/cluster/package_json_updater'; +import {ClusterWorkerPackageJsonUpdater} from '../../../src/execution/cluster/package_json_updater'; import {JsonObject} from '../../../src/packages/entry_point'; import {PackageJsonPropertyPositioning, PackageJsonUpdate, PackageJsonUpdater} from '../../../src/writing/package_json_updater'; import {mockProperty} from '../../helpers/spy_utils'; @@ -23,203 +23,177 @@ runInEachFileSystem(() => { const runAsClusterMaster = mockProperty(cluster, 'isMaster'); const mockProcessSend = mockProperty(process, 'send'); let processSendSpy: jasmine.Spy; - let delegate: PackageJsonUpdater; - let updater: ClusterPackageJsonUpdater; beforeEach(() => { processSendSpy = jasmine.createSpy('process.send'); mockProcessSend(processSendSpy); + }); - delegate = new MockPackageJsonUpdater(); - updater = new ClusterPackageJsonUpdater(delegate); + describe('constructor()', () => { + it('should throw an error if used on a cluster master', () => { + runAsClusterMaster(true); + expect(() => new ClusterWorkerPackageJsonUpdater()) + .toThrowError( + 'Tried to create cluster worker PackageJsonUpdater on the master process.'); + }); }); describe('createUpdate()', () => { - [true, false].forEach( - isMaster => describe(`(on cluster ${isMaster ? 'master' : 'worker'})`, () => { - beforeEach(() => runAsClusterMaster(isMaster)); - - it('should return a `PackageJsonUpdate` instance', - () => { expect(updater.createUpdate()).toEqual(jasmine.any(PackageJsonUpdate)); }); - - it('should wire up the `PackageJsonUpdate` with its `writeChanges()` method', () => { - const writeChangesSpy = spyOn(updater, 'writeChanges'); - const jsonPath = _('/foo/package.json'); - const update = updater.createUpdate(); - - update.addChange(['foo'], 'updated'); - update.addChange(['baz'], 'updated 2', 'alphabetic'); - update.addChange(['bar'], 'updated 3', {before: 'bar'}); - update.writeChanges(jsonPath); - - expect(writeChangesSpy) - .toHaveBeenCalledWith( - [ - [['foo'], 'updated', 'unimportant'], - [['baz'], 'updated 2', 'alphabetic'], - [['bar'], 'updated 3', {before: 'bar'}], - ], - jsonPath, undefined); - }); - })); + let updater: ClusterWorkerPackageJsonUpdater; + beforeEach(() => { + runAsClusterMaster(false); + updater = new ClusterWorkerPackageJsonUpdater(); + }); + + it('should return a `PackageJsonUpdate` instance', () => { + expect(updater.createUpdate()).toEqual(jasmine.any(PackageJsonUpdate)); + }); + + it('should wire up the `PackageJsonUpdate` with its `writeChanges()` method', () => { + const writeChangesSpy = spyOn(updater, 'writeChanges'); + const jsonPath = _('/foo/package.json'); + const update = updater.createUpdate(); + + update.addChange(['foo'], 'updated'); + update.addChange(['baz'], 'updated 2', 'alphabetic'); + update.addChange(['bar'], 'updated 3', {before: 'bar'}); + update.writeChanges(jsonPath); + + expect(writeChangesSpy) + .toHaveBeenCalledWith( + [ + [['foo'], 'updated', 'unimportant'], + [['baz'], 'updated 2', 'alphabetic'], + [['bar'], 'updated 3', {before: 'bar'}], + ], + jsonPath, undefined); + }); }); describe('writeChanges()', () => { - describe('(on cluster master)', () => { - beforeEach(() => runAsClusterMaster(true)); - afterEach(() => expect(processSendSpy).not.toHaveBeenCalled()); - - it('should forward the call to the delegate `PackageJsonUpdater`', () => { - const jsonPath = _('/foo/package.json'); - const parsedJson = {foo: 'bar'}; - - updater.createUpdate() - .addChange(['foo'], 'updated') - .addChange(['bar'], 'updated too', 'alphabetic') - .writeChanges(jsonPath, parsedJson); - - expect(delegate.writeChanges) - .toHaveBeenCalledWith( - [ - [['foo'], 'updated', 'unimportant'], - [['bar'], 'updated too', 'alphabetic'], - ], - jsonPath, parsedJson); + let updater: ClusterWorkerPackageJsonUpdater; + beforeEach(() => { + runAsClusterMaster(false); + updater = new ClusterWorkerPackageJsonUpdater(); + }); + + it('should send an `update-package-json` message to the master process', () => { + const jsonPath = _('/foo/package.json'); + + const writeToProp = + (propPath: string[], positioning?: PackageJsonPropertyPositioning, + parsed?: JsonObject) => updater.createUpdate() + .addChange(propPath, 'updated', positioning) + .writeChanges(jsonPath, parsed); + + writeToProp(['foo']); + expect(processSendSpy).toHaveBeenCalledWith({ + type: 'update-package-json', + packageJsonPath: jsonPath, + changes: [[['foo'], 'updated', 'unimportant']], }); - it('should throw, if trying to re-apply an already applied update', () => { - const update = updater.createUpdate().addChange(['foo'], 'updated'); + writeToProp(['bar'], {before: 'foo'}); + expect(processSendSpy).toHaveBeenCalledWith({ + type: 'update-package-json', + packageJsonPath: jsonPath, + changes: [[['bar'], 'updated', {before: 'foo'}]], + }); - expect(() => update.writeChanges(_('/foo/package.json'))).not.toThrow(); - expect(() => update.writeChanges(_('/foo/package.json'))) - .toThrowError('Trying to apply a `PackageJsonUpdate` that has already been applied.'); - expect(() => update.writeChanges(_('/bar/package.json'))) - .toThrowError('Trying to apply a `PackageJsonUpdate` that has already been applied.'); + writeToProp(['bar', 'baz', 'qux'], 'alphabetic', {}); + expect(processSendSpy).toHaveBeenCalledWith({ + type: 'update-package-json', + packageJsonPath: jsonPath, + changes: [[['bar', 'baz', 'qux'], 'updated', 'alphabetic']], }); }); - describe('(on cluster worker)', () => { - beforeEach(() => runAsClusterMaster(false)); - afterEach(() => expect(delegate.writeChanges).not.toHaveBeenCalled()); - - it('should send an `update-package-json` message to the master process', () => { - const jsonPath = _('/foo/package.json'); - - const writeToProp = - (propPath: string[], positioning?: PackageJsonPropertyPositioning, - parsed?: JsonObject) => updater.createUpdate() - .addChange(propPath, 'updated', positioning) - .writeChanges(jsonPath, parsed); - - writeToProp(['foo']); - expect(processSendSpy).toHaveBeenCalledWith({ - type: 'update-package-json', - packageJsonPath: jsonPath, - changes: [[['foo'], 'updated', 'unimportant']], - }); - - writeToProp(['bar'], {before: 'foo'}); - expect(processSendSpy).toHaveBeenCalledWith({ - type: 'update-package-json', - packageJsonPath: jsonPath, - changes: [[['bar'], 'updated', {before: 'foo'}]], - }); - - writeToProp(['bar', 'baz', 'qux'], 'alphabetic', {}); - expect(processSendSpy).toHaveBeenCalledWith({ - type: 'update-package-json', - packageJsonPath: jsonPath, - changes: [[['bar', 'baz', 'qux'], 'updated', 'alphabetic']], - }); + it('should update an in-memory representation (if provided)', () => { + const jsonPath = _('/foo/package.json'); + const parsedJson: JsonObject = { + foo: true, + bar: {baz: 'OK'}, + }; + + const update = + updater.createUpdate().addChange(['foo'], false).addChange(['bar', 'baz'], 42); + + // Not updated yet. + expect(parsedJson).toEqual({ + foo: true, + bar: {baz: 'OK'}, }); - it('should update an in-memory representation (if provided)', () => { - const jsonPath = _('/foo/package.json'); - const parsedJson: JsonObject = { - foo: true, - bar: {baz: 'OK'}, - }; - - const update = - updater.createUpdate().addChange(['foo'], false).addChange(['bar', 'baz'], 42); - - // Not updated yet. - expect(parsedJson).toEqual({ - foo: true, - bar: {baz: 'OK'}, - }); - - update.writeChanges(jsonPath, parsedJson); - - // Updated now. - expect(parsedJson).toEqual({ - foo: false, - bar: {baz: 42}, - }); + update.writeChanges(jsonPath, parsedJson); + + // Updated now. + expect(parsedJson).toEqual({ + foo: false, + bar: {baz: 42}, }); + }); - it('should create any missing ancestor objects', () => { - const jsonPath = _('/foo/package.json'); - const parsedJson: JsonObject = {foo: {}}; + it('should create any missing ancestor objects', () => { + const jsonPath = _('/foo/package.json'); + const parsedJson: JsonObject = {foo: {}}; - updater.createUpdate() - .addChange(['foo', 'bar', 'baz', 'qux'], 'updated') - .writeChanges(jsonPath, parsedJson); + updater.createUpdate() + .addChange(['foo', 'bar', 'baz', 'qux'], 'updated') + .writeChanges(jsonPath, parsedJson); - expect(parsedJson).toEqual({ - foo: { - bar: { - baz: { - qux: 'updated', - }, + expect(parsedJson).toEqual({ + foo: { + bar: { + baz: { + qux: 'updated', }, }, - }); + }, }); + }); - it('should throw, if a property-path is empty', () => { - const jsonPath = _('/foo/package.json'); + it('should throw, if a property-path is empty', () => { + const jsonPath = _('/foo/package.json'); - expect(() => updater.createUpdate().addChange([], 'missing').writeChanges(jsonPath, {})) - .toThrowError(`Missing property path for writing value to '${jsonPath}'.`); - }); + expect(() => updater.createUpdate().addChange([], 'missing').writeChanges(jsonPath, {})) + .toThrowError(`Missing property path for writing value to '${jsonPath}'.`); + }); - it('should throw, if a property-path points to a non-object intermediate value', () => { - const jsonPath = _('/foo/package.json'); - const parsedJson = {foo: null, bar: 42, baz: {qux: []}}; + it('should throw, if a property-path points to a non-object intermediate value', () => { + const jsonPath = _('/foo/package.json'); + const parsedJson = {foo: null, bar: 42, baz: {qux: []}}; - const writeToProp = (propPath: string[], parsed?: JsonObject) => - updater.createUpdate().addChange(propPath, 'updated').writeChanges(jsonPath, parsed); + const writeToProp = (propPath: string[], parsed?: JsonObject) => + updater.createUpdate().addChange(propPath, 'updated').writeChanges(jsonPath, parsed); - expect(() => writeToProp(['foo', 'child'], parsedJson)) - .toThrowError('Property path \'foo.child\' does not point to an object.'); - expect(() => writeToProp(['bar', 'child'], parsedJson)) - .toThrowError('Property path \'bar.child\' does not point to an object.'); - expect(() => writeToProp(['baz', 'qux', 'child'], parsedJson)) - .toThrowError('Property path \'baz.qux.child\' does not point to an object.'); + expect(() => writeToProp(['foo', 'child'], parsedJson)) + .toThrowError('Property path \'foo.child\' does not point to an object.'); + expect(() => writeToProp(['bar', 'child'], parsedJson)) + .toThrowError('Property path \'bar.child\' does not point to an object.'); + expect(() => writeToProp(['baz', 'qux', 'child'], parsedJson)) + .toThrowError('Property path \'baz.qux.child\' does not point to an object.'); - // It should not throw, if no parsed representation is provided. - // (The error will still be thrown on the master process, but that is out of scope for - // this test.) - expect(() => writeToProp(['foo', 'child'])).not.toThrow(); - }); + // It should not throw, if no parsed representation is provided. + // (The error will still be thrown on the master process, but that is out of scope for + // this test.) + expect(() => writeToProp(['foo', 'child'])).not.toThrow(); + }); - it('should throw, if trying to re-apply an already applied update', () => { - const update = updater.createUpdate().addChange(['foo'], 'updated'); + it('should throw, if trying to re-apply an already applied update', () => { + const update = updater.createUpdate().addChange(['foo'], 'updated'); - expect(() => update.writeChanges(_('/foo/package.json'))).not.toThrow(); - expect(() => update.writeChanges(_('/foo/package.json'))) - .toThrowError('Trying to apply a `PackageJsonUpdate` that has already been applied.'); - expect(() => update.writeChanges(_('/bar/package.json'))) - .toThrowError('Trying to apply a `PackageJsonUpdate` that has already been applied.'); - }); + expect(() => update.writeChanges(_('/foo/package.json'))).not.toThrow(); + expect(() => update.writeChanges(_('/foo/package.json'))) + .toThrowError('Trying to apply a `PackageJsonUpdate` that has already been applied.'); + expect(() => update.writeChanges(_('/bar/package.json'))) + .toThrowError('Trying to apply a `PackageJsonUpdate` that has already been applied.'); }); }); - - // Helpers - class MockPackageJsonUpdater implements PackageJsonUpdater { - createUpdate = jasmine.createSpy('MockPackageJsonUpdater#createUpdate'); - writeChanges = jasmine.createSpy('MockPackageJsonUpdater#writeChanges'); - } }); + + // Helpers + class MockPackageJsonUpdater implements PackageJsonUpdater { + createUpdate = jasmine.createSpy('MockPackageJsonUpdater#createUpdate'); + writeChanges = jasmine.createSpy('MockPackageJsonUpdater#writeChanges'); + } }); diff --git a/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts b/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts index 89463a3b92a08..8cc9370b3cfc6 100644 --- a/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts @@ -11,13 +11,13 @@ import * as cluster from 'cluster'; import {EventEmitter} from 'events'; -import {ClusterWorker} from '../../../src/execution/cluster/worker'; +import {startWorker} from '../../../src/execution/cluster/worker'; import {Task, TaskCompletedCallback, TaskProcessingOutcome} from '../../../src/execution/tasks/api'; import {MockLogger} from '../../helpers/mock_logger'; import {mockProperty} from '../../helpers/spy_utils'; -describe('ClusterWorker', () => { +describe('startWorker()', () => { const runAsClusterMaster = mockProperty(cluster, 'isMaster'); const mockProcessSend = mockProperty(process, 'send'); let processSendSpy: jasmine.Spy; @@ -35,131 +35,116 @@ describe('ClusterWorker', () => { mockLogger = new MockLogger(); }); - describe('constructor()', () => { - describe('(on cluster master)', () => { - beforeEach(() => runAsClusterMaster(true)); + describe('(on cluster master)', () => { + beforeEach(() => runAsClusterMaster(true)); - it('should throw an error', () => { - expect(() => new ClusterWorker(mockLogger, createCompileFnSpy)) - .toThrowError('Tried to instantiate `ClusterWorker` on the master process.'); - expect(createCompileFnSpy).not.toHaveBeenCalled(); - }); + it('should throw an error', async () => { + await expectAsync(startWorker(mockLogger, createCompileFnSpy)) + .toBeRejectedWithError('Tried to run cluster worker on the master process.'); + expect(createCompileFnSpy).not.toHaveBeenCalled(); }); + }); - describe('(on cluster worker)', () => { - beforeEach(() => runAsClusterMaster(false)); + describe('(on cluster worker)', () => { + // The `cluster.worker` property is normally `undefined` on the master process and set to the + // current `cluster.worker` on worker processes. + const mockClusterWorker = mockProperty(cluster, 'worker'); - it('should create the `compileFn()`', () => { - new ClusterWorker(mockLogger, createCompileFnSpy); - expect(createCompileFnSpy).toHaveBeenCalledWith(jasmine.any(Function)); - }); + beforeEach(() => { + runAsClusterMaster(false); + mockClusterWorker(Object.assign(new EventEmitter(), {id: 42}) as cluster.Worker); + }); - it('should set up `compileFn()` to send `task-completed` messages to master', () => { - new ClusterWorker(mockLogger, createCompileFnSpy); - const onTaskCompleted: TaskCompletedCallback = createCompileFnSpy.calls.argsFor(0)[0]; - - onTaskCompleted(null as any, TaskProcessingOutcome.Processed, null); - expect(processSendSpy).toHaveBeenCalledTimes(1); - expect(processSendSpy).toHaveBeenCalledWith({ - type: 'task-completed', - outcome: TaskProcessingOutcome.Processed, - message: null - }); - - processSendSpy.calls.reset(); - - onTaskCompleted(null as any, TaskProcessingOutcome.Failed, 'error message'); - expect(processSendSpy).toHaveBeenCalledTimes(1); - expect(processSendSpy).toHaveBeenCalledWith({ - type: 'task-completed', - outcome: TaskProcessingOutcome.Failed, - message: 'error message', - }); - }); + it('should create the `compileFn()`', () => { + startWorker(mockLogger, createCompileFnSpy); + expect(createCompileFnSpy).toHaveBeenCalledWith(jasmine.any(Function)); }); - }); - describe('run()', () => { - describe( - '(on cluster master)', - () => {/* No tests needed, becasue the constructor would have thrown. */}); + it('should set up `compileFn()` to send `task-completed` messages to master', () => { + startWorker(mockLogger, createCompileFnSpy); + const onTaskCompleted: TaskCompletedCallback = createCompileFnSpy.calls.argsFor(0)[0]; - describe('(on cluster worker)', () => { - // The `cluster.worker` property is normally `undefined` on the master process and set to the - // current `cluster.Worker` on worker processes. - const mockClusterWorker = mockProperty(cluster, 'worker'); - let worker: ClusterWorker; + onTaskCompleted(null as any, TaskProcessingOutcome.Processed, null); + expect(processSendSpy).toHaveBeenCalledTimes(1); + expect(processSendSpy) + .toHaveBeenCalledWith( + {type: 'task-completed', outcome: TaskProcessingOutcome.Processed, message: null}); - beforeEach(() => { - runAsClusterMaster(false); - mockClusterWorker(Object.assign(new EventEmitter(), {id: 42}) as cluster.Worker); + processSendSpy.calls.reset(); - worker = new ClusterWorker(mockLogger, createCompileFnSpy); + onTaskCompleted(null as any, TaskProcessingOutcome.Failed, 'error message'); + expect(processSendSpy).toHaveBeenCalledTimes(1); + expect(processSendSpy).toHaveBeenCalledWith({ + type: 'task-completed', + outcome: TaskProcessingOutcome.Failed, + message: 'error message', }); + }); - it('should return a promise (that is never resolved)', done => { - const promise = worker.run(); + it('should return a promise (that is never resolved)', done => { + const promise = startWorker(mockLogger, createCompileFnSpy); - expect(promise).toEqual(jasmine.any(Promise)); + expect(promise).toEqual(jasmine.any(Promise)); - promise.then( - () => done.fail('Expected promise not to resolve'), - () => done.fail('Expected promise not to reject')); + promise.then( + () => done.fail('Expected promise not to resolve'), + () => done.fail('Expected promise not to reject')); - // We can't wait forever to verify that the promise is not resolved, but at least verify - // that it is not resolved immediately. - setTimeout(done, 100); - }); + // We can't wait forever to verify that the promise is not resolved, but at least verify + // that it is not resolved immediately. + setTimeout(done, 100); + }); - it('should handle `process-task` messages', () => { - const mockTask = { - entryPoint: {name: 'foo'}, - formatProperty: 'es2015', - processDts: true, - } as unknown as Task; + it('should handle `process-task` messages', () => { + const mockTask = { + entryPoint: {name: 'foo'}, + formatProperty: 'es2015', + processDts: true, + } as unknown as Task; - worker.run(); - cluster.worker.emit('message', {type: 'process-task', task: mockTask}); + startWorker(mockLogger, createCompileFnSpy); + cluster.worker.emit('message', {type: 'process-task', task: mockTask}); - expect(compileFnSpy).toHaveBeenCalledWith(mockTask); - expect(processSendSpy).not.toHaveBeenCalled(); + expect(compileFnSpy).toHaveBeenCalledWith(mockTask); + expect(processSendSpy).not.toHaveBeenCalled(); - expect(mockLogger.logs.debug[0]).toEqual([ - '[Worker #42] Processing task: {entryPoint: foo, formatProperty: es2015, processDts: true}', - ]); - }); + expect(mockLogger.logs.debug[0]).toEqual([ + '[Worker #42] Processing task: {entryPoint: foo, formatProperty: es2015, processDts: true}', + ]); + }); - it('should send errors during task processing back to the master process', () => { - const mockTask = { - entryPoint: {name: 'foo'}, - formatProperty: 'es2015', - processDts: true, - } as unknown as Task; + it('should send errors during task processing back to the master process', () => { + const mockTask = { + entryPoint: {name: 'foo'}, + formatProperty: 'es2015', + processDts: true, + } as unknown as Task; - let err: string|Error; - compileFnSpy.and.callFake(() => { throw err; }); + let err: string|Error; + compileFnSpy.and.callFake(() => { + throw err; + }); - worker.run(); + startWorker(mockLogger, createCompileFnSpy); - err = 'Error string.'; - cluster.worker.emit('message', {type: 'process-task', task: mockTask}); - expect(processSendSpy).toHaveBeenCalledWith({type: 'error', error: err}); + err = 'Error string.'; + cluster.worker.emit('message', {type: 'process-task', task: mockTask}); + expect(processSendSpy).toHaveBeenCalledWith({type: 'error', error: err}); - err = new Error('Error object.'); - cluster.worker.emit('message', {type: 'process-task', task: mockTask}); - expect(processSendSpy).toHaveBeenCalledWith({type: 'error', error: err.stack}); - }); + err = new Error('Error object.'); + cluster.worker.emit('message', {type: 'process-task', task: mockTask}); + expect(processSendSpy).toHaveBeenCalledWith({type: 'error', error: err.stack}); + }); - it('should throw, when an unknown message type is received', () => { - worker.run(); - cluster.worker.emit('message', {type: 'unknown', foo: 'bar'}); + it('should throw, when an unknown message type is received', () => { + startWorker(mockLogger, createCompileFnSpy); + cluster.worker.emit('message', {type: 'unknown', foo: 'bar'}); - expect(compileFnSpy).not.toHaveBeenCalled(); - expect(processSendSpy).toHaveBeenCalledWith({ - type: 'error', - error: jasmine.stringMatching( - 'Error: \\[Worker #42\\] Invalid message received: {"type":"unknown","foo":"bar"}'), - }); + expect(compileFnSpy).not.toHaveBeenCalled(); + expect(processSendSpy).toHaveBeenCalledWith({ + type: 'error', + error: jasmine.stringMatching( + 'Error: \\[Worker #42\\] Invalid message received: {"type":"unknown","foo":"bar"}'), }); }); }); diff --git a/packages/compiler-cli/ngcc/test/execution/helpers.ts b/packages/compiler-cli/ngcc/test/execution/helpers.ts index 0cd3fbd13977a..a72ad23452c90 100644 --- a/packages/compiler-cli/ngcc/test/execution/helpers.ts +++ b/packages/compiler-cli/ngcc/test/execution/helpers.ts @@ -14,8 +14,8 @@ import {EntryPoint} from '../../src/packages/entry_point'; * * NOTE 1: The first task for each entry-point generates typings (which is similar to what happens * in the actual code). - * NOTE 2: The `computeTaskDependencies()` implementation relies on the fact that tasks are sorted in such - * a way that a task can only depend upon earlier tasks (i.e. dependencies always come + * NOTE 2: The `computeTaskDependencies()` implementation relies on the fact that tasks are sorted + * in such a way that a task can only depend upon earlier tasks (i.e. dependencies always come * before dependents in the list of tasks). * To preserve this attribute, you need to ensure that entry-points will only depend on * entry-points with a lower index. Take this into account when defining `entryPointDeps`. @@ -52,7 +52,7 @@ export function createTasksAndGraph( graph.addNode(entryPoint.path); for (let tIdx = 0; tIdx < tasksPerEntryPointCount; tIdx++) { - tasks.push({ entryPoint, formatProperty: `prop-${tIdx}`, processDts: tIdx === 0 } as Task); + tasks.push({entryPoint, formatProperty: `prop-${tIdx}`, processDts: tIdx === 0} as Task); } } diff --git a/packages/compiler-cli/ngcc/test/execution/single_processor_executor_spec.ts b/packages/compiler-cli/ngcc/test/execution/single_processor_executor_spec.ts index b6e7c09db8e9e..a1fdb16e79449 100644 --- a/packages/compiler-cli/ngcc/test/execution/single_processor_executor_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/single_processor_executor_spec.ts @@ -33,11 +33,13 @@ describe('SingleProcessExecutor', () => { executor = new SingleProcessExecutorSync(mockLogger, locker, createTaskCompletedCallback); }); - const noTasks = () => ({ allTasksCompleted: true, getNextTask: () => null } as TaskQueue); + const noTasks = () => ({allTasksCompleted: true, getNextTask: () => null} as TaskQueue); const oneTask = () => { let tasksCount = 1; return <TaskQueue>{ - get allTasksCompleted() { return tasksCount === 0; }, + get allTasksCompleted() { + return tasksCount === 0; + }, getNextTask() { tasksCount--; return {}; @@ -55,7 +57,9 @@ describe('SingleProcessExecutor', () => { }); it('should call LockFile.write() and LockFile.remove() if `analyzeEntryPoints` fails', () => { - const errorFn: () => never = () => { throw new Error('analyze error'); }; + const errorFn: () => never = () => { + throw new Error('analyze error'); + }; const createCompileFn: () => any = () => undefined; let error: string = ''; try { @@ -68,7 +72,9 @@ describe('SingleProcessExecutor', () => { }); it('should call LockFile.write() and LockFile.remove() if `createCompileFn` fails', () => { - const createErrorCompileFn: () => any = () => { throw new Error('compile error'); }; + const createErrorCompileFn: () => any = () => { + throw new Error('compile error'); + }; let error: string = ''; try { executor.execute(oneTask, createErrorCompileFn); @@ -85,7 +91,9 @@ describe('SingleProcessExecutor', () => { throw new Error('LockFile.write() error'); }); - const analyzeFn: () => any = () => { lockFileLog.push('analyzeFn'); }; + const analyzeFn: () => any = () => { + lockFileLog.push('analyzeFn'); + }; const anyFn: () => any = () => undefined; executor = new SingleProcessExecutorSync(mockLogger, locker, createTaskCompletedCallback); let error = ''; diff --git a/packages/compiler-cli/ngcc/test/execution/tasks/queues/parallel_task_queue_spec.ts b/packages/compiler-cli/ngcc/test/execution/tasks/queues/parallel_task_queue_spec.ts index 03d196fe374d5..cb524d970e29d 100644 --- a/packages/compiler-cli/ngcc/test/execution/tasks/queues/parallel_task_queue_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/tasks/queues/parallel_task_queue_spec.ts @@ -77,9 +77,9 @@ describe('ParallelTaskQueue', () => { it('should be `true`, when there are no unprocess or in-progress tasks', () => { const {queue} = createQueue(3); - const task1 = queue.getNextTask() !; - const task2 = queue.getNextTask() !; - const task3 = queue.getNextTask() !; + const task1 = queue.getNextTask()!; + const task2 = queue.getNextTask()!; + const task3 = queue.getNextTask()!; expect(queue.allTasksCompleted).toBe(false); queue.markTaskCompleted(task1); @@ -266,8 +266,8 @@ describe('ParallelTaskQueue', () => { it('should mark a task as completed', () => { const {queue} = createQueue(2); - const task1 = queue.getNextTask() !; - const task2 = queue.getNextTask() !; + const task1 = queue.getNextTask()!; + const task2 = queue.getNextTask()!; expect(queue.allTasksCompleted).toBe(false); queue.markTaskCompleted(task1); @@ -327,7 +327,7 @@ describe('ParallelTaskQueue', () => { processNextTask(queue2); processNextTask(queue2); - const task = queue2.getNextTask() !; + const task = queue2.getNextTask()!; expect(queue2.toString()).toContain(' All tasks completed: false\n'); @@ -344,7 +344,7 @@ describe('ParallelTaskQueue', () => { ' - {entryPoint: entry-point-1, formatProperty: prop-0, processDts: true}\n' + ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n'); - const task1 = queue.getNextTask() !; + const task1 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' Unprocessed tasks (2): \n' + @@ -352,7 +352,7 @@ describe('ParallelTaskQueue', () => { ' - {entryPoint: entry-point-2, formatProperty: prop-0, processDts: true}\n'); queue.markTaskCompleted(task1); - const task2 = queue.getNextTask() !; + const task2 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' Unprocessed tasks (1): \n' + @@ -367,14 +367,14 @@ describe('ParallelTaskQueue', () => { const {queue} = createQueue(3); expect(queue.toString()).toContain(' In-progress tasks (0): \n'); - const task1 = queue.getNextTask() !; + const task1 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' In-progress tasks (1): \n' + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}\n'); queue.markTaskCompleted(task1); - const task2 = queue.getNextTask() !; + const task2 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' In-progress tasks (1): \n' + diff --git a/packages/compiler-cli/ngcc/test/execution/tasks/queues/serial_task_queue_spec.ts b/packages/compiler-cli/ngcc/test/execution/tasks/queues/serial_task_queue_spec.ts index ea53af115e01c..c5cd05c5f4fce 100644 --- a/packages/compiler-cli/ngcc/test/execution/tasks/queues/serial_task_queue_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/tasks/queues/serial_task_queue_spec.ts @@ -30,12 +30,10 @@ describe('SerialTaskQueue', () => { const tasks: PartiallyOrderedTasks = [] as any; const graph = new DepGraph<EntryPoint>(); for (let i = 0; i < taskCount; i++) { - const entryPoint = { - name: `entry-point-${i}`, - path: `/path/to/entry/point/${i}` - } as EntryPoint; + const entryPoint = {name: `entry-point-${i}`, path: `/path/to/entry/point/${i}`} as + EntryPoint; tasks.push( - { entryPoint: entryPoint, formatProperty: `prop-${i}`, processDts: i % 2 === 0 } as Task); + {entryPoint: entryPoint, formatProperty: `prop-${i}`, processDts: i % 2 === 0} as Task); graph.addNode(entryPoint.path); } const dependencies = computeTaskDependencies(tasks, graph); @@ -140,7 +138,7 @@ describe('SerialTaskQueue', () => { describe('markTaskCompleted()', () => { it('should mark a task as completed, so that the next task can be picked', () => { const {queue} = createQueue(3); - const task = queue.getNextTask() !; + const task = queue.getNextTask()!; expect(() => queue.getNextTask()).toThrow(); @@ -174,7 +172,7 @@ describe('SerialTaskQueue', () => { processNextTask(queue2); processNextTask(queue2); - const task = queue2.getNextTask() !; + const task = queue2.getNextTask()!; expect(queue2.toString()).toContain(' All tasks completed: false\n'); @@ -191,7 +189,7 @@ describe('SerialTaskQueue', () => { ' - {entryPoint: entry-point-1, formatProperty: prop-1, processDts: false}\n' + ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}\n'); - const task1 = queue.getNextTask() !; + const task1 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' Unprocessed tasks (2): \n' + @@ -199,7 +197,7 @@ describe('SerialTaskQueue', () => { ' - {entryPoint: entry-point-2, formatProperty: prop-2, processDts: true}\n'); queue.markTaskCompleted(task1); - const task2 = queue.getNextTask() !; + const task2 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' Unprocessed tasks (1): \n' + @@ -214,14 +212,14 @@ describe('SerialTaskQueue', () => { const {queue} = createQueue(3); expect(queue.toString()).toContain(' In-progress tasks (0): '); - const task1 = queue.getNextTask() !; + const task1 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' In-progress tasks (1): \n' + ' - {entryPoint: entry-point-0, formatProperty: prop-0, processDts: true}'); queue.markTaskCompleted(task1); - const task2 = queue.getNextTask() !; + const task2 = queue.getNextTask()!; expect(queue.toString()) .toContain( ' In-progress tasks (1): \n' + @@ -253,7 +251,7 @@ describe('SerialTaskQueue', () => { ' In-progress tasks (0): '); processNextTask(queue2); - const task = queue2.getNextTask() !; + const task = queue2.getNextTask()!; expect(queue2.toString()) .toBe( 'SerialTaskQueue\n' + diff --git a/packages/compiler-cli/ngcc/test/helpers/mock_lock_file.ts b/packages/compiler-cli/ngcc/test/helpers/mock_lock_file.ts index c4271216b8eb8..5917df00a5f56 100644 --- a/packages/compiler-cli/ngcc/test/helpers/mock_lock_file.ts +++ b/packages/compiler-cli/ngcc/test/helpers/mock_lock_file.ts @@ -15,10 +15,14 @@ export class MockLockFile implements LockFile { constructor( fs: FileSystem, private log: string[] = [], public path = fs.resolve('/lockfile'), private pid = '1234') {} - write() { this.log.push('write()'); } + write() { + this.log.push('write()'); + } read(): string { this.log.push('read()'); return this.pid; } - remove() { this.log.push('remove()'); } + remove() { + this.log.push('remove()'); + } } diff --git a/packages/compiler-cli/ngcc/test/helpers/mock_logger.ts b/packages/compiler-cli/ngcc/test/helpers/mock_logger.ts index bc686a994f6c7..83e5afe09d23d 100644 --- a/packages/compiler-cli/ngcc/test/helpers/mock_logger.ts +++ b/packages/compiler-cli/ngcc/test/helpers/mock_logger.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {LogLevel, Logger} from '../../src/logging/logger'; +import {Logger, LogLevel} from '../../src/logging/logger'; export class MockLogger implements Logger { constructor(public level = LogLevel.info) {} @@ -17,8 +17,16 @@ export class MockLogger implements Logger { warn: [], error: [], }; - debug(...args: string[]) { this.logs.debug.push(args); } - info(...args: string[]) { this.logs.info.push(args); } - warn(...args: string[]) { this.logs.warn.push(args); } - error(...args: string[]) { this.logs.error.push(args); } + debug(...args: string[]) { + this.logs.debug.push(args); + } + info(...args: string[]) { + this.logs.info.push(args); + } + warn(...args: string[]) { + this.logs.warn.push(args); + } + error(...args: string[]) { + this.logs.error.push(args); + } } \ No newline at end of file diff --git a/packages/compiler-cli/ngcc/test/helpers/utils.ts b/packages/compiler-cli/ngcc/test/helpers/utils.ts index 781be94e3d573..cfd4315b1aada 100644 --- a/packages/compiler-cli/ngcc/test/helpers/utils.ts +++ b/packages/compiler-cli/ngcc/test/helpers/utils.ts @@ -7,7 +7,7 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, NgtscCompilerHost, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, getFileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system'; import {TestFile} from '../../../src/ngtsc/file_system/testing'; import {BundleProgram, makeBundleProgram} from '../../src/packages/bundle_program'; import {NgccEntryPointConfig} from '../../src/packages/configuration'; @@ -49,7 +49,12 @@ export function makeTestEntryPointBundle( return { entryPoint, format, - rootDirs: [absoluteFrom('/')], src, dts, isCore, isFlatCore, enableI18nLegacyMessageIdFormat + rootDirs: [absoluteFrom('/')], + src, + dts, + isCore, + isFlatCore, + enableI18nLegacyMessageIdFormat }; } diff --git a/packages/compiler-cli/ngcc/test/host/commonjs_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/commonjs_host_import_helper_spec.ts index 0d9554a810473..cb3e012f4e2bf 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_import_helper_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_import_helper_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; @@ -90,15 +90,15 @@ exports.AliasedDirective$1 = AliasedDirective$1; const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('core.Directive'); + expect(decorator.identifier!.getText()).toEqual('core.Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -111,15 +111,15 @@ exports.AliasedDirective$1 = AliasedDirective$1; const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'AliasedDirective$1', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('core.Directive'); + expect(decorator.identifier!.getText()).toEqual('core.Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); diff --git a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts index 3ebd4f1fd526c..b4a4231371e53 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts @@ -8,12 +8,15 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {ClassMemberKind, CtorParameter, InlineDeclaration, KnownDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; +import {ClassMemberKind, CtorParameter, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {CommonJsReflectionHost} from '../../src/host/commonjs_host'; +import {DelegatingReflectionHost} from '../../src/host/delegating_host'; import {getIifeBody} from '../../src/host/esm5_host'; +import {NgccReflectionHost} from '../../src/host/ngcc_host'; +import {BundleProgram} from '../../src/packages/bundle_program'; import {MockLogger} from '../helpers/mock_logger'; import {getRootFiles, makeTestBundleProgram, makeTestDtsBundleProgram} from '../helpers/utils'; @@ -21,7 +24,6 @@ import {expectTypeValueReferencesForParameters} from './util'; runInEachFileSystem(() => { describe('CommonJsReflectionHost', () => { - let _: typeof absoluteFrom; let SOME_DIRECTIVE_FILE: TestFile; @@ -45,6 +47,12 @@ runInEachFileSystem(() => { let TYPINGS_DTS_FILES: TestFile[]; let MODULE_WITH_PROVIDERS_PROGRAM: TestFile[]; + // Helpers + const createHost = (bundle: BundleProgram, ngccHost: CommonJsReflectionHost) => { + const tsHost = new TypeScriptReflectionHost(bundle.program.getTypeChecker()); + return new DelegatingReflectionHost(tsHost, ngccHost); + }; + beforeEach(() => { _ = absoluteFrom; @@ -916,16 +924,16 @@ exports.ExternalModule = ExternalModule; }); describe('CommonJsReflectionHost', () => { - describe('getDecoratorsOfDeclaration()', () => { it('should find the decorators on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); @@ -933,7 +941,7 @@ exports.ExternalModule = ExternalModule; const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -941,11 +949,12 @@ exports.ExternalModule = ExternalModule; it('should find the decorators on a class at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); @@ -953,7 +962,7 @@ exports.ExternalModule = ExternalModule; const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -961,7 +970,8 @@ exports.ExternalModule = ExternalModule; it('should return null if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); const decorators = host.getDecoratorsOfDeclaration(functionNode); @@ -971,7 +981,8 @@ exports.ExternalModule = ExternalModule; it('should return null if there are no decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode); @@ -981,7 +992,8 @@ exports.ExternalModule = ExternalModule; it('should ignore `decorators` if it is not an array literal', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); @@ -992,11 +1004,12 @@ exports.ExternalModule = ExternalModule; it('should ignore decorator elements that are not object literals', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1005,11 +1018,12 @@ exports.ExternalModule = ExternalModule; it('should ignore decorator elements that have no `type` property', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1018,11 +1032,12 @@ exports.ExternalModule = ExternalModule; it('should ignore decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1032,11 +1047,12 @@ exports.ExternalModule = ExternalModule; it('should be an empty array if decorator has no `args` property', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1046,11 +1062,12 @@ exports.ExternalModule = ExternalModule; it('should be an empty array if decorator\'s `args` has no property assignment', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1060,11 +1077,12 @@ exports.ExternalModule = ExternalModule; it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1077,94 +1095,100 @@ exports.ExternalModule = ExternalModule; it('should find decorated members on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); it('should find decorated members on a class at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); it('should find non decorated properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const instanceProperty = members.find(member => member.name === 'instanceProperty') !; + const instanceProperty = members.find(member => member.name === 'instanceProperty')!; expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); expect(instanceProperty.isStatic).toEqual(false); - expect(ts.isBinaryExpression(instanceProperty.implementation !)).toEqual(true); - expect(instanceProperty.value !.getText()).toEqual(`'instance'`); + expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); + expect(instanceProperty.value!.getText()).toEqual(`'instance'`); }); it('should find static methods on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticMethod = members.find(member => member.name === 'staticMethod') !; + const staticMethod = members.find(member => member.name === 'staticMethod')!; expect(staticMethod.kind).toEqual(ClassMemberKind.Method); expect(staticMethod.isStatic).toEqual(true); - expect(ts.isFunctionExpression(staticMethod.implementation !)).toEqual(true); + expect(ts.isFunctionExpression(staticMethod.implementation!)).toEqual(true); }); it('should find static properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticProperty = members.find(member => member.name === 'staticProperty') !; + const staticProperty = members.find(member => member.name === 'staticProperty')!; expect(staticProperty.kind).toEqual(ClassMemberKind.Property); expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation !)).toEqual(true); - expect(staticProperty.value !.getText()).toEqual(`'static'`); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); }); it('should throw if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); expect(() => { @@ -1175,7 +1199,8 @@ exports.ExternalModule = ExternalModule; it('should return an empty array if there are no prop decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); @@ -1187,7 +1212,8 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); @@ -1199,13 +1225,14 @@ exports.ExternalModule = ExternalModule; it('should ignore prop decorator elements that are not object literals', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1214,13 +1241,14 @@ exports.ExternalModule = ExternalModule; it('should ignore prop decorator elements that have no `type` property', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1229,13 +1257,14 @@ exports.ExternalModule = ExternalModule; it('should ignore prop decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1244,12 +1273,13 @@ exports.ExternalModule = ExternalModule; it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); @@ -1259,13 +1289,14 @@ exports.ExternalModule = ExternalModule; it('should be an empty array if prop decorator has no `args` property', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1276,13 +1307,14 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1292,13 +1324,14 @@ exports.ExternalModule = ExternalModule; it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1311,17 +1344,18 @@ exports.ExternalModule = ExternalModule; it('should find the decorated constructor parameters', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, @@ -1331,17 +1365,18 @@ exports.ExternalModule = ExternalModule; it('should find the decorated constructor parameters at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, @@ -1351,11 +1386,12 @@ exports.ExternalModule = ExternalModule; it('should accept `ctorParameters` as an array', () => { loadTestFiles([CTOR_DECORATORS_ARRAY_FILE]); const bundle = makeTestBundleProgram(CTOR_DECORATORS_ARRAY_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, CTOR_DECORATORS_ARRAY_FILE.name, 'CtorDecoratedAsArray', isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters).toBeDefined(); expect(parameters.map(parameter => parameter.name)).toEqual(['arg1']); @@ -1365,10 +1401,13 @@ exports.ExternalModule = ExternalModule; it('should throw if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(() => { host.getConstructorParameters(functionNode); }) + expect(() => { + host.getConstructorParameters(functionNode); + }) .toThrowError( 'Attempted to get constructor parameters of a non-class: "function foo() {}"'); }); @@ -1379,22 +1418,24 @@ exports.ExternalModule = ExternalModule; it('should return an array even if there are no decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toEqual(jasmine.any(Array)); - expect(parameters !.length).toEqual(1); - expect(parameters ![0].name).toEqual('foo'); - expect(parameters ![0].decorators).toBe(null); + expect(parameters!.length).toEqual(1); + expect(parameters![0].name).toEqual('foo'); + expect(parameters![0].decorators).toBe(null); }); it('should return an empty array if there are no constructor parameters', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', isNamedVariableDeclaration); @@ -1409,14 +1450,15 @@ exports.ExternalModule = ExternalModule; it('should ignore `ctorParameters` if it does not return an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(1); - expect(parameters ![0]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters!.length).toBe(1); + expect(parameters![0]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg1', decorators: null, })); @@ -1426,18 +1468,19 @@ exports.ExternalModule = ExternalModule; it('should ignore param decorator elements that are not object literals', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(2); - expect(parameters ![0]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters!.length).toBe(2); + expect(parameters![0]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg1', decorators: null, })); - expect(parameters ![1]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters![1]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg2', decorators: jasmine.any(Array) as any })); @@ -1446,12 +1489,13 @@ exports.ExternalModule = ExternalModule; it('should ignore param decorator elements that have no `type` property', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); @@ -1461,12 +1505,13 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); @@ -1475,13 +1520,14 @@ exports.ExternalModule = ExternalModule; it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![2].decorators !; + const decorators = parameters![2].decorators!; expect(decorators.length).toEqual(1); expect(decorators[0].name).toBe('Inject'); @@ -1493,13 +1539,14 @@ exports.ExternalModule = ExternalModule; it('should be an empty array if param decorator has no `args` property', () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(1); - const decorators = parameters ![0].decorators !; + expect(parameters!.length).toBe(1); + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1510,12 +1557,13 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1525,12 +1573,13 @@ exports.ExternalModule = ExternalModule; it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1544,45 +1593,46 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles([FUNCTION_BODY_FILE]); const bundle = makeTestBundleProgram(FUNCTION_BODY_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const fooNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration) !; - const fooDef = host.getDefinitionOfFunction(fooNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration)!; + const fooDef = host.getDefinitionOfFunction(fooNode)!; expect(fooDef.node).toBe(fooNode); - expect(fooDef.body !.length).toEqual(1); - expect(fooDef.body ![0].getText()).toEqual(`return x;`); + expect(fooDef.body!.length).toEqual(1); + expect(fooDef.body![0].getText()).toEqual(`return x;`); expect(fooDef.parameters.length).toEqual(1); expect(fooDef.parameters[0].name).toEqual('x'); expect(fooDef.parameters[0].initializer).toBe(null); const barNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration) !; - const barDef = host.getDefinitionOfFunction(barNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration)!; + const barDef = host.getDefinitionOfFunction(barNode)!; expect(barDef.node).toBe(barNode); - expect(barDef.body !.length).toEqual(1); - expect(ts.isReturnStatement(barDef.body ![0])).toBeTruthy(); - expect(barDef.body ![0].getText()).toEqual(`return x + y;`); + expect(barDef.body!.length).toEqual(1); + expect(ts.isReturnStatement(barDef.body![0])).toBeTruthy(); + expect(barDef.body![0].getText()).toEqual(`return x + y;`); expect(barDef.parameters.length).toEqual(2); expect(barDef.parameters[0].name).toEqual('x'); expect(fooDef.parameters[0].initializer).toBe(null); expect(barDef.parameters[1].name).toEqual('y'); - expect(barDef.parameters[1].initializer !.getText()).toEqual('42'); + expect(barDef.parameters[1].initializer!.getText()).toEqual('42'); const bazNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration) !; - const bazDef = host.getDefinitionOfFunction(bazNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration)!; + const bazDef = host.getDefinitionOfFunction(bazNode)!; expect(bazDef.node).toBe(bazNode); - expect(bazDef.body !.length).toEqual(3); + expect(bazDef.body!.length).toEqual(3); expect(bazDef.parameters.length).toEqual(1); expect(bazDef.parameters[0].name).toEqual('x'); expect(bazDef.parameters[0].initializer).toBe(null); const quxNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration) !; - const quxDef = host.getDefinitionOfFunction(quxNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration)!; + const quxDef = host.getDefinitionOfFunction(quxNode)!; expect(quxDef.node).toBe(quxNode); - expect(quxDef.body !.length).toEqual(2); + expect(quxDef.body!.length).toEqual(2); expect(quxDef.parameters.length).toEqual(1); expect(quxDef.parameters[0].name).toEqual('x'); expect(quxDef.parameters[0].initializer).toBe(null); @@ -1593,7 +1643,8 @@ exports.ExternalModule = ExternalModule; it('should find the import of an identifier', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/file_b.js'), 'b', isNamedVariableDeclaration); const identifier = (variableNode.initializer && @@ -1603,14 +1654,15 @@ exports.ExternalModule = ExternalModule; null; expect(identifier).not.toBe(null); - const importOfIdent = host.getImportOfIdentifier(identifier !); + const importOfIdent = host.getImportOfIdentifier(identifier!); expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); }); it('should return null if the identifier was not imported', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/file_b.js'), 'd', isNamedVariableDeclaration); const importOfIdent = @@ -1622,7 +1674,8 @@ exports.ExternalModule = ExternalModule; it('should handle factory functions not wrapped in parentheses', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/file_c.js'), 'c', isNamedVariableDeclaration); const identifier = (variableNode.initializer && @@ -1632,7 +1685,7 @@ exports.ExternalModule = ExternalModule; null; expect(identifier).not.toBe(null); - const importOfIdent = host.getImportOfIdentifier(identifier !); + const importOfIdent = host.getImportOfIdentifier(identifier!); expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); }); }); @@ -1640,10 +1693,10 @@ exports.ExternalModule = ExternalModule; describe('getDeclarationOfIdentifier', () => { // Helpers const createTestForTsHelper = - (program: ts.Program, host: CommonJsReflectionHost, srcFile: TestFile, + (program: ts.Program, host: NgccReflectionHost, srcFile: TestFile, getHelperDeclaration: (name: string) => ts.Declaration) => (varName: string, helperName: string, knownAs: KnownDeclaration, - viaModule: string | null = null) => { + viaModule: string|null = null) => { const node = getDeclaration(program, srcFile.name, varName, ts.isVariableDeclaration); const helperIdentifier = getIdentifierFromCallExpression(node); @@ -1651,7 +1704,8 @@ exports.ExternalModule = ExternalModule; expect(helperDeclaration).toEqual({ known: knownAs, - node: getHelperDeclaration(helperName), viaModule, + node: getHelperDeclaration(helperName), + viaModule, }); }; @@ -1667,12 +1721,13 @@ exports.ExternalModule = ExternalModule; it('should return the declaration of a locally defined identifier', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const ctrDecorators = host.getConstructorParameters(classNode) !; - const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{ + const ctrDecorators = host.getConstructorParameters(classNode)!; + const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { local: true, expression: ts.Identifier, defaultImportStatement: null, @@ -1683,8 +1738,8 @@ exports.ExternalModule = ExternalModule; isNamedVariableDeclaration); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe(null); }); it('should return the correct declaration for an outer alias identifier', () => { @@ -1702,7 +1757,8 @@ exports.ExternalModule = ExternalModule; loadTestFiles([PROGRAM_FILE]); const bundle = makeTestBundleProgram(PROGRAM_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const expectedDeclaration = getDeclaration( bundle.program, PROGRAM_FILE.name, 'AliasedClass', isNamedVariableDeclaration); @@ -1713,29 +1769,31 @@ exports.ExternalModule = ExternalModule; expect(aliasIdentifier.getText()).toBe('AliasedClass_1'); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclaration); + expect(actualDeclaration!.node).toBe(expectedDeclaration); }); it('should return the source-file of an import namespace', () => { loadFakeCore(getFileSystem()); loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode) !; - const identifierOfDirective = (((classDecorators[0].node as ts.ObjectLiteralExpression) - .properties[0] as ts.PropertyAssignment) - .initializer as ts.PropertyAccessExpression) - .expression as ts.Identifier; + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const identifierOfDirective = + (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as + ts.PropertyAssignment) + .initializer as ts.PropertyAccessExpression) + .expression as ts.Identifier; const expectedDeclarationNode = getSourceFileOrError(bundle.program, _('/node_modules/@angular/core/index.d.ts')); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe('@angular/core'); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); it('should return viaModule: null for relative imports', () => { @@ -1756,12 +1814,13 @@ exports.ExternalModule = ExternalModule; ]); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/index.js'), 'b', isNamedVariableDeclaration); const identifier = variableNode.name as ts.Identifier; - const importOfIdent = host.getDeclarationOfIdentifier(identifier !) !; + const importOfIdent = host.getDeclarationOfIdentifier(identifier!)!; expect(importOfIdent.node).not.toBeNull(); expect(importOfIdent.viaModule).toBeNull(); }); @@ -1782,13 +1841,14 @@ exports.ExternalModule = ExternalModule; ]); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/index.js'), 'b', isNamedVariableDeclaration); const identifier = - (variableNode.initializer !as ts.PropertyAccessExpression).name as ts.Identifier; + (variableNode.initializer! as ts.PropertyAccessExpression).name as ts.Identifier; - const importOfIdent = host.getDeclarationOfIdentifier(identifier !) !; + const importOfIdent = host.getDeclarationOfIdentifier(identifier!)!; expect(importOfIdent.viaModule).toBe('lib'); }); @@ -1807,7 +1867,8 @@ exports.ExternalModule = ExternalModule; }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, file, @@ -1834,7 +1895,8 @@ exports.ExternalModule = ExternalModule; }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, file, @@ -1861,7 +1923,8 @@ exports.ExternalModule = ExternalModule; }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, file, @@ -1888,7 +1951,8 @@ exports.ExternalModule = ExternalModule; }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, file, @@ -1925,7 +1989,8 @@ exports.ExternalModule = ExternalModule; const [testFile, tslibFile] = files; const bundle = makeTestBundleProgram(testFile.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const tslibSourceFile = getSourceFileOrError(bundle.program, tslibFile.name); const testForHelper = @@ -1935,6 +2000,74 @@ exports.ExternalModule = ExternalModule; testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); }); + + it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + var a = __assign({foo: 'bar'}, {baz: 'qux'}); + var b = __spread(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); + + const testForHelper = + (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = + getDeclaration(bundle.program, file.name, varName, ts.isVariableDeclaration); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + known: knownAs, + expression: helperIdentifier, + node: null, + viaModule: null, + }); + }; + + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + }); + + it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); + var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); + + const testForHelper = + (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = + getDeclaration(bundle.program, file.name, varName, ts.isVariableDeclaration); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + known: knownAs, + expression: helperIdentifier, + node: null, + viaModule: null, + }); + }; + + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + }); }); describe('getExportsOfModule()', () => { @@ -1942,12 +2075,13 @@ exports.ExternalModule = ExternalModule; loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/b_module.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, '@angular/core'], ['a', `a = 'a'`, null], @@ -1968,13 +2102,14 @@ exports.ExternalModule = ExternalModule; loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports_emitted_helpers.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], ['a', `a = 'a'`, _('/b_module')], @@ -1997,13 +2132,14 @@ exports.ExternalModule = ExternalModule; loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], ['a', `a = 'a'`, _('/b_module')], @@ -2025,11 +2161,12 @@ exports.ExternalModule = ExternalModule; it('should handle inline exports', () => { loadTestFiles([INLINE_EXPORT_FILE]); const bundle = makeTestBundleProgram(_('/inline_export.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/inline_export.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBeNull(); - const decl = exportDeclarations !.get('directives') as InlineDeclaration; + const decl = exportDeclarations!.get('directives') as InlineDeclaration; expect(decl).not.toBeUndefined(); expect(decl.node).toBeNull(); expect(decl.expression).toBeDefined(); @@ -2047,9 +2184,10 @@ exports.ExternalModule = ExternalModule; }; loadTestFiles([tslib]); const bundle = makeTestBundleProgram(tslib.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const sf = getSourceFileOrError(bundle.program, tslib.name); - const exportDeclarations = host.getExportsOfModule(sf) !; + const exportDeclarations = host.getExportsOfModule(sf)!; expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known])) .toEqual([ @@ -2065,56 +2203,59 @@ exports.ExternalModule = ExternalModule; it('should return the class symbol for an ES2015 class', () => { loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); const classSymbol = host.getClassSymbol(node); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(node); - expect(classSymbol !.implementation.valueDeclaration).toBe(node); + expect(classSymbol!.declaration.valueDeclaration).toBe(node); + expect(classSymbol!.implementation.valueDeclaration).toBe(node); }); it('should return the class symbol for an ES5 class (outer variable declaration)', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the class symbol for an ES5 class (inner function declaration)', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(innerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the same class symbol (of the outer declaration) for outer and inner declarations', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = - getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; - const innerSymbol = host.getClassSymbol(innerNode) !; - const outerSymbol = host.getClassSymbol(outerNode) !; + const innerSymbol = host.getClassSymbol(innerNode)!; + const outerSymbol = host.getClassSymbol(outerNode)!; expect(innerSymbol.declaration).toBe(outerSymbol.declaration); expect(innerSymbol.implementation).toBe(outerSymbol.implementation); }); @@ -2123,40 +2264,41 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', isNamedVariableDeclaration); - const innerNode = - getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the class symbol for an ES5 class whose IIFE is not wrapped with inner parens', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', isNamedVariableDeclaration); - const innerNode = - getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return undefined if node is not an ES5 class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); const classSymbol = host.getClassSymbol(node); @@ -2172,7 +2314,8 @@ exports.ExternalModule = ExternalModule; }; loadTestFiles([testFile]); const bundle = makeTestBundleProgram(testFile.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, testFile.name, 'MyClass', isNamedVariableDeclaration); const classSymbol = host.getClassSymbol(node); @@ -2185,7 +2328,8 @@ exports.ExternalModule = ExternalModule; it('should return true if a given node is a TS class declaration', () => { loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); expect(host.isClass(node)).toBe(true); @@ -2195,7 +2339,8 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); expect(host.isClass(node)).toBe(true); @@ -2205,18 +2350,19 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); - const innerNode = - getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; expect(host.isClass(innerNode)).toBe(true); }); it('should return false if a given node is a function declaration', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); expect(host.isClass(node)).toBe(false); @@ -2232,7 +2378,8 @@ exports.ExternalModule = ExternalModule; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); return host.hasBaseClass(classNode); @@ -2279,7 +2426,8 @@ exports.ExternalModule = ExternalModule; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); const expression = host.getBaseClassExpression(classNode); @@ -2301,7 +2449,7 @@ exports.ExternalModule = ExternalModule; function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier !.text).toBe('BaseClass'); + expect(identifier!.text).toBe('BaseClass'); }); it('should find the base class of an IIFE with a unique name generated for the _super parameter', @@ -2316,7 +2464,7 @@ exports.ExternalModule = ExternalModule; function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier !.text).toBe('BaseClass'); + expect(identifier!.text).toBe('BaseClass'); }); it('should not find a base class for an IIFE without parameter', () => { @@ -2351,10 +2499,11 @@ exports.ExternalModule = ExternalModule; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - const expression = host.getBaseClassExpression(classNode) !; + const expression = host.getBaseClassExpression(classNode)!; expect(expression.getText()).toBe('foo()'); }); }); @@ -2363,7 +2512,8 @@ exports.ExternalModule = ExternalModule; it('should return an array of all classes in the given source file', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(getRootFiles(DECORATED_FILES)[0]); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); @@ -2381,7 +2531,8 @@ exports.ExternalModule = ExternalModule; it('should return decorators of class symbol', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(getRootFiles(DECORATED_FILES)[0]); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); @@ -2389,14 +2540,14 @@ exports.ExternalModule = ExternalModule; const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s)); expect(classDecoratorsPrimary.length).toEqual(2); - expect(classDecoratorsPrimary[0] !.map(d => d.name)).toEqual(['Directive']); - expect(classDecoratorsPrimary[1] !.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[0]!.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[1]!.map(d => d.name)).toEqual(['Directive']); const classSymbolsSecondary = host.findClassSymbols(secondaryFile); const classDecoratorsSecondary = classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s)); expect(classDecoratorsSecondary.length).toEqual(1); - expect(classDecoratorsSecondary[0] !.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsSecondary[0]!.map(d => d.name)).toEqual(['Directive']); }); }); @@ -2409,11 +2560,11 @@ exports.ExternalModule = ExternalModule; const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/class1.js'), 'Class1', ts.isVariableDeclaration); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const host = createHost( + bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find the dts declaration for exported functions', () => { @@ -2423,9 +2574,10 @@ exports.ExternalModule = ExternalModule; const dts = makeTestDtsBundleProgram(_('/ep/typings/func1.d.ts'), _('/')); const mooFn = getDeclaration( bundle.program, _('/ep/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); }); it('should return null if there is no matching class in the matching dts file', () => { @@ -2435,7 +2587,8 @@ exports.ExternalModule = ExternalModule; const dts = makeTestDtsBundleProgram(_('/ep/typings/index.d.ts'), _('/')); const missingClass = getDeclaration( bundle.program, _('/ep/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBe(null); }); @@ -2448,7 +2601,8 @@ exports.ExternalModule = ExternalModule; const missingClass = getDeclaration( bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', ts.isVariableDeclaration); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBe(null); }); @@ -2461,11 +2615,11 @@ exports.ExternalModule = ExternalModule; const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const host = createHost( + bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find aliased exports', () => { @@ -2475,7 +2629,8 @@ exports.ExternalModule = ExternalModule; const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const sourceClass = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'SourceClass', ts.isVariableDeclaration); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(sourceClass); if (dtsDeclaration === null) { @@ -2498,10 +2653,11 @@ exports.ExternalModule = ExternalModule; const internalClass = getDeclaration( bundle.program, _('/ep/src/internal.js'), 'InternalClass', ts.isVariableDeclaration); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const host = createHost( + bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(internalClass); - expect(dtsDeclaration !.getSourceFile().fileName) + expect(dtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/internal.d.ts')); }); @@ -2511,18 +2667,19 @@ exports.ExternalModule = ExternalModule; loadTestFiles(TYPINGS_DTS_FILES); const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); const dts = makeTestDtsBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0], _('/ep')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle, dts); + const host = createHost( + bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle, dts)); const class2 = getDeclaration( bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedVariableDeclaration); const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration !.getSourceFile().fileName) + expect(class2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/class2.d.ts')); const internalClass2 = getDeclaration( bundle.program, _('/ep/src/internal.js'), 'Class2', isNamedVariableDeclaration); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration !.getSourceFile().fileName) + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/internal.d.ts')); }); }); @@ -2531,7 +2688,8 @@ exports.ExternalModule = ExternalModule; it('should return the name of the inner class declaration', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const emptyClass = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); @@ -2555,7 +2713,8 @@ exports.ExternalModule = ExternalModule; it('should return the name of the inner class declaration', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const emptyClass = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); @@ -2580,10 +2739,11 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(_('/src/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/functions.js')); const fns = host.getModuleWithProvidersFunctions(file); - expect(fns.map(fn => [fn.declaration.name !.getText(), fn.ngModule.node.name.text])) + expect(fns.map(fn => [fn.declaration.name!.getText(), fn.ngModule.node.name.text])) .toEqual([ ['ngModuleIdentifier', 'InternalModule'], ['ngModuleWithEmptyProviders', 'InternalModule'], @@ -2596,7 +2756,8 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(_('/src/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/methods.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ @@ -2623,7 +2784,8 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(_('/src/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/outer_aliased_class.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ @@ -2636,7 +2798,8 @@ exports.ExternalModule = ExternalModule; () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(_('/src/index.js')); - const host = new CommonJsReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/inner_aliased_class.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_import_helper_spec.ts index e9baddd052215..554862120e9cf 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_import_helper_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_import_helper_spec.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {ClassMemberKind, Import, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles, loadTsLib} from '../../../test/helpers'; @@ -168,16 +168,16 @@ runInEachFileSystem(() => { const classNode = getDeclaration( bundle.program, _('/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('Directive'); + expect(decorator.identifier!.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -189,16 +189,16 @@ runInEachFileSystem(() => { const classNode = getDeclaration( bundle.program, _('/some_directive_ctor_parameters.js'), 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('Directive'); + expect(decorator.identifier!.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -210,16 +210,16 @@ runInEachFileSystem(() => { const classNode = getDeclaration( bundle.program, _('/node_modules/@angular/core/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('Directive'); + expect(decorator.identifier!.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: './directives'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -234,15 +234,15 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); it('should find decorated members on a class when mixing `ctorParameters` and `__decorate`', @@ -254,10 +254,10 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); it('should find non decorated properties on a class', () => { @@ -268,11 +268,11 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const instanceProperty = members.find(member => member.name === 'instanceProperty') !; + const instanceProperty = members.find(member => member.name === 'instanceProperty')!; expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); expect(instanceProperty.isStatic).toEqual(false); - expect(ts.isBinaryExpression(instanceProperty.implementation !)).toEqual(true); - expect(instanceProperty.value !.getText()).toEqual(`'instance'`); + expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); + expect(instanceProperty.value!.getText()).toEqual(`'instance'`); }); it('should find static methods on a class', () => { @@ -283,10 +283,10 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticMethod = members.find(member => member.name === 'staticMethod') !; + const staticMethod = members.find(member => member.name === 'staticMethod')!; expect(staticMethod.kind).toEqual(ClassMemberKind.Method); expect(staticMethod.isStatic).toEqual(true); - expect(ts.isMethodDeclaration(staticMethod.implementation !)).toEqual(true); + expect(ts.isMethodDeclaration(staticMethod.implementation!)).toEqual(true); }); it('should find static properties on a class', () => { @@ -297,11 +297,11 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticProperty = members.find(member => member.name === 'staticProperty') !; + const staticProperty = members.find(member => member.name === 'staticProperty')!; expect(staticProperty.kind).toEqual(ClassMemberKind.Property); expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation !)).toEqual(true); - expect(staticProperty.value !.getText()).toEqual(`'static'`); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); }); it('should find static properties on a class that has an intermediate variable assignment', @@ -313,11 +313,11 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticProperty = members.find(member => member.name === 'staticProperty') !; + const staticProperty = members.find(member => member.name === 'staticProperty')!; expect(staticProperty.kind).toEqual(ClassMemberKind.Property); expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation !)).toEqual(true); - expect(staticProperty.value !.getText()).toEqual(`'static'`); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); }); it('should support decorators being used inside @angular/core', () => { @@ -329,10 +329,10 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); }); @@ -346,10 +346,10 @@ runInEachFileSystem(() => { const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', 'String', @@ -366,20 +366,20 @@ runInEachFileSystem(() => { const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, ]); - const decorators = parameters ![2].decorators !; + const decorators = parameters![2].decorators!; expect(decorators.length).toEqual(1); expect(decorators[0].name).toBe('Inject'); - expect(decorators[0].import !.from).toBe('@angular/core'); - expect(decorators[0].import !.name).toBe('Inject'); + expect(decorators[0].import!.from).toBe('@angular/core'); + expect(decorators[0].import!.name).toBe('Inject'); }); }); @@ -390,8 +390,8 @@ runInEachFileSystem(() => { const classNode = getDeclaration( bundle.program, _('/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const ctrDecorators = host.getConstructorParameters(classNode) !; - const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{ + const ctrDecorators = host.getConstructorParameters(classNode)!; + const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { local: true, expression: ts.Identifier, defaultImportStatement: null, @@ -401,8 +401,8 @@ runInEachFileSystem(() => { bundle.program, _('/some_directive.js'), 'ViewContainerRef', ts.isClassDeclaration); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe(null); }); it('should return the declaration of an externally defined identifier', () => { @@ -411,8 +411,8 @@ runInEachFileSystem(() => { const classNode = getDeclaration( bundle.program, _('/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode) !; - const decoratorNode = classDecorators[0].node !; + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const decoratorNode = classDecorators[0].node!; const identifierOfDirective = ts.isCallExpression(decoratorNode) && ts.isIdentifier(decoratorNode.expression) ? decoratorNode.expression : @@ -421,10 +421,10 @@ runInEachFileSystem(() => { const expectedDeclarationNode = getDeclaration( bundle.program, _('/node_modules/@angular/core/index.d.ts'), 'Directive', isNamedVariableDeclaration); - const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective !); + const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective!); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe('@angular/core'); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); }); @@ -435,13 +435,13 @@ runInEachFileSystem(() => { const ngModuleRef = findVariableDeclaration( getSourceFileOrError(bundle.program, _('/ngmodule.js')), 'HttpClientXsrfModule_1'); - const value = host.getVariableValue(ngModuleRef !); + const value = host.getVariableValue(ngModuleRef!); expect(value).not.toBe(null); if (!value || !ts.isClassExpression(value)) { throw new Error( `Expected value to be a class expression: ${value && value.getText()}.`); } - expect(value.name !.text).toBe('HttpClientXsrfModule'); + expect(value.name!.text).toBe('HttpClientXsrfModule'); }); it('should return null if the variable has no assignment', () => { @@ -449,7 +449,7 @@ runInEachFileSystem(() => { const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); const missingValue = findVariableDeclaration( getSourceFileOrError(bundle.program, _('/ngmodule.js')), 'missingValue'); - const value = host.getVariableValue(missingValue !); + const value = host.getVariableValue(missingValue!); expect(value).toBe(null); }); @@ -458,7 +458,7 @@ runInEachFileSystem(() => { const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); const nonDecoratedVar = findVariableDeclaration( getSourceFileOrError(bundle.program, _('/ngmodule.js')), 'nonDecoratedVar'); - const value = host.getVariableValue(nonDecoratedVar !); + const value = host.getVariableValue(nonDecoratedVar!); expect(value).toBe(null); }); }); @@ -468,7 +468,7 @@ runInEachFileSystem(() => { const bundle = makeTestBundleProgram(_('/ngmodule.js')); const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); const classSymbol = - host.findClassSymbols(bundle.program.getSourceFile(_('/ngmodule.js')) !)[0]; + host.findClassSymbols(bundle.program.getSourceFile(_('/ngmodule.js'))!)[0]; const endOfClass = host.getEndOfClass(classSymbol); expect(endOfClass.getText()) .toMatch( @@ -479,7 +479,7 @@ runInEachFileSystem(() => { }); function findVariableDeclaration( - node: ts.Node | undefined, variableName: string): ts.VariableDeclaration|undefined { + node: ts.Node|undefined, variableName: string): ts.VariableDeclaration|undefined { if (!node) { return; } diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts index 6f086011db370..f0b5956d1b053 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts @@ -9,11 +9,13 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {ClassMemberKind, CtorParameter, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; +import {ClassMemberKind, CtorParameter, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; +import {DelegatingReflectionHost} from '../../src/host/delegating_host'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; +import {BundleProgram} from '../../src/packages/bundle_program'; import {MockLogger} from '../helpers/mock_logger'; import {getRootFiles, makeTestBundleProgram, makeTestDtsBundleProgram} from '../helpers/utils'; @@ -21,7 +23,6 @@ import {expectTypeValueReferencesForParameters} from './util'; runInEachFileSystem(() => { describe('Esm2015ReflectionHost', () => { - let _: typeof absoluteFrom; let SOME_DIRECTIVE_FILE: TestFile; @@ -48,6 +49,12 @@ runInEachFileSystem(() => { let NAMESPACED_IMPORT_FILE: TestFile; let INDEX_SIGNATURE_PROP_FILE: TestFile; + // Helpers + const createHost = (bundle: BundleProgram, ngccHost: Esm2015ReflectionHost) => { + const tsHost = new TypeScriptReflectionHost(bundle.program.getTypeChecker()); + return new DelegatingReflectionHost(tsHost, ngccHost); + }; + beforeEach(() => { _ = absoluteFrom; @@ -720,10 +727,10 @@ runInEachFileSystem(() => { it('should find the decorators on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); @@ -731,7 +738,7 @@ runInEachFileSystem(() => { const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -739,10 +746,10 @@ runInEachFileSystem(() => { it('should find the decorators on an aliased class', () => { loadTestFiles([CLASS_EXPRESSION_FILE]); const bundle = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, CLASS_EXPRESSION_FILE.name, 'AliasedClass', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); @@ -750,7 +757,7 @@ runInEachFileSystem(() => { const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -758,7 +765,7 @@ runInEachFileSystem(() => { it('should return null if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); const decorators = host.getDecoratorsOfDeclaration(functionNode); @@ -768,7 +775,7 @@ runInEachFileSystem(() => { it('should return null if there are no decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode); @@ -778,7 +785,7 @@ runInEachFileSystem(() => { it('should ignore `decorators` if it is not an array literal', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration); @@ -789,11 +796,11 @@ runInEachFileSystem(() => { it('should ignore decorator elements that are not object literals', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedClassDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -802,11 +809,11 @@ runInEachFileSystem(() => { it('should ignore decorator elements that have no `type` property', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -815,10 +822,10 @@ runInEachFileSystem(() => { it('should ignore decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -827,10 +834,10 @@ runInEachFileSystem(() => { it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); @@ -840,11 +847,12 @@ runInEachFileSystem(() => { it('should be an empty array if decorator has no `args` property', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedClassDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -854,11 +862,12 @@ runInEachFileSystem(() => { it('should be an empty array if decorator\'s `args` has no property assignment', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedClassDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -868,11 +877,12 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -885,43 +895,43 @@ runInEachFileSystem(() => { it('should find decorated properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); - expect(input1.decorators ![0].import).toEqual({name: 'Input', from: '@angular/core'}); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators![0].import).toEqual({name: 'Input', from: '@angular/core'}); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input2.decorators !.map(d => d.name)).toEqual(['Input']); - expect(input2.decorators ![0].import).toEqual({name: 'Input', from: '@angular/core'}); + expect(input2.decorators!.map(d => d.name)).toEqual(['Input']); + expect(input2.decorators![0].import).toEqual({name: 'Input', from: '@angular/core'}); }); it('should find non decorated properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const instanceProperty = members.find(member => member.name === 'instanceProperty') !; + const instanceProperty = members.find(member => member.name === 'instanceProperty')!; expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); expect(instanceProperty.isStatic).toEqual(false); - expect(ts.isBinaryExpression(instanceProperty.implementation !)).toEqual(true); - expect(instanceProperty.value !.getText()).toEqual(`'instance'`); + expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); + expect(instanceProperty.value!.getText()).toEqual(`'instance'`); }); it('should handle equally named getter/setter pairs correctly', () => { loadTestFiles([ACCESSORS_FILE]); const bundle = makeTestBundleProgram(ACCESSORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, ACCESSORS_FILE.name, 'SomeDirective', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); @@ -930,50 +940,50 @@ runInEachFileSystem(() => { members.filter(member => member.name === 'setterAndGetter'); expect(combinedSetter.kind).toEqual(ClassMemberKind.Setter); expect(combinedSetter.isStatic).toEqual(false); - expect(ts.isSetAccessor(combinedSetter.implementation !)).toEqual(true); + expect(ts.isSetAccessor(combinedSetter.implementation!)).toEqual(true); expect(combinedSetter.value).toBeNull(); - expect(combinedSetter.decorators !.map(d => d.name)).toEqual(['Input']); + expect(combinedSetter.decorators!.map(d => d.name)).toEqual(['Input']); expect(combinedGetter.kind).toEqual(ClassMemberKind.Getter); expect(combinedGetter.isStatic).toEqual(false); - expect(ts.isGetAccessor(combinedGetter.implementation !)).toEqual(true); + expect(ts.isGetAccessor(combinedGetter.implementation!)).toEqual(true); expect(combinedGetter.value).toBeNull(); - expect(combinedGetter.decorators !.map(d => d.name)).toEqual([]); + expect(combinedGetter.decorators!.map(d => d.name)).toEqual([]); }); it('should find static methods on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const staticMethod = members.find(member => member.name === 'staticMethod') !; + const staticMethod = members.find(member => member.name === 'staticMethod')!; expect(staticMethod.kind).toEqual(ClassMemberKind.Method); expect(staticMethod.isStatic).toEqual(true); - expect(ts.isMethodDeclaration(staticMethod.implementation !)).toEqual(true); + expect(ts.isMethodDeclaration(staticMethod.implementation!)).toEqual(true); }); it('should find static properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const staticProperty = members.find(member => member.name === 'staticProperty') !; + const staticProperty = members.find(member => member.name === 'staticProperty')!; expect(staticProperty.kind).toEqual(ClassMemberKind.Property); expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation !)).toEqual(true); - expect(staticProperty.value !.getText()).toEqual(`'static'`); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); }); it('should ignore index signature properties', () => { loadTestFiles([INDEX_SIGNATURE_PROP_FILE]); const logger = new MockLogger(); const bundle = makeTestBundleProgram(INDEX_SIGNATURE_PROP_FILE.name); - const host = new Esm2015ReflectionHost(logger, false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(logger, false, bundle)); const classNode = getDeclaration( bundle.program, INDEX_SIGNATURE_PROP_FILE.name, 'IndexSignatureClass', isNamedClassDeclaration); @@ -986,7 +996,7 @@ runInEachFileSystem(() => { it('should throw if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); expect(() => { @@ -997,7 +1007,7 @@ runInEachFileSystem(() => { it('should return an empty array if there are no prop decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); @@ -1009,7 +1019,8 @@ runInEachFileSystem(() => { () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedClassDeclaration); @@ -1021,13 +1032,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements that are not object literals', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); @@ -1036,13 +1047,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements that have no `type` property', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); @@ -1051,13 +1062,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); @@ -1067,13 +1078,14 @@ runInEachFileSystem(() => { it('should be an empty array if prop decorator has no `args` property', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1084,13 +1096,14 @@ runInEachFileSystem(() => { () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1100,13 +1113,14 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1120,10 +1134,10 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters).toBeDefined(); expect(parameters.map(parameter => parameter.name)).toEqual([ @@ -1136,11 +1150,11 @@ runInEachFileSystem(() => { it('should accept `ctorParameters` as an array', () => { loadTestFiles([CTOR_DECORATORS_ARRAY_FILE]); const bundle = makeTestBundleProgram(CTOR_DECORATORS_ARRAY_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, CTOR_DECORATORS_ARRAY_FILE.name, 'CtorDecoratedAsArray', isNamedClassDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters).toBeDefined(); expect(parameters.map(parameter => parameter.name)).toEqual(['arg1']); @@ -1150,10 +1164,12 @@ runInEachFileSystem(() => { it('should throw if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(() => { host.getConstructorParameters(functionNode); }) + expect(() => { + host.getConstructorParameters(functionNode); + }) .toThrowError( 'Attempted to get constructor parameters of a non-class: "function foo() {}"'); }); @@ -1161,7 +1177,7 @@ runInEachFileSystem(() => { it('should return `null` if there is no constructor', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); const parameters = host.getConstructorParameters(classNode); @@ -1171,11 +1187,11 @@ runInEachFileSystem(() => { it('should return an array even if there are no decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', isNamedClassDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters).toEqual(jasmine.any(Array)); expect(parameters.length).toEqual(1); @@ -1186,7 +1202,7 @@ runInEachFileSystem(() => { it('should return an empty array if there are no constructor parameters', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', isNamedClassDeclaration); @@ -1198,11 +1214,11 @@ runInEachFileSystem(() => { it('should ignore decorators that are not imported from core', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotFromCore', isNamedClassDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters.length).toBe(1); expect(parameters[0]).toEqual(jasmine.objectContaining<CtorParameter>({ @@ -1214,11 +1230,11 @@ runInEachFileSystem(() => { it('should ignore `ctorParameters` if it is not an arrow function', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrowFunction', isNamedClassDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters.length).toBe(1); expect(parameters[0]).toEqual(jasmine.objectContaining<CtorParameter>({ @@ -1230,11 +1246,11 @@ runInEachFileSystem(() => { it('should ignore `ctorParameters` if it does not return an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters.length).toBe(1); expect(parameters[0]).toEqual(jasmine.objectContaining<CtorParameter>({ @@ -1257,7 +1273,8 @@ runInEachFileSystem(() => { loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedClassDeclaration); return host.getConstructorParameters(classNode); @@ -1279,7 +1296,7 @@ runInEachFileSystem(() => { super(arguments); }`); - expect(parameters !.length).toBe(0); + expect(parameters!.length).toBe(0); }); it('does not consider constructors with parameters as synthesized', () => { @@ -1288,7 +1305,7 @@ runInEachFileSystem(() => { super(...arguments); }`); - expect(parameters !.length).toBe(1); + expect(parameters!.length).toBe(1); }); it('does not consider manual super calls as synthesized', () => { @@ -1297,7 +1314,7 @@ runInEachFileSystem(() => { super(); }`); - expect(parameters !.length).toBe(0); + expect(parameters!.length).toBe(0); }); it('does not consider empty constructors as synthesized', () => { @@ -1305,7 +1322,7 @@ runInEachFileSystem(() => { constructor() { }`); - expect(parameters !.length).toBe(0); + expect(parameters!.length).toBe(0); }); }); @@ -1313,18 +1330,19 @@ runInEachFileSystem(() => { it('should ignore param decorator elements that are not object literals', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedClassDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(2); - expect(parameters ![0]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters!.length).toBe(2); + expect(parameters![0]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg1', decorators: null, })); - expect(parameters ![1]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters![1]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg2', decorators: jasmine.any(Array) as any })); @@ -1333,12 +1351,13 @@ runInEachFileSystem(() => { it('should ignore param decorator elements that have no `type` property', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', isNamedClassDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); @@ -1347,12 +1366,13 @@ runInEachFileSystem(() => { it('should ignore param decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', isNamedClassDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); @@ -1361,11 +1381,12 @@ runInEachFileSystem(() => { it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); - const parameters = host.getConstructorParameters(classNode) !; - const decorators = parameters[2].decorators !; + const parameters = host.getConstructorParameters(classNode)!; + const decorators = parameters[2].decorators!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toEqual({name: 'Inject', from: '@angular/core'}); @@ -1376,13 +1397,14 @@ runInEachFileSystem(() => { it('should be an empty array if param decorator has no `args` property', () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedClassDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(1); - const decorators = parameters ![0].decorators !; + expect(parameters!.length).toBe(1); + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1393,12 +1415,13 @@ runInEachFileSystem(() => { () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedClassDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1408,12 +1431,13 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedClassDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1427,61 +1451,62 @@ runInEachFileSystem(() => { () => { loadTestFiles([FUNCTION_BODY_FILE]); const bundle = makeTestBundleProgram(FUNCTION_BODY_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const fooNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration) !; - const fooDef = host.getDefinitionOfFunction(fooNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration)!; + const fooDef = host.getDefinitionOfFunction(fooNode)!; expect(fooDef.node).toBe(fooNode); - expect(fooDef.body !.length).toEqual(1); - expect(fooDef.body ![0].getText()).toEqual(`return x;`); + expect(fooDef.body!.length).toEqual(1); + expect(fooDef.body![0].getText()).toEqual(`return x;`); expect(fooDef.parameters.length).toEqual(1); expect(fooDef.parameters[0].name).toEqual('x'); expect(fooDef.parameters[0].initializer).toBe(null); const barNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration) !; - const barDef = host.getDefinitionOfFunction(barNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration)!; + const barDef = host.getDefinitionOfFunction(barNode)!; expect(barDef.node).toBe(barNode); - expect(barDef.body !.length).toEqual(1); - expect(ts.isReturnStatement(barDef.body ![0])).toBeTruthy(); - expect(barDef.body ![0].getText()).toEqual(`return x + y;`); + expect(barDef.body!.length).toEqual(1); + expect(ts.isReturnStatement(barDef.body![0])).toBeTruthy(); + expect(barDef.body![0].getText()).toEqual(`return x + y;`); expect(barDef.parameters.length).toEqual(2); expect(barDef.parameters[0].name).toEqual('x'); expect(fooDef.parameters[0].initializer).toBe(null); expect(barDef.parameters[1].name).toEqual('y'); - expect(barDef.parameters[1].initializer !.getText()).toEqual('42'); + expect(barDef.parameters[1].initializer!.getText()).toEqual('42'); const bazNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration) !; - const bazDef = host.getDefinitionOfFunction(bazNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration)!; + const bazDef = host.getDefinitionOfFunction(bazNode)!; expect(bazDef.node).toBe(bazNode); - expect(bazDef.body !.length).toEqual(3); + expect(bazDef.body!.length).toEqual(3); expect(bazDef.parameters.length).toEqual(1); expect(bazDef.parameters[0].name).toEqual('x'); expect(bazDef.parameters[0].initializer).toBe(null); const quxNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration) !; - const quxDef = host.getDefinitionOfFunction(quxNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration)!; + const quxDef = host.getDefinitionOfFunction(quxNode)!; expect(quxDef.node).toBe(quxNode); - expect(quxDef.body !.length).toEqual(2); + expect(quxDef.body!.length).toEqual(2); expect(quxDef.parameters.length).toEqual(1); expect(quxDef.parameters[0].name).toEqual('x'); expect(quxDef.parameters[0].initializer).toBe(null); const mooNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'moo', isNamedFunctionDeclaration) !; - const mooDef = host.getDefinitionOfFunction(mooNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'moo', isNamedFunctionDeclaration)!; + const mooDef = host.getDefinitionOfFunction(mooNode)!; expect(mooDef.node).toBe(mooNode); - expect(mooDef.body !.length).toEqual(3); + expect(mooDef.body!.length).toEqual(3); expect(mooDef.parameters).toEqual([]); const juuNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'juu', isNamedFunctionDeclaration) !; - const juuDef = host.getDefinitionOfFunction(juuNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'juu', isNamedFunctionDeclaration)!; + const juuDef = host.getDefinitionOfFunction(juuNode)!; expect(juuDef.node).toBe(juuNode); - expect(juuDef.body !.length).toEqual(2); + expect(juuDef.body!.length).toEqual(2); expect(juuDef.parameters).toEqual([]); }); }); @@ -1490,7 +1515,7 @@ runInEachFileSystem(() => { it('should find the import of an identifier', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/b.js'), 'b', isNamedVariableDeclaration); const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); @@ -1501,7 +1526,7 @@ runInEachFileSystem(() => { it('should find the name by which the identifier was exported, not imported', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/b.js'), 'c', isNamedVariableDeclaration); const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); @@ -1512,7 +1537,7 @@ runInEachFileSystem(() => { it('should return null if the identifier was not imported', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/b.js'), 'd', isNamedVariableDeclaration); const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); @@ -1526,61 +1551,63 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); const actualDeclaration = host.getDeclarationOfIdentifier(classNode.name); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(classNode); - expect(actualDeclaration !.viaModule).toBe(null); + expect(actualDeclaration!.node).toBe(classNode); + expect(actualDeclaration!.viaModule).toBe(null); }); it('should return the declaration of an externally defined identifier', () => { loadFakeCore(getFileSystem()); loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedClassDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode) !; - const identifierOfDirective = ((classDecorators[0].node as ts.ObjectLiteralExpression) - .properties[0] as ts.PropertyAssignment) - .initializer as ts.Identifier; + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const identifierOfDirective = + ((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as + ts.PropertyAssignment) + .initializer as ts.Identifier; const expectedDeclarationNode = getDeclaration( bundle.program, _('/node_modules/@angular/core/index.d.ts'), 'Directive', isNamedVariableDeclaration); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe('@angular/core'); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); it('should return the source-file of an import namespace', () => { loadFakeCore(getFileSystem()); loadTestFiles([NAMESPACED_IMPORT_FILE]); const bundle = makeTestBundleProgram(NAMESPACED_IMPORT_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, NAMESPACED_IMPORT_FILE.name, 'SomeDirective', ts.isClassDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode) !; - const identifier = (((classDecorators[0].node as ts.ObjectLiteralExpression) - .properties[0] as ts.PropertyAssignment) - .initializer as ts.PropertyAccessExpression) - .expression as ts.Identifier; + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const identifier = + (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as + ts.PropertyAssignment) + .initializer as ts.PropertyAccessExpression) + .expression as ts.Identifier; const expectedDeclarationNode = getSourceFileOrError(bundle.program, _('/node_modules/@angular/core/index.d.ts')); const actualDeclaration = host.getDeclarationOfIdentifier(identifier); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe('@angular/core'); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); it('should return the original declaration of an aliased class', () => { loadTestFiles([CLASS_EXPRESSION_FILE]); const bundle = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classDeclaration = getDeclaration( bundle.program, CLASS_EXPRESSION_FILE.name, 'AliasedClass', ts.isVariableDeclaration); const usageOfAliasedClass = getDeclaration( @@ -1588,7 +1615,7 @@ runInEachFileSystem(() => { ts.isVariableDeclaration); const aliasedClassIdentifier = usageOfAliasedClass.initializer as ts.Identifier; expect(aliasedClassIdentifier.text).toBe('AliasedClass_1'); - expect(host.getDeclarationOfIdentifier(aliasedClassIdentifier) !.node) + expect(host.getDeclarationOfIdentifier(aliasedClassIdentifier)!.node) .toBe(classDeclaration); }); }); @@ -1598,11 +1625,11 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/b.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.keys())).toEqual([ + expect(Array.from(exportDeclarations!.keys())).toEqual([ 'Directive', 'a', 'b', @@ -1614,8 +1641,8 @@ runInEachFileSystem(() => { ]); const values = - Array.from(exportDeclarations !.values()) - .map(declaration => [declaration.node !.getText(), declaration.viaModule]); + Array.from(exportDeclarations!.values()) + .map(declaration => [declaration.node!.getText(), declaration.viaModule]); expect(values).toEqual([ [`Directive: FnWithArg<(clazz: any) => any>`, null], [`a = 'a'`, null], @@ -1633,21 +1660,22 @@ runInEachFileSystem(() => { it('should return the class symbol for an ES2015 class', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); const classSymbol = host.getClassSymbol(node); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(node); - expect(classSymbol !.implementation.valueDeclaration).toBe(node); + expect(classSymbol!.declaration.valueDeclaration).toBe(node); + expect(classSymbol!.implementation.valueDeclaration).toBe(node); }); it('should return the class symbol for a class expression (outer variable declaration)', () => { loadTestFiles([CLASS_EXPRESSION_FILE]); const bundle = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, CLASS_EXPRESSION_FILE.name, 'EmptyClass', isNamedVariableDeclaration); @@ -1655,36 +1683,37 @@ runInEachFileSystem(() => { const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the class symbol for a class expression (inner class expression)', () => { loadTestFiles([CLASS_EXPRESSION_FILE]); const bundle = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, CLASS_EXPRESSION_FILE.name, 'EmptyClass', isNamedVariableDeclaration); const innerNode = (outerNode.initializer as ts.ClassExpression); const classSymbol = host.getClassSymbol(innerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the same class symbol (of the outer declaration) for outer and inner declarations', () => { loadTestFiles([CLASS_EXPRESSION_FILE]); const bundle = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, CLASS_EXPRESSION_FILE.name, 'EmptyClass', isNamedVariableDeclaration); const innerNode = (outerNode.initializer as ts.ClassExpression); - const innerSymbol = host.getClassSymbol(innerNode) !; - const outerSymbol = host.getClassSymbol(outerNode) !; + const innerSymbol = host.getClassSymbol(innerNode)!; + const outerSymbol = host.getClassSymbol(outerNode)!; expect(innerSymbol.declaration).toBe(outerSymbol.declaration); expect(innerSymbol.implementation).toBe(outerSymbol.implementation); }); @@ -1692,7 +1721,7 @@ runInEachFileSystem(() => { it('should return undefined if node is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); const classSymbol = host.getClassSymbol(node); @@ -1708,7 +1737,8 @@ runInEachFileSystem(() => { }; loadTestFiles([testFile]); const bundle = makeTestBundleProgram(testFile.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration(bundle.program, testFile.name, 'MyClass', isNamedVariableDeclaration); const classSymbol = host.getClassSymbol(node); @@ -1721,7 +1751,7 @@ runInEachFileSystem(() => { it('should return true if a given node is a TS class declaration', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); expect(host.isClass(node)).toBe(true); @@ -1731,7 +1761,8 @@ runInEachFileSystem(() => { () => { loadTestFiles([CLASS_EXPRESSION_FILE]); const bundle = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, CLASS_EXPRESSION_FILE.name, 'EmptyClass', ts.isVariableDeclaration); expect(host.isClass(node)).toBe(true); @@ -1741,7 +1772,8 @@ runInEachFileSystem(() => { () => { loadTestFiles([CLASS_EXPRESSION_FILE]); const bundle = makeTestBundleProgram(CLASS_EXPRESSION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, CLASS_EXPRESSION_FILE.name, 'AliasedClass', ts.isVariableDeclaration); @@ -1751,7 +1783,7 @@ runInEachFileSystem(() => { it('should return false if a given node is a TS function declaration', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); expect(host.isClass(node)).toBe(false); @@ -1766,7 +1798,7 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedClassDeclaration); expect(host.hasBaseClass(classNode)).toBe(false); @@ -1781,7 +1813,7 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedClassDeclaration); expect(host.hasBaseClass(classNode)).toBe(true); @@ -1797,7 +1829,7 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); expect(host.hasBaseClass(classNode)).toBe(true); @@ -1812,7 +1844,7 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedClassDeclaration); expect(host.getBaseClassExpression(classNode)).toBe(null); @@ -1827,10 +1859,10 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedClassDeclaration); - const baseIdentifier = host.getBaseClassExpression(classNode) !; + const baseIdentifier = host.getBaseClassExpression(classNode)!; if (!ts.isIdentifier(baseIdentifier)) { throw new Error(`Expected ${baseIdentifier.getText()} to be an identifier.`); } @@ -1847,10 +1879,10 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - const baseIdentifier = host.getBaseClassExpression(classNode) !; + const baseIdentifier = host.getBaseClassExpression(classNode)!; if (!ts.isIdentifier(baseIdentifier)) { throw new Error(`Expected ${baseIdentifier.getText()} to be an identifier.`); } @@ -1868,10 +1900,11 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedClassDeclaration); - const baseExpression = host.getBaseClassExpression(classNode) !; + const baseExpression = host.getBaseClassExpression(classNode)!; expect(baseExpression.getText()).toEqual('foo()'); }); }); @@ -1881,7 +1914,8 @@ runInEachFileSystem(() => { loadTestFiles(ARITY_CLASSES); const bundle = makeTestBundleProgram(ARITY_CLASSES[0].name); const dts = makeTestBundleProgram(ARITY_CLASSES[1].name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); const noTypeParamClass = getDeclaration( bundle.program, _('/src/class.js'), 'NoTypeParam', isNamedClassDeclaration); expect(host.getGenericArityOfClass(noTypeParamClass)).toBe(0); @@ -1899,10 +1933,11 @@ runInEachFileSystem(() => { () => { loadTestFiles([MARKER_FILE]); const bundle = makeTestBundleProgram(MARKER_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, MARKER_FILE.name); const declarations = host.getSwitchableDeclarations(file); - expect(declarations.map(d => [d.name.getText(), d.initializer !.getText()])).toEqual([ + expect(declarations.map(d => [d.name.getText(), d.initializer!.getText()])).toEqual([ ['compileNgModuleFactory', 'compileNgModuleFactory__PRE_R3__'] ]); }); @@ -1912,7 +1947,7 @@ runInEachFileSystem(() => { it('should return an array of all classes in the given source file', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(getRootFiles(DECORATED_FILES)[0]); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); @@ -1930,31 +1965,31 @@ runInEachFileSystem(() => { it('should return decorators of class symbol', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(getRootFiles(DECORATED_FILES)[0]); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); const classSymbolsPrimary = host.findClassSymbols(primaryFile); const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s)); expect(classDecoratorsPrimary.length).toEqual(3); - expect(classDecoratorsPrimary[0] !.map(d => d.name)).toEqual(['Directive']); - expect(classDecoratorsPrimary[1] !.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[0]!.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[1]!.map(d => d.name)).toEqual(['Directive']); expect(classDecoratorsPrimary[2]).toBe(null); const classSymbolsSecondary = host.findClassSymbols(secondaryFile); const classDecoratorsSecondary = classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s)); expect(classDecoratorsSecondary.length).toEqual(1); - expect(classDecoratorsSecondary[0] !.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsSecondary[0]!.map(d => d.name)).toEqual(['Directive']); }); it('should return a cloned array on each invocation', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(getRootFiles(DECORATED_FILES)[0]); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const classDecl = - getDeclaration(bundle.program, DECORATED_FILES[0].name, 'A', ts.isClassDeclaration) !; - const classSymbol = host.getClassSymbol(classDecl) !; + getDeclaration(bundle.program, DECORATED_FILES[0].name, 'A', ts.isClassDeclaration)!; + const classSymbol = host.getClassSymbol(classDecl)!; const firstResult = host.getDecoratorsOfSymbol(classSymbol); const secondResult = host.getDecoratorsOfSymbol(classSymbol); @@ -1972,10 +2007,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/class1.js'), 'Class1', isNamedClassDeclaration); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find the dts declaration for exported functions', () => { @@ -1985,10 +2021,11 @@ runInEachFileSystem(() => { const dts = makeTestDtsBundleProgram(_('/ep/typings/func1.d.ts'), _('/')); const mooFn = getDeclaration( bundle.program, _('/ep/src/func1.js'), 'mooFn', isNamedFunctionDeclaration); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); }); it('should return null if there is no matching class in the matching dts file', () => { @@ -1999,7 +2036,8 @@ runInEachFileSystem(() => { const missingClass = getDeclaration( bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', isNamedClassDeclaration); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBe(null); }); @@ -2012,7 +2050,8 @@ runInEachFileSystem(() => { const missingClass = getDeclaration( bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', isNamedClassDeclaration); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBe(null); }); @@ -2026,9 +2065,10 @@ runInEachFileSystem(() => { getRootFiles(TYPINGS_DTS_FILES)[0], false, [_('/ep/typings/shadow-class.d.ts')]); const shadowClass = getDeclaration( bundle.program, _('/ep/src/shadow-class.js'), 'ShadowClass', isNamedClassDeclaration); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); - const dtsDecl = host.getDtsDeclaration(shadowClass) !; + const dtsDecl = host.getDtsDeclaration(shadowClass)!; expect(dtsDecl).not.toBeNull(); expect(dtsDecl.getSourceFile().fileName).toEqual(_('/ep/typings/shadow-class.d.ts')); }); @@ -2054,7 +2094,8 @@ runInEachFileSystem(() => { const missingClass = getDeclaration( bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', isNamedClassDeclaration); - const host = new TestEsm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new TestEsm2015ReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBeNull(); }); @@ -2067,10 +2108,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'Class1', isNamedClassDeclaration); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find aliased exports', () => { @@ -2080,7 +2122,8 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const sourceClass = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'SourceClass', isNamedClassDeclaration); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(sourceClass); if (dtsDeclaration === null) { @@ -2102,11 +2145,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const internalClass = getDeclaration( bundle.program, _('/ep/src/internal.js'), 'InternalClass', isNamedClassDeclaration); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(internalClass); - expect(dtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/ep/typings/internal.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/internal.d.ts')); }); it('should match publicly and internal exported classes correctly, even if they have the same name', @@ -2115,18 +2158,19 @@ runInEachFileSystem(() => { loadTestFiles(TYPINGS_DTS_FILES); const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle, dts)); const class2 = getDeclaration( bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedClassDeclaration); const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration !.getSourceFile().fileName) + expect(class2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/class2.d.ts')); const internalClass2 = getDeclaration( bundle.program, _('/ep/src/internal.js'), 'Class2', isNamedClassDeclaration); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration !.getSourceFile().fileName) + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/internal.d.ts')); }); }); @@ -2135,7 +2179,7 @@ runInEachFileSystem(() => { it('should return the name of the class (there is no separate inner class in ES2015)', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); expect(host.getInternalNameOfClass(node).text).toEqual('EmptyClass'); @@ -2146,7 +2190,7 @@ runInEachFileSystem(() => { it('should return the name of the class (there is no separate inner class in ES2015)', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); expect(host.getAdjacentNameOfClass(node).text).toEqual('EmptyClass'); @@ -2158,10 +2202,11 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(getRootFiles(MODULE_WITH_PROVIDERS_PROGRAM)[0]); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/functions.js')); const fns = host.getModuleWithProvidersFunctions(file); - expect(fns.map(fn => [fn.declaration.name !.getText(), fn.ngModule.node.name.text])) + expect(fns.map(fn => [fn.declaration.name!.getText(), fn.ngModule.node.name.text])) .toEqual([ ['ngModuleIdentifier', 'InternalModule'], ['ngModuleWithEmptyProviders', 'InternalModule'], @@ -2175,10 +2220,11 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(getRootFiles(MODULE_WITH_PROVIDERS_PROGRAM)[0]); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/methods.js')); const fn = host.getModuleWithProvidersFunctions(file); - expect(fn.map(fn => [fn.declaration.name !.getText(), fn.ngModule.node.name.text])) + expect(fn.map(fn => [fn.declaration.name!.getText(), fn.ngModule.node.name.text])) .toEqual([ ['ngModuleIdentifier', 'InternalModule'], ['ngModuleWithEmptyProviders', 'InternalModule'], @@ -2192,13 +2238,12 @@ runInEachFileSystem(() => { it('should resolve aliased module references to their original declaration', () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(getRootFiles(MODULE_WITH_PROVIDERS_PROGRAM)[0]); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/aliased_class.js')); const fn = host.getModuleWithProvidersFunctions(file); - expect(fn.map(fn => [fn.declaration.name !.getText(), fn.ngModule.node.name.text])) - .toEqual([ - ['forRoot', 'AliasedModule'], - ]); + expect(fn.map(fn => [fn.declaration.name!.getText(), fn.ngModule.node.name.text])).toEqual([ + ['forRoot', 'AliasedModule'], + ]); }); }); @@ -2224,8 +2269,8 @@ runInEachFileSystem(() => { }; loadTestFiles([testFile]); const bundle = makeTestBundleProgram(testFile.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); - const classSymbol = host.findClassSymbols(bundle.program.getSourceFile(testFile.name) !)[0]; + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); + const classSymbol = host.findClassSymbols(bundle.program.getSourceFile(testFile.name)!)[0]; const endOfClass = host.getEndOfClass(classSymbol); expect(endOfClass.getText()) .toEqual( @@ -2247,8 +2292,8 @@ runInEachFileSystem(() => { }; loadTestFiles([testFile]); const bundle = makeTestBundleProgram(testFile.name); - const host = new Esm2015ReflectionHost(new MockLogger(), false, bundle); - const classSymbol = host.findClassSymbols(bundle.program.getSourceFile(testFile.name) !)[0]; + const host = createHost(bundle, new Esm2015ReflectionHost(new MockLogger(), false, bundle)); + const classSymbol = host.findClassSymbols(bundle.program.getSourceFile(testFile.name)!)[0]; const endOfClass = host.getEndOfClass(classSymbol); expect(endOfClass.getText()) .toEqual( diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts index 16c6deb129b54..6ad45d394aaf8 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_import_helper_spec.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {ClassMemberKind, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles, loadTsLib} from '../../../test/helpers'; @@ -227,19 +227,18 @@ export { AliasedDirective$1 }; const classNode = getDeclaration( bundle.program, _('/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('Directive'); + expect(decorator.identifier!.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); - }); it('should find the decorators on a minified class', () => { @@ -248,19 +247,18 @@ export { AliasedDirective$1 }; const classNode = getDeclaration( bundle.program, _('/some_minified_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('Directive'); + expect(decorator.identifier!.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); - }); it('should find the decorators on an aliased class', () => { @@ -269,16 +267,16 @@ export { AliasedDirective$1 }; const classNode = getDeclaration( bundle.program, _('/some_aliased_directive.js'), 'AliasedDirective$1', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('Directive'); + expect(decorator.identifier!.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -290,16 +288,16 @@ export { AliasedDirective$1 }; const classNode = getDeclaration( bundle.program, _('/some_directive_ctor_parameters.js'), 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('Directive'); + expect(decorator.identifier!.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -311,16 +309,16 @@ export { AliasedDirective$1 }; const classNode = getDeclaration( bundle.program, _('/node_modules/@angular/core/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('Directive'); + expect(decorator.identifier!.getText()).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: './directives'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -333,13 +331,12 @@ export { AliasedDirective$1 }; const classNode = getDeclaration( bundle.program, _('/some_minified_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const innerNode = - getIifeBody(classNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(classNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(classNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(classNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(classNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); }); @@ -352,15 +349,15 @@ export { AliasedDirective$1 }; isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); it('should find decorated members on a class when mixing `ctorParameters` and `__decorate`', @@ -372,10 +369,10 @@ export { AliasedDirective$1 }; isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); it('should find non decorated properties on a class', () => { @@ -386,11 +383,11 @@ export { AliasedDirective$1 }; isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const instanceProperty = members.find(member => member.name === 'instanceProperty') !; + const instanceProperty = members.find(member => member.name === 'instanceProperty')!; expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); expect(instanceProperty.isStatic).toEqual(false); - expect(ts.isBinaryExpression(instanceProperty.implementation !)).toEqual(true); - expect(instanceProperty.value !.getText()).toEqual(`'instance'`); + expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); + expect(instanceProperty.value!.getText()).toEqual(`'instance'`); }); it('should find static methods on a class', () => { @@ -401,10 +398,10 @@ export { AliasedDirective$1 }; isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticMethod = members.find(member => member.name === 'staticMethod') !; + const staticMethod = members.find(member => member.name === 'staticMethod')!; expect(staticMethod.kind).toEqual(ClassMemberKind.Method); expect(staticMethod.isStatic).toEqual(true); - expect(ts.isFunctionExpression(staticMethod.implementation !)).toEqual(true); + expect(ts.isFunctionExpression(staticMethod.implementation!)).toEqual(true); }); it('should find static properties on a class', () => { @@ -415,11 +412,11 @@ export { AliasedDirective$1 }; isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticProperty = members.find(member => member.name === 'staticProperty') !; + const staticProperty = members.find(member => member.name === 'staticProperty')!; expect(staticProperty.kind).toEqual(ClassMemberKind.Property); expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation !)).toEqual(true); - expect(staticProperty.value !.getText()).toEqual(`'static'`); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); }); it('should support decorators being used inside @angular/core', () => { @@ -431,10 +428,10 @@ export { AliasedDirective$1 }; isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); }); describe('getConstructorParameters', () => { @@ -447,10 +444,10 @@ export { AliasedDirective$1 }; const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', 'String', @@ -467,10 +464,10 @@ export { AliasedDirective$1 }; const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, @@ -485,7 +482,7 @@ export { AliasedDirective$1 }; bundle.program, _('/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![2].decorators !; + const decorators = parameters![2].decorators!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toEqual({name: 'Inject', from: '@angular/core'}); @@ -522,7 +519,7 @@ export { AliasedDirective$1 }; const ngModuleDecorators = ngModuleClasses.map(s => host.getDecoratorsOfSymbol(s)); expect(ngModuleClasses.length).toEqual(1); - expect(ngModuleDecorators[0] !.map(d => d.name)).toEqual(['NgModule']); + expect(ngModuleDecorators[0]!.map(d => d.name)).toEqual(['NgModule']); const someDirectiveFile = getSourceFileOrError(bundle.program, _('/some_directive.js')); const someDirectiveClasses = host.findClassSymbols(someDirectiveFile); @@ -532,7 +529,7 @@ export { AliasedDirective$1 }; expect(someDirectiveDecorators.length).toEqual(3); expect(someDirectiveDecorators[0]).toBe(null); expect(someDirectiveDecorators[1]).toBe(null); - expect(someDirectiveDecorators[2] !.map(d => d.name)).toEqual(['Directive']); + expect(someDirectiveDecorators[2]!.map(d => d.name)).toEqual(['Directive']); }); }); @@ -543,8 +540,8 @@ export { AliasedDirective$1 }; const classNode = getDeclaration( bundle.program, _('/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const ctrDecorators = host.getConstructorParameters(classNode) !; - const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{ + const ctrDecorators = host.getConstructorParameters(classNode)!; + const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { local: true, expression: ts.Identifier, defaultImportStatement: null, @@ -555,8 +552,8 @@ export { AliasedDirective$1 }; isNamedVariableDeclaration); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe(null); }); it('should return the declaration of an externally defined identifier', () => { @@ -565,8 +562,8 @@ export { AliasedDirective$1 }; const classNode = getDeclaration( bundle.program, _('/some_directive.js'), 'SomeDirective', isNamedVariableDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode) !; - const decoratorNode = classDecorators[0].node !; + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const decoratorNode = classDecorators[0].node!; const identifierOfDirective = ts.isCallExpression(decoratorNode) && ts.isIdentifier(decoratorNode.expression) ? @@ -576,10 +573,10 @@ export { AliasedDirective$1 }; const expectedDeclarationNode = getDeclaration( bundle.program, _('/node_modules/@angular/core/index.d.ts'), 'Directive', isNamedVariableDeclaration); - const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective !); + const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective!); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe('@angular/core'); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); it('should find the "actual" declaration of an aliased variable identifier', () => { @@ -589,9 +586,9 @@ export { AliasedDirective$1 }; getSourceFileOrError(bundle.program, _('/ngmodule.js')), 'HttpClientXsrfModule_1', isNgModulePropertyAssignment); - const declaration = host.getDeclarationOfIdentifier(ngModuleRef !); + const declaration = host.getDeclarationOfIdentifier(ngModuleRef!); expect(declaration).not.toBe(null); - expect(declaration !.node !.getText()).toContain('function HttpClientXsrfModule()'); + expect(declaration!.node!.getText()).toContain('function HttpClientXsrfModule()'); }); }); describe('getVariableValue', () => { @@ -601,7 +598,7 @@ export { AliasedDirective$1 }; const ngModuleRef = findVariableDeclaration( getSourceFileOrError(bundle.program, _('/ngmodule.js')), 'HttpClientXsrfModule_1'); - const value = host.getVariableValue(ngModuleRef !); + const value = host.getVariableValue(ngModuleRef!); expect(value).not.toBe(null); if (!value || !ts.isFunctionDeclaration(value.parent)) { throw new Error( @@ -615,7 +612,7 @@ export { AliasedDirective$1 }; const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); const missingValue = findVariableDeclaration( getSourceFileOrError(bundle.program, _('/ngmodule.js')), 'missingValue'); - const value = host.getVariableValue(missingValue !); + const value = host.getVariableValue(missingValue!); expect(value).toBe(null); }); @@ -624,7 +621,7 @@ export { AliasedDirective$1 }; const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); const nonDecoratedVar = findVariableDeclaration( getSourceFileOrError(bundle.program, _('/ngmodule.js')), 'nonDecoratedVar'); - const value = host.getVariableValue(nonDecoratedVar !); + const value = host.getVariableValue(nonDecoratedVar!); expect(value).toBe(null); }); }); @@ -634,7 +631,7 @@ export { AliasedDirective$1 }; const bundle = makeTestBundleProgram(_('/ngmodule.js')); const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); const classSymbol = - host.findClassSymbols(bundle.program.getSourceFile(_('/ngmodule.js')) !)[0]; + host.findClassSymbols(bundle.program.getSourceFile(_('/ngmodule.js'))!)[0]; const endOfClass = host.getEndOfClass(classSymbol); expect(endOfClass.getText()) .toMatch( @@ -644,7 +641,7 @@ export { AliasedDirective$1 }; }); function findVariableDeclaration( - node: ts.Node | undefined, variableName: string): ts.VariableDeclaration|undefined { + node: ts.Node|undefined, variableName: string): ts.VariableDeclaration|undefined { if (!node) { return; } @@ -656,7 +653,7 @@ export { AliasedDirective$1 }; }); function findIdentifier( - node: ts.Node | undefined, identifierName: string, + node: ts.Node|undefined, identifierName: string, requireFn: (node: ts.Identifier) => boolean): ts.Identifier|undefined { if (!node) { return undefined; diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts index be3b064a677dd..0f5190a3c2a43 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts @@ -9,12 +9,15 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {ClassMemberKind, CtorParameter, Decorator, KnownDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; +import {ClassMemberKind, CtorParameter, Decorator, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; +import {DelegatingReflectionHost} from '../../src/host/delegating_host'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {Esm5ReflectionHost, getIifeBody} from '../../src/host/esm5_host'; +import {NgccReflectionHost} from '../../src/host/ngcc_host'; +import {BundleProgram} from '../../src/packages/bundle_program'; import {MockLogger} from '../helpers/mock_logger'; import {getRootFiles, makeTestBundleProgram, makeTestDtsBundleProgram} from '../helpers/utils'; @@ -22,7 +25,6 @@ import {expectTypeValueReferencesForParameters} from './util'; runInEachFileSystem(() => { describe('Esm5ReflectionHost', () => { - let _: typeof absoluteFrom; let SOME_DIRECTIVE_FILE: TestFile; @@ -48,6 +50,12 @@ runInEachFileSystem(() => { let MODULE_WITH_PROVIDERS_PROGRAM: TestFile[]; let NAMESPACED_IMPORT_FILE: TestFile; + // Helpers + const createHost = (bundle: BundleProgram, ngccHost: Esm5ReflectionHost) => { + const tsHost = new TypeScriptReflectionHost(bundle.program.getTypeChecker()); + return new DelegatingReflectionHost(tsHost, ngccHost); + }; + beforeEach(() => { _ = absoluteFrom; SOME_DIRECTIVE_FILE = { @@ -891,10 +899,10 @@ runInEachFileSystem(() => { it('should find the decorators on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); @@ -902,7 +910,7 @@ runInEachFileSystem(() => { const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -910,11 +918,11 @@ runInEachFileSystem(() => { it('should find the decorators on a class at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); @@ -922,7 +930,7 @@ runInEachFileSystem(() => { const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -930,7 +938,7 @@ runInEachFileSystem(() => { it('should return null if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); const decorators = host.getDecoratorsOfDeclaration(functionNode); @@ -940,7 +948,7 @@ runInEachFileSystem(() => { it('should return null if there are no decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode); @@ -950,7 +958,7 @@ runInEachFileSystem(() => { it('should ignore `decorators` if it is not an array literal', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); @@ -961,11 +969,11 @@ runInEachFileSystem(() => { it('should ignore decorator elements that are not object literals', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining<Decorator>({name: 'Directive'})); @@ -974,11 +982,11 @@ runInEachFileSystem(() => { it('should ignore decorator elements that have no `type` property', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -987,11 +995,11 @@ runInEachFileSystem(() => { it('should ignore decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1000,10 +1008,10 @@ runInEachFileSystem(() => { it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); @@ -1013,11 +1021,11 @@ runInEachFileSystem(() => { it('should be an empty array if decorator has no `args` property', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1027,11 +1035,11 @@ runInEachFileSystem(() => { it('should be an empty array if decorator\'s `args` has no property assignment', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1041,11 +1049,11 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1058,67 +1066,67 @@ runInEachFileSystem(() => { it('should find decorated members on a class at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); - expect(input1.decorators ![0].import).toEqual({name: 'Input', from: '@angular/core'}); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators![0].import).toEqual({name: 'Input', from: '@angular/core'}); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input2.decorators !.map(d => d.name)).toEqual(['Input']); - expect(input2.decorators ![0].import).toEqual({name: 'Input', from: '@angular/core'}); + expect(input2.decorators!.map(d => d.name)).toEqual(['Input']); + expect(input2.decorators![0].import).toEqual({name: 'Input', from: '@angular/core'}); }); it('should find decorated members on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input2.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input2.decorators!.map(d => d.name)).toEqual(['Input']); }); it('should find Object.defineProperty members on a class', () => { loadTestFiles([ACCESSORS_FILE]); const bundle = makeTestBundleProgram(ACCESSORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, ACCESSORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const setter = members.find(member => member.name === 'setter') !; + const setter = members.find(member => member.name === 'setter')!; expect(setter.kind).toEqual(ClassMemberKind.Setter); expect(setter.isStatic).toEqual(false); expect(setter.value).toBeNull(); - expect(setter.decorators !.map(d => d.name)).toEqual(['Input']); - expect(ts.isFunctionExpression(setter.implementation !)).toEqual(true); + expect(setter.decorators!.map(d => d.name)).toEqual(['Input']); + expect(ts.isFunctionExpression(setter.implementation!)).toEqual(true); expect((setter.implementation as ts.FunctionExpression).body.statements[0].getText()) .toEqual('this.value = value;'); - const getter = members.find(member => member.name === 'getter') !; + const getter = members.find(member => member.name === 'getter')!; expect(getter.kind).toEqual(ClassMemberKind.Getter); expect(getter.isStatic).toEqual(false); expect(getter.value).toBeNull(); - expect(getter.decorators !.map(d => d.name)).toEqual(['Output']); - expect(ts.isFunctionExpression(getter.implementation !)).toEqual(true); + expect(getter.decorators!.map(d => d.name)).toEqual(['Output']); + expect(ts.isFunctionExpression(getter.implementation!)).toEqual(true); expect((getter.implementation as ts.FunctionExpression).body.statements[0].getText()) .toEqual('return null;'); @@ -1126,16 +1134,16 @@ runInEachFileSystem(() => { members.filter(member => member.name === 'setterAndGetter'); expect(combinedSetter.kind).toEqual(ClassMemberKind.Setter); expect(combinedSetter.isStatic).toEqual(false); - expect(combinedSetter.decorators !.map(d => d.name)).toEqual(['Input']); + expect(combinedSetter.decorators!.map(d => d.name)).toEqual(['Input']); expect(combinedGetter.kind).toEqual(ClassMemberKind.Getter); expect(combinedGetter.isStatic).toEqual(false); - expect(combinedGetter.decorators !.map(d => d.name)).toEqual([]); + expect(combinedGetter.decorators!.map(d => d.name)).toEqual([]); - const staticSetter = members.find(member => member.name === 'staticSetter') !; + const staticSetter = members.find(member => member.name === 'staticSetter')!; expect(staticSetter.kind).toEqual(ClassMemberKind.Setter); expect(staticSetter.isStatic).toEqual(true); expect(staticSetter.value).toBeNull(); - expect(staticSetter.decorators !.map(d => d.name)).toEqual([]); + expect(staticSetter.decorators!.map(d => d.name)).toEqual([]); const none = members.find(member => member.name === 'none'); expect(none).toBeUndefined(); @@ -1147,56 +1155,56 @@ runInEachFileSystem(() => { it('should find non decorated properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const instanceProperty = members.find(member => member.name === 'instanceProperty') !; + const instanceProperty = members.find(member => member.name === 'instanceProperty')!; expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); expect(instanceProperty.isStatic).toEqual(false); - expect(ts.isBinaryExpression(instanceProperty.implementation !)).toEqual(true); - expect(instanceProperty.value !.getText()).toEqual(`'instance'`); + expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); + expect(instanceProperty.value!.getText()).toEqual(`'instance'`); }); it('should find static methods on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticMethod = members.find(member => member.name === 'staticMethod') !; + const staticMethod = members.find(member => member.name === 'staticMethod')!; expect(staticMethod.kind).toEqual(ClassMemberKind.Method); expect(staticMethod.isStatic).toEqual(true); expect(staticMethod.value).toBeNull(); - expect(ts.isFunctionExpression(staticMethod.implementation !)).toEqual(true); + expect(ts.isFunctionExpression(staticMethod.implementation!)).toEqual(true); }); it('should find static properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticProperty = members.find(member => member.name === 'staticProperty') !; + const staticProperty = members.find(member => member.name === 'staticProperty')!; expect(staticProperty.kind).toEqual(ClassMemberKind.Property); expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation !)).toEqual(true); - expect(staticProperty.value !.getText()).toEqual(`'static'`); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); }); it('should accept `ctorParameters` as an array', () => { loadTestFiles([CTOR_DECORATORS_ARRAY_FILE]); const bundle = makeTestBundleProgram(CTOR_DECORATORS_ARRAY_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, CTOR_DECORATORS_ARRAY_FILE.name, 'CtorDecoratedAsArray', isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters).toBeDefined(); expect(parameters.map(parameter => parameter.name)).toEqual(['arg1']); @@ -1206,7 +1214,7 @@ runInEachFileSystem(() => { it('should throw if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); expect(() => { @@ -1217,7 +1225,7 @@ runInEachFileSystem(() => { it('should return an empty array if there are no prop decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); @@ -1229,7 +1237,7 @@ runInEachFileSystem(() => { () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); @@ -1241,13 +1249,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements that are not object literals', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); @@ -1256,13 +1264,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements that have no `type` property', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); @@ -1271,13 +1279,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Input'})); @@ -1287,13 +1295,13 @@ runInEachFileSystem(() => { it('should be an empty array if prop decorator has no `args` property', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1304,13 +1312,14 @@ runInEachFileSystem(() => { () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1320,13 +1329,13 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1338,7 +1347,7 @@ runInEachFileSystem(() => { () => { loadTestFiles([UNWANTED_PROTOTYPE_EXPORT_FILE]); const bundle = makeTestBundleProgram(UNWANTED_PROTOTYPE_EXPORT_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, UNWANTED_PROTOTYPE_EXPORT_FILE.name, 'SomeParam', isNamedClassDeclaration); @@ -1351,16 +1360,16 @@ runInEachFileSystem(() => { it('should find the decorated constructor parameters', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, @@ -1370,17 +1379,17 @@ runInEachFileSystem(() => { it('should find the decorated constructor parameters at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, @@ -1390,10 +1399,12 @@ runInEachFileSystem(() => { it('should throw if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(() => { host.getConstructorParameters(functionNode); }) + expect(() => { + host.getConstructorParameters(functionNode); + }) .toThrowError( 'Attempted to get constructor parameters of a non-class: "function foo() {}"'); }); @@ -1404,22 +1415,22 @@ runInEachFileSystem(() => { it('should return an array even if there are no decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toEqual(jasmine.any(Array)); - expect(parameters !.length).toEqual(1); - expect(parameters ![0].name).toEqual('foo'); - expect(parameters ![0].decorators).toBe(null); + expect(parameters!.length).toEqual(1); + expect(parameters![0].name).toEqual('foo'); + expect(parameters![0].decorators).toBe(null); }); it('should return an empty array if there are no constructor parameters', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', isNamedVariableDeclaration); @@ -1434,14 +1445,14 @@ runInEachFileSystem(() => { it('should ignore `ctorParameters` if it does not return an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(1); - expect(parameters ![0]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters!.length).toBe(1); + expect(parameters![0]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg1', decorators: null, })); @@ -1451,18 +1462,18 @@ runInEachFileSystem(() => { it('should ignore param decorator elements that are not object literals', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(2); - expect(parameters ![0]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters!.length).toBe(2); + expect(parameters![0]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg1', decorators: null, })); - expect(parameters ![1]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters![1]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg2', decorators: jasmine.any(Array) as any })); @@ -1471,12 +1482,12 @@ runInEachFileSystem(() => { it('should ignore param decorator elements that have no `type` property', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); @@ -1485,12 +1496,12 @@ runInEachFileSystem(() => { it('should ignore param decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); @@ -1499,12 +1510,12 @@ runInEachFileSystem(() => { it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![2].decorators !; + const decorators = parameters![2].decorators!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toEqual({name: 'Inject', from: '@angular/core'}); @@ -1526,7 +1537,7 @@ runInEachFileSystem(() => { loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); return host.getConstructorParameters(classNode); @@ -1570,7 +1581,7 @@ runInEachFileSystem(() => { return _super !== null && _super.apply(this, arguments) || this; }`); - expect(parameters !.length).toBe(1); + expect(parameters!.length).toBe(1); }); it('does not consider manual super calls as synthesized', () => { @@ -1579,7 +1590,7 @@ runInEachFileSystem(() => { return _super.call(this) || this; }`); - expect(parameters !.length).toBe(0); + expect(parameters!.length).toBe(0); }); it('does not consider empty constructors as synthesized', () => { @@ -1587,7 +1598,7 @@ runInEachFileSystem(() => { function TestClass() { }`); - expect(parameters !.length).toBe(0); + expect(parameters!.length).toBe(0); }); }); @@ -1595,13 +1606,13 @@ runInEachFileSystem(() => { it('should be an empty array if param decorator has no `args` property', () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(1); - const decorators = parameters ![0].decorators !; + expect(parameters!.length).toBe(1); + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1612,12 +1623,13 @@ runInEachFileSystem(() => { () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1627,12 +1639,12 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1646,45 +1658,45 @@ runInEachFileSystem(() => { () => { loadTestFiles([FUNCTION_BODY_FILE]); const bundle = makeTestBundleProgram(FUNCTION_BODY_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const fooNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration) !; - const fooDef = host.getDefinitionOfFunction(fooNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration)!; + const fooDef = host.getDefinitionOfFunction(fooNode)!; expect(fooDef.node).toBe(fooNode); - expect(fooDef.body !.length).toEqual(1); - expect(fooDef.body ![0].getText()).toEqual(`return x;`); + expect(fooDef.body!.length).toEqual(1); + expect(fooDef.body![0].getText()).toEqual(`return x;`); expect(fooDef.parameters.length).toEqual(1); expect(fooDef.parameters[0].name).toEqual('x'); expect(fooDef.parameters[0].initializer).toBe(null); const barNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration) !; - const barDef = host.getDefinitionOfFunction(barNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration)!; + const barDef = host.getDefinitionOfFunction(barNode)!; expect(barDef.node).toBe(barNode); - expect(barDef.body !.length).toEqual(1); - expect(ts.isReturnStatement(barDef.body ![0])).toBeTruthy(); - expect(barDef.body ![0].getText()).toEqual(`return x + y;`); + expect(barDef.body!.length).toEqual(1); + expect(ts.isReturnStatement(barDef.body![0])).toBeTruthy(); + expect(barDef.body![0].getText()).toEqual(`return x + y;`); expect(barDef.parameters.length).toEqual(2); expect(barDef.parameters[0].name).toEqual('x'); expect(fooDef.parameters[0].initializer).toBe(null); expect(barDef.parameters[1].name).toEqual('y'); - expect(barDef.parameters[1].initializer !.getText()).toEqual('42'); + expect(barDef.parameters[1].initializer!.getText()).toEqual('42'); const bazNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration) !; - const bazDef = host.getDefinitionOfFunction(bazNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration)!; + const bazDef = host.getDefinitionOfFunction(bazNode)!; expect(bazDef.node).toBe(bazNode); - expect(bazDef.body !.length).toEqual(3); + expect(bazDef.body!.length).toEqual(3); expect(bazDef.parameters.length).toEqual(1); expect(bazDef.parameters[0].name).toEqual('x'); expect(bazDef.parameters[0].initializer).toBe(null); const quxNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration) !; - const quxDef = host.getDefinitionOfFunction(quxNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration)!; + const quxDef = host.getDefinitionOfFunction(quxNode)!; expect(quxDef.node).toBe(quxNode); - expect(quxDef.body !.length).toEqual(2); + expect(quxDef.body!.length).toEqual(2); expect(quxDef.parameters.length).toEqual(1); expect(quxDef.parameters[0].name).toEqual('x'); expect(quxDef.parameters[0].initializer).toBe(null); @@ -1695,7 +1707,7 @@ runInEachFileSystem(() => { it('should find the import of an identifier', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/b.js'), 'b', isNamedVariableDeclaration); const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); @@ -1706,7 +1718,7 @@ runInEachFileSystem(() => { it('should find the name by which the identifier was exported, not imported', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/b.js'), 'c', isNamedVariableDeclaration); const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); @@ -1717,7 +1729,7 @@ runInEachFileSystem(() => { it('should return null if the identifier was not imported', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/b.js'), 'd', isNamedVariableDeclaration); const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); @@ -1729,10 +1741,10 @@ runInEachFileSystem(() => { describe('getDeclarationOfIdentifier()', () => { // Helpers const createTestForTsHelper = - (program: ts.Program, host: Esm5ReflectionHost, srcFile: TestFile, + (program: ts.Program, host: NgccReflectionHost, srcFile: TestFile, getHelperDeclaration: (name: string) => ts.Declaration) => (varName: string, helperName: string, knownAs: KnownDeclaration, - viaModule: string | null = null) => { + viaModule: string|null = null) => { const node = getDeclaration(program, srcFile.name, varName, ts.isVariableDeclaration); const helperIdentifier = getIdentifierFromCallExpression(node); @@ -1740,7 +1752,8 @@ runInEachFileSystem(() => { expect(helperDeclaration).toEqual({ known: knownAs, - node: getHelperDeclaration(helperName), viaModule, + node: getHelperDeclaration(helperName), + viaModule, }); }; @@ -1756,11 +1769,11 @@ runInEachFileSystem(() => { it('should return the declaration of a locally defined identifier', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const ctrDecorators = host.getConstructorParameters(classNode) !; - const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{ + const ctrDecorators = host.getConstructorParameters(classNode)!; + const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { local: true, expression: ts.Identifier, defaultImportStatement: null, @@ -1771,51 +1784,53 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe(null); }); it('should return the declaration of an externally defined identifier', () => { loadFakeCore(getFileSystem()); loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode) !; - const identifierOfDirective = ((classDecorators[0].node as ts.ObjectLiteralExpression) - .properties[0] as ts.PropertyAssignment) - .initializer as ts.Identifier; + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const identifierOfDirective = + ((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as + ts.PropertyAssignment) + .initializer as ts.Identifier; const expectedDeclarationNode = getDeclaration( bundle.program, _('/node_modules/@angular/core/index.d.ts'), 'Directive', isNamedVariableDeclaration); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe('@angular/core'); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); it('should return the source-file of an import namespace', () => { loadFakeCore(getFileSystem()); loadTestFiles([NAMESPACED_IMPORT_FILE]); const bundle = makeTestBundleProgram(NAMESPACED_IMPORT_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, NAMESPACED_IMPORT_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode) !; - const identifier = (((classDecorators[0].node as ts.ObjectLiteralExpression) - .properties[0] as ts.PropertyAssignment) - .initializer as ts.PropertyAccessExpression) - .expression as ts.Identifier; + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const identifier = + (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as + ts.PropertyAssignment) + .initializer as ts.PropertyAccessExpression) + .expression as ts.Identifier; const expectedDeclarationNode = getSourceFileOrError(bundle.program, _('/node_modules/@angular/core/index.d.ts')); const actualDeclaration = host.getDeclarationOfIdentifier(identifier); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe('@angular/core'); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); it('should return the correct declaration for an inner function identifier inside an ES5 IIFE', @@ -1825,25 +1840,26 @@ runInEachFileSystem(() => { .and.callThrough(); loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const outerDeclaration = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerDeclaration = (((outerDeclaration.initializer as ts.ParenthesizedExpression) - .expression as ts.CallExpression) - .expression as ts.FunctionExpression) - .body.statements[0] as ts.FunctionDeclaration; + const innerDeclaration = + (((outerDeclaration.initializer as ts.ParenthesizedExpression).expression as + ts.CallExpression) + .expression as ts.FunctionExpression) + .body.statements[0] as ts.FunctionDeclaration; const outerIdentifier = outerDeclaration.name as ts.Identifier; const innerIdentifier = innerDeclaration.name as ts.Identifier; - expect(host.getDeclarationOfIdentifier(outerIdentifier) !.node).toBe(outerDeclaration); + expect(host.getDeclarationOfIdentifier(outerIdentifier)!.node).toBe(outerDeclaration); expect(superGetDeclarationOfIdentifierSpy).toHaveBeenCalledWith(outerIdentifier); expect(superGetDeclarationOfIdentifierSpy).toHaveBeenCalledTimes(1); superGetDeclarationOfIdentifierSpy.calls.reset(); - expect(host.getDeclarationOfIdentifier(innerIdentifier) !.node).toBe(outerDeclaration); + expect(host.getDeclarationOfIdentifier(innerIdentifier)!.node).toBe(outerDeclaration); expect(superGetDeclarationOfIdentifierSpy).toHaveBeenCalledWith(innerIdentifier); expect(superGetDeclarationOfIdentifierSpy).toHaveBeenCalledWith(outerIdentifier); expect(superGetDeclarationOfIdentifierSpy).toHaveBeenCalledTimes(2); @@ -1864,17 +1880,17 @@ runInEachFileSystem(() => { loadTestFiles([PROGRAM_FILE]); const bundle = makeTestBundleProgram(PROGRAM_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const expectedDeclaration = getDeclaration( bundle.program, PROGRAM_FILE.name, 'AliasedClass', isNamedVariableDeclaration); // Grab the `AliasedClass_1` identifier (which is an alias for `AliasedClass`). const aliasIdentifier = (expectedDeclaration.initializer as ts.BinaryExpression).left as ts.Identifier; - const actualDeclaration = host.getDeclarationOfIdentifier(aliasIdentifier) !; + const actualDeclaration = host.getDeclarationOfIdentifier(aliasIdentifier)!; expect(aliasIdentifier.getText()).toBe('AliasedClass_1'); - expect(actualDeclaration.node !.getText()).toBe(expectedDeclaration.getText()); + expect(actualDeclaration.node!.getText()).toBe(expectedDeclaration.getText()); }); it('should return the correct outer declaration for an aliased inner class declaration inside an ES5 IIFE', @@ -1907,23 +1923,24 @@ runInEachFileSystem(() => { loadTestFiles([PROGRAM_FILE]); const bundle = makeTestBundleProgram(PROGRAM_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const expectedDeclaration = getDeclaration( bundle.program, PROGRAM_FILE.name, 'FroalaEditorModule', isNamedVariableDeclaration); // Grab the `FroalaEditorModule_1` identifier returned from the `forRoot()` method - const forRootMethod = ((((expectedDeclaration.initializer as ts.ParenthesizedExpression) - .expression as ts.CallExpression) - .expression as ts.FunctionExpression) - .body.statements[2] as ts.ExpressionStatement); + const forRootMethod = + ((((expectedDeclaration.initializer as ts.ParenthesizedExpression).expression as + ts.CallExpression) + .expression as ts.FunctionExpression) + .body.statements[2] as ts.ExpressionStatement); const identifier = (((((forRootMethod.expression as ts.BinaryExpression).right as ts.FunctionExpression) .body.statements[0] as ts.ReturnStatement) .expression as ts.ObjectLiteralExpression) .properties[0] as ts.PropertyAssignment) .initializer as ts.Identifier; - const actualDeclaration = host.getDeclarationOfIdentifier(identifier) !; - expect(actualDeclaration.node !.getText()).toBe(expectedDeclaration.getText()); + const actualDeclaration = host.getDeclarationOfIdentifier(identifier)!; + expect(actualDeclaration.node!.getText()).toBe(expectedDeclaration.getText()); }); it('should recognize TypeScript helpers (as function declarations)', () => { @@ -1941,7 +1958,7 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, file, @@ -1968,7 +1985,7 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, file, @@ -1995,7 +2012,7 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, file, @@ -2022,7 +2039,7 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, file, @@ -2059,7 +2076,7 @@ runInEachFileSystem(() => { const [testFile, tslibFile] = files; const bundle = makeTestBundleProgram(testFile.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, testFile, @@ -2096,7 +2113,7 @@ runInEachFileSystem(() => { const [testFile, tslibFile] = files; const bundle = makeTestBundleProgram(testFile.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const testForHelper = createTestForTsHelper( bundle.program, host, testFile, @@ -2107,6 +2124,68 @@ runInEachFileSystem(() => { testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); }); + + it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + var a = __assign({foo: 'bar'}, {baz: 'qux'}); + var b = __spread(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); + + const testForHelper = (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = getDeclaration(bundle.program, file.name, varName, ts.isVariableDeclaration); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + known: knownAs, + expression: helperIdentifier, + node: null, + viaModule: null, + }); + }; + + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + }); + + it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); + var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); + + const testForHelper = (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = getDeclaration(bundle.program, file.name, varName, ts.isVariableDeclaration); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + known: knownAs, + expression: helperIdentifier, + node: null, + viaModule: null, + }); + }; + + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + }); }); describe('getExportsOfModule()', () => { @@ -2114,11 +2193,11 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/b.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.keys())).toEqual([ + expect(Array.from(exportDeclarations!.keys())).toEqual([ 'Directive', 'a', 'b', @@ -2130,8 +2209,8 @@ runInEachFileSystem(() => { ]); const values = - Array.from(exportDeclarations !.values()) - .map(declaration => [declaration.node !.getText(), declaration.viaModule]); + Array.from(exportDeclarations!.values()) + .map(declaration => [declaration.node!.getText(), declaration.viaModule]); expect(values).toEqual([ [`Directive: FnWithArg<(clazz: any) => any>`, null], [`a = 'a'`, null], @@ -2162,9 +2241,9 @@ runInEachFileSystem(() => { }; loadTestFiles([tslib]); const bundle = makeTestBundleProgram(tslib.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const sf = getSourceFileOrError(bundle.program, tslib.name); - const exportDeclarations = host.getExportsOfModule(sf) !; + const exportDeclarations = host.getExportsOfModule(sf)!; expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known])) .toEqual([ @@ -2180,55 +2259,55 @@ runInEachFileSystem(() => { it('should return the class symbol for an ES2015 class', () => { loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); const classSymbol = host.getClassSymbol(node); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(node); - expect(classSymbol !.implementation.valueDeclaration).toBe(node); + expect(classSymbol!.declaration.valueDeclaration).toBe(node); + expect(classSymbol!.implementation.valueDeclaration).toBe(node); }); it('should return the class symbol for an ES5 class (outer variable declaration)', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the class symbol for an ES5 class (inner function declaration)', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(innerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the same class symbol (of the outer declaration) for outer and inner declarations', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; - const innerSymbol = host.getClassSymbol(innerNode) !; - const outerSymbol = host.getClassSymbol(outerNode) !; + const innerSymbol = host.getClassSymbol(innerNode)!; + const outerSymbol = host.getClassSymbol(outerNode)!; expect(innerSymbol.declaration).toBe(outerSymbol.declaration); expect(innerSymbol.implementation).toBe(outerSymbol.implementation); }); @@ -2237,37 +2316,37 @@ runInEachFileSystem(() => { () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the class symbol for an ES5 class whose IIFE is not wrapped with inner parens', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return undefined if node is not an ES5 class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); const classSymbol = host.getClassSymbol(node); @@ -2282,7 +2361,7 @@ runInEachFileSystem(() => { }; loadTestFiles([testFile]); const bundle = makeTestBundleProgram(testFile.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration(bundle.program, testFile.name, 'MyClass', isNamedVariableDeclaration); const classSymbol = host.getClassSymbol(node); @@ -2295,7 +2374,7 @@ runInEachFileSystem(() => { it('should return true if a given node is a TS class declaration', () => { loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); expect(host.isClass(node)).toBe(true); @@ -2304,7 +2383,7 @@ runInEachFileSystem(() => { it('should return true if a given node is the outer variable declaration of a class', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); expect(host.isClass(node)).toBe(true); @@ -2313,17 +2392,17 @@ runInEachFileSystem(() => { it('should return true if a given node is the inner variable declaration of a class', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; expect(host.isClass(innerNode)).toBe(true); }); it('should return false if a given node is a function declaration', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); expect(host.isClass(node)).toBe(false); @@ -2339,7 +2418,7 @@ runInEachFileSystem(() => { loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); return host.hasBaseClass(classNode); @@ -2386,7 +2465,7 @@ runInEachFileSystem(() => { loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); const expression = host.getBaseClassExpression(classNode); @@ -2408,7 +2487,7 @@ runInEachFileSystem(() => { function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier !.text).toBe('BaseClass'); + expect(identifier!.text).toBe('BaseClass'); }); it('should find the base class of an IIFE with a unique name generated for the _super parameter', @@ -2423,7 +2502,7 @@ runInEachFileSystem(() => { function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier !.text).toBe('BaseClass'); + expect(identifier!.text).toBe('BaseClass'); }); it('should not find a base class for an IIFE without parameter', () => { @@ -2458,10 +2537,10 @@ runInEachFileSystem(() => { loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - const expression = host.getBaseClassExpression(classNode) !; + const expression = host.getBaseClassExpression(classNode)!; expect(expression.getText()).toBe('foo()'); }); }); @@ -2470,7 +2549,7 @@ runInEachFileSystem(() => { it('should return an array of all classes in the given source file', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(getRootFiles(DECORATED_FILES)[0]); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); @@ -2488,21 +2567,21 @@ runInEachFileSystem(() => { it('should return decorators of class symbol', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(getRootFiles(DECORATED_FILES)[0]); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); const classSymbolsPrimary = host.findClassSymbols(primaryFile); const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s)); expect(classDecoratorsPrimary.length).toEqual(2); - expect(classDecoratorsPrimary[0] !.map(d => d.name)).toEqual(['Directive']); - expect(classDecoratorsPrimary[1] !.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[0]!.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[1]!.map(d => d.name)).toEqual(['Directive']); const classSymbolsSecondary = host.findClassSymbols(secondaryFile); const classDecoratorsSecondary = classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s)); expect(classDecoratorsSecondary.length).toEqual(1); - expect(classDecoratorsSecondary[0] !.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsSecondary[0]!.map(d => d.name)).toEqual(['Directive']); }); }); @@ -2515,10 +2594,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/class1.js'), 'Class1', ts.isVariableDeclaration); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find the dts declaration for exported functions', () => { @@ -2528,10 +2608,11 @@ runInEachFileSystem(() => { const dts = makeTestDtsBundleProgram(_('/ep/typings/func1.d.ts'), _('/')); const mooFn = getDeclaration( bundle.program, _('/ep/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); }); it('should return null if there is no matching class in the matching dts file', () => { @@ -2541,7 +2622,8 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const missingClass = getDeclaration( bundle.program, _('/ep/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBe(null); }); @@ -2554,7 +2636,8 @@ runInEachFileSystem(() => { const missingClass = getDeclaration( bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', ts.isVariableDeclaration); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBe(null); }); @@ -2567,10 +2650,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find aliased exports', () => { @@ -2580,7 +2664,8 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const sourceClass = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'SourceClass', ts.isVariableDeclaration); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(sourceClass); if (dtsDeclaration === null) { @@ -2602,11 +2687,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const internalClass = getDeclaration( bundle.program, _('/ep/src/internal.js'), 'InternalClass', ts.isVariableDeclaration); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(internalClass); - expect(dtsDeclaration !.getSourceFile().fileName) - .toEqual(_('/ep/typings/internal.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/internal.d.ts')); }); it('should match publicly and internal exported classes correctly, even if they have the same name', @@ -2615,18 +2700,19 @@ runInEachFileSystem(() => { loadTestFiles(TYPINGS_DTS_FILES); const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle, dts)); const class2 = getDeclaration( bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedVariableDeclaration); const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration !.getSourceFile().fileName) + expect(class2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/class2.d.ts')); const internalClass2 = getDeclaration( bundle.program, _('/ep/src/internal.js'), 'Class2', isNamedVariableDeclaration); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration !.getSourceFile().fileName) + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/internal.d.ts')); }); }); @@ -2635,7 +2721,7 @@ runInEachFileSystem(() => { it('should return the name of the inner class declaration', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const emptyClass = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); @@ -2659,7 +2745,7 @@ runInEachFileSystem(() => { it('should return the name of the inner class declaration', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const emptyClass = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); @@ -2684,10 +2770,10 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(_('/src/index.js')); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/functions.js')); const fns = host.getModuleWithProvidersFunctions(file); - expect(fns.map(fn => [fn.declaration.name !.getText(), fn.ngModule.node.name.text])) + expect(fns.map(fn => [fn.declaration.name!.getText(), fn.ngModule.node.name.text])) .toEqual([ ['ngModuleIdentifier', 'InternalModule'], ['ngModuleWithEmptyProviders', 'InternalModule'], @@ -2701,7 +2787,7 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(_('/src/index.js')); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/methods.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ @@ -2732,7 +2818,7 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(_('/src/index.js')); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/outer_aliased_class.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ @@ -2745,7 +2831,7 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(_('/src/index.js')); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/inner_aliased_class.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ @@ -2758,9 +2844,9 @@ runInEachFileSystem(() => { it('should return the last static property of the class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new Esm5ReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new Esm5ReflectionHost(new MockLogger(), false, bundle)); const classSymbol = - host.findClassSymbols(bundle.program.getSourceFile(SOME_DIRECTIVE_FILE.name) !)[0]; + host.findClassSymbols(bundle.program.getSourceFile(SOME_DIRECTIVE_FILE.name)!)[0]; const endOfClass = host.getEndOfClass(classSymbol); expect(endOfClass.getText()).toEqual(`SomeDirective.propDecorators = { "input1": [{ type: Input },], diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_import_helper_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_import_helper_spec.ts index 35929b4f7106e..9be9a1f3a2129 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_import_helper_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_import_helper_spec.ts @@ -7,7 +7,7 @@ */ import {absoluteFrom} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {ClassMemberKind, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadTestFiles} from '../../../test/helpers'; @@ -81,16 +81,16 @@ runInEachFileSystem(() => { const host = new UmdReflectionHost(new MockLogger(), false, bundle); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('core.Directive'); + expect(decorator.identifier!.getText()).toEqual('core.Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -102,16 +102,16 @@ runInEachFileSystem(() => { const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'AliasedDirective$1', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); - expect(decorator.identifier !.getText()).toEqual('core.Directive'); + expect(decorator.identifier!.getText()).toEqual('core.Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -126,15 +126,15 @@ runInEachFileSystem(() => { bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); describe('getConstructorParameters', () => { @@ -148,10 +148,10 @@ runInEachFileSystem(() => { const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index 6a549b65eba32..5aea2a1f13287 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -9,12 +9,15 @@ import * as ts from 'typescript'; import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {ClassMemberKind, CtorParameter, Import, InlineDeclaration, KnownDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration} from '../../../src/ngtsc/reflection'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; +import {ClassMemberKind, CtorParameter, Import, InlineDeclaration, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; +import {DelegatingReflectionHost} from '../../src/host/delegating_host'; import {getIifeBody} from '../../src/host/esm5_host'; -import {UmdReflectionHost, parseStatementForUmdModule} from '../../src/host/umd_host'; +import {NgccReflectionHost} from '../../src/host/ngcc_host'; +import {parseStatementForUmdModule, UmdReflectionHost} from '../../src/host/umd_host'; +import {BundleProgram} from '../../src/packages/bundle_program'; import {MockLogger} from '../helpers/mock_logger'; import {getRootFiles, makeTestBundleProgram} from '../helpers/utils'; @@ -45,6 +48,12 @@ runInEachFileSystem(() => { let TYPINGS_DTS_FILES: TestFile[]; let MODULE_WITH_PROVIDERS_PROGRAM: TestFile[]; + // Helpers + const createHost = (bundle: BundleProgram, ngccHost: UmdReflectionHost) => { + const tsHost = new TypeScriptReflectionHost(bundle.program.getTypeChecker()); + return new DelegatingReflectionHost(tsHost, ngccHost); + }; + beforeEach(() => { _ = absoluteFrom; @@ -1084,10 +1093,10 @@ runInEachFileSystem(() => { it('should find the decorators on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); @@ -1095,7 +1104,7 @@ runInEachFileSystem(() => { const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -1103,11 +1112,11 @@ runInEachFileSystem(() => { it('should find the decorators on a class at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators).toBeDefined(); expect(decorators.length).toEqual(1); @@ -1115,7 +1124,7 @@ runInEachFileSystem(() => { const decorator = decorators[0]; expect(decorator.name).toEqual('Directive'); expect(decorator.import).toEqual({name: 'Directive', from: '@angular/core'}); - expect(decorator.args !.map(arg => arg.getText())).toEqual([ + expect(decorator.args!.map(arg => arg.getText())).toEqual([ '{ selector: \'[someDirective]\' }', ]); }); @@ -1123,7 +1132,7 @@ runInEachFileSystem(() => { it('should return null if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); const decorators = host.getDecoratorsOfDeclaration(functionNode); @@ -1133,7 +1142,7 @@ runInEachFileSystem(() => { it('should return null if there are no decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); const decorators = host.getDecoratorsOfDeclaration(classNode); @@ -1143,7 +1152,7 @@ runInEachFileSystem(() => { it('should ignore `decorators` if it is not an array literal', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); @@ -1154,11 +1163,11 @@ runInEachFileSystem(() => { it('should ignore decorator elements that are not object literals', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1167,11 +1176,11 @@ runInEachFileSystem(() => { it('should ignore decorator elements that have no `type` property', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1180,11 +1189,11 @@ runInEachFileSystem(() => { it('should ignore decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1193,11 +1202,11 @@ runInEachFileSystem(() => { it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); @@ -1206,32 +1215,32 @@ runInEachFileSystem(() => { it('should find decorated members on a class at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); describe('(returned decorators `args`)', () => { it('should be an empty array if decorator has no `args` property', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1241,11 +1250,11 @@ runInEachFileSystem(() => { it('should be an empty array if decorator\'s `args` has no property assignment', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1255,11 +1264,11 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Directive'); @@ -1272,70 +1281,70 @@ runInEachFileSystem(() => { it('should find decorated members on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const input1 = members.find(member => member.name === 'input1') !; + const input1 = members.find(member => member.name === 'input1')!; expect(input1.kind).toEqual(ClassMemberKind.Property); expect(input1.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); - const input2 = members.find(member => member.name === 'input2') !; + const input2 = members.find(member => member.name === 'input2')!; expect(input2.kind).toEqual(ClassMemberKind.Property); expect(input2.isStatic).toEqual(false); - expect(input1.decorators !.map(d => d.name)).toEqual(['Input']); + expect(input1.decorators!.map(d => d.name)).toEqual(['Input']); }); it('should find non decorated properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const instanceProperty = members.find(member => member.name === 'instanceProperty') !; + const instanceProperty = members.find(member => member.name === 'instanceProperty')!; expect(instanceProperty.kind).toEqual(ClassMemberKind.Property); expect(instanceProperty.isStatic).toEqual(false); - expect(ts.isBinaryExpression(instanceProperty.implementation !)).toEqual(true); - expect(instanceProperty.value !.getText()).toEqual(`'instance'`); + expect(ts.isBinaryExpression(instanceProperty.implementation!)).toEqual(true); + expect(instanceProperty.value!.getText()).toEqual(`'instance'`); }); it('should find static methods on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticMethod = members.find(member => member.name === 'staticMethod') !; + const staticMethod = members.find(member => member.name === 'staticMethod')!; expect(staticMethod.kind).toEqual(ClassMemberKind.Method); expect(staticMethod.isStatic).toEqual(true); - expect(ts.isFunctionExpression(staticMethod.implementation !)).toEqual(true); + expect(ts.isFunctionExpression(staticMethod.implementation!)).toEqual(true); }); it('should find static properties on a class', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const staticProperty = members.find(member => member.name === 'staticProperty') !; + const staticProperty = members.find(member => member.name === 'staticProperty')!; expect(staticProperty.kind).toEqual(ClassMemberKind.Property); expect(staticProperty.isStatic).toEqual(true); - expect(ts.isPropertyAccessExpression(staticProperty.implementation !)).toEqual(true); - expect(staticProperty.value !.getText()).toEqual(`'static'`); + expect(ts.isPropertyAccessExpression(staticProperty.implementation!)).toEqual(true); + expect(staticProperty.value!.getText()).toEqual(`'static'`); }); it('should throw if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); expect(() => { @@ -1346,7 +1355,7 @@ runInEachFileSystem(() => { it('should return an empty array if there are no prop decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); @@ -1358,7 +1367,7 @@ runInEachFileSystem(() => { () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); @@ -1370,13 +1379,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements that are not object literals', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1385,13 +1394,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements that have no `type` property', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1401,13 +1410,13 @@ runInEachFileSystem(() => { it('should ignore prop decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_PROP_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Directive'})); @@ -1416,11 +1425,11 @@ runInEachFileSystem(() => { it('should have import information on decorators', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const decorators = host.getDecoratorsOfDeclaration(classNode) !; + const decorators = host.getDecoratorsOfDeclaration(classNode)!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toEqual({name: 'Directive', from: '@angular/core'}); @@ -1430,13 +1439,13 @@ runInEachFileSystem(() => { it('should be an empty array if prop decorator has no `args` property', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1446,13 +1455,13 @@ runInEachFileSystem(() => { it('should be an empty array if prop decorator\'s `args` has no property assignment', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1462,13 +1471,13 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_PROP_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_PROP_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const members = host.getMembersOfClass(classNode); - const prop = members.find(m => m.name === 'prop') !; - const decorators = prop.decorators !; + const prop = members.find(m => m.name === 'prop')!; + const decorators = prop.decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Input'); @@ -1480,16 +1489,16 @@ runInEachFileSystem(() => { it('should find the decorated constructor parameters', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, @@ -1499,17 +1508,17 @@ runInEachFileSystem(() => { it('should find the decorated constructor parameters at the top level', () => { loadTestFiles([TOPLEVEL_DECORATORS_FILE]); const bundle = makeTestBundleProgram(TOPLEVEL_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, TOPLEVEL_DECORATORS_FILE.name, 'SomeDirective', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toBeDefined(); - expect(parameters !.map(parameter => parameter.name)).toEqual([ + expect(parameters!.map(parameter => parameter.name)).toEqual([ '_viewContainer', '_template', 'injected' ]); - expectTypeValueReferencesForParameters(parameters !, [ + expectTypeValueReferencesForParameters(parameters!, [ 'ViewContainerRef', 'TemplateRef', null, @@ -1519,11 +1528,11 @@ runInEachFileSystem(() => { it('should accept `ctorParameters` as an array', () => { loadTestFiles([CTOR_DECORATORS_ARRAY_FILE]); const bundle = makeTestBundleProgram(CTOR_DECORATORS_ARRAY_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, CTOR_DECORATORS_ARRAY_FILE.name, 'CtorDecoratedAsArray', isNamedVariableDeclaration); - const parameters = host.getConstructorParameters(classNode) !; + const parameters = host.getConstructorParameters(classNode)!; expect(parameters).toBeDefined(); expect(parameters.map(parameter => parameter.name)).toEqual(['arg1']); @@ -1533,10 +1542,12 @@ runInEachFileSystem(() => { it('should throw if the symbol is not a class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const functionNode = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); - expect(() => { host.getConstructorParameters(functionNode); }) + expect(() => { + host.getConstructorParameters(functionNode); + }) .toThrowError( 'Attempted to get constructor parameters of a non-class: "function foo() {}"'); }); @@ -1547,22 +1558,22 @@ runInEachFileSystem(() => { it('should return an array even if there are no decorators', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); expect(parameters).toEqual(jasmine.any(Array)); - expect(parameters !.length).toEqual(1); - expect(parameters ![0].name).toEqual('foo'); - expect(parameters ![0].decorators).toBe(null); + expect(parameters!.length).toEqual(1); + expect(parameters![0].name).toEqual('foo'); + expect(parameters![0].decorators).toBe(null); }); it('should return an empty array if there are no constructor parameters', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', isNamedVariableDeclaration); @@ -1577,14 +1588,14 @@ runInEachFileSystem(() => { it('should ignore `ctorParameters` if it does not return an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(1); - expect(parameters ![0]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters!.length).toBe(1); + expect(parameters![0]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg1', decorators: null, })); @@ -1594,18 +1605,18 @@ runInEachFileSystem(() => { it('should ignore param decorator elements that are not object literals', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(2); - expect(parameters ![0]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters!.length).toBe(2); + expect(parameters![0]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg1', decorators: null, })); - expect(parameters ![1]).toEqual(jasmine.objectContaining<CtorParameter>({ + expect(parameters![1]).toEqual(jasmine.objectContaining<CtorParameter>({ name: 'arg2', decorators: jasmine.any(Array) as any })); @@ -1614,12 +1625,12 @@ runInEachFileSystem(() => { it('should ignore param decorator elements that have no `type` property', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); @@ -1628,12 +1639,12 @@ runInEachFileSystem(() => { it('should ignore param decorator elements whose `type` value is not an identifier', () => { loadTestFiles([INVALID_CTOR_DECORATORS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATORS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0]).toEqual(jasmine.objectContaining({name: 'Inject'})); @@ -1642,7 +1653,7 @@ runInEachFileSystem(() => { it('should use `getImportOfIdentifier()` to retrieve import info', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); @@ -1651,7 +1662,7 @@ runInEachFileSystem(() => { .and.returnValue(mockImportInfo); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![2].decorators !; + const decorators = parameters![2].decorators!; expect(decorators.length).toEqual(1); expect(decorators[0].import).toBe(mockImportInfo); @@ -1662,13 +1673,13 @@ runInEachFileSystem(() => { it('should be an empty array if param decorator has no `args` property', () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - expect(parameters !.length).toBe(1); - const decorators = parameters ![0].decorators !; + expect(parameters!.length).toBe(1); + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1679,12 +1690,13 @@ runInEachFileSystem(() => { () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1694,12 +1706,12 @@ runInEachFileSystem(() => { it('should be an empty array if `args` property value is not an array literal', () => { loadTestFiles([INVALID_CTOR_DECORATOR_ARGS_FILE]); const bundle = makeTestBundleProgram(INVALID_CTOR_DECORATOR_ARGS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', isNamedVariableDeclaration); const parameters = host.getConstructorParameters(classNode); - const decorators = parameters ![0].decorators !; + const decorators = parameters![0].decorators!; expect(decorators.length).toBe(1); expect(decorators[0].name).toBe('Inject'); @@ -1713,45 +1725,45 @@ runInEachFileSystem(() => { () => { loadTestFiles([FUNCTION_BODY_FILE]); const bundle = makeTestBundleProgram(FUNCTION_BODY_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const fooNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration) !; - const fooDef = host.getDefinitionOfFunction(fooNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'foo', isNamedFunctionDeclaration)!; + const fooDef = host.getDefinitionOfFunction(fooNode)!; expect(fooDef.node).toBe(fooNode); - expect(fooDef.body !.length).toEqual(1); - expect(fooDef.body ![0].getText()).toEqual(`return x;`); + expect(fooDef.body!.length).toEqual(1); + expect(fooDef.body![0].getText()).toEqual(`return x;`); expect(fooDef.parameters.length).toEqual(1); expect(fooDef.parameters[0].name).toEqual('x'); expect(fooDef.parameters[0].initializer).toBe(null); const barNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration) !; - const barDef = host.getDefinitionOfFunction(barNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'bar', isNamedFunctionDeclaration)!; + const barDef = host.getDefinitionOfFunction(barNode)!; expect(barDef.node).toBe(barNode); - expect(barDef.body !.length).toEqual(1); - expect(ts.isReturnStatement(barDef.body ![0])).toBeTruthy(); - expect(barDef.body ![0].getText()).toEqual(`return x + y;`); + expect(barDef.body!.length).toEqual(1); + expect(ts.isReturnStatement(barDef.body![0])).toBeTruthy(); + expect(barDef.body![0].getText()).toEqual(`return x + y;`); expect(barDef.parameters.length).toEqual(2); expect(barDef.parameters[0].name).toEqual('x'); expect(fooDef.parameters[0].initializer).toBe(null); expect(barDef.parameters[1].name).toEqual('y'); - expect(barDef.parameters[1].initializer !.getText()).toEqual('42'); + expect(barDef.parameters[1].initializer!.getText()).toEqual('42'); const bazNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration) !; - const bazDef = host.getDefinitionOfFunction(bazNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'baz', isNamedFunctionDeclaration)!; + const bazDef = host.getDefinitionOfFunction(bazNode)!; expect(bazDef.node).toBe(bazNode); - expect(bazDef.body !.length).toEqual(3); + expect(bazDef.body!.length).toEqual(3); expect(bazDef.parameters.length).toEqual(1); expect(bazDef.parameters[0].name).toEqual('x'); expect(bazDef.parameters[0].initializer).toBe(null); const quxNode = getDeclaration( - bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration) !; - const quxDef = host.getDefinitionOfFunction(quxNode) !; + bundle.program, FUNCTION_BODY_FILE.name, 'qux', isNamedFunctionDeclaration)!; + const quxDef = host.getDefinitionOfFunction(quxNode)!; expect(quxDef.node).toBe(quxNode); - expect(quxDef.body !.length).toEqual(2); + expect(quxDef.body!.length).toEqual(2); expect(quxDef.parameters.length).toEqual(1); expect(quxDef.parameters[0].name).toEqual('x'); expect(quxDef.parameters[0].initializer).toBe(null); @@ -1762,7 +1774,7 @@ runInEachFileSystem(() => { it('should find the import of an identifier', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/file_b.js'), 'b', isNamedVariableDeclaration); const identifier = @@ -1772,14 +1784,14 @@ runInEachFileSystem(() => { null; expect(identifier).not.toBe(null); - const importOfIdent = host.getImportOfIdentifier(identifier !); + const importOfIdent = host.getImportOfIdentifier(identifier!); expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); }); it('should return null if the identifier was not imported', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/file_b.js'), 'd', isNamedVariableDeclaration); const importOfIdent = host.getImportOfIdentifier(variableNode.initializer as ts.Identifier); @@ -1790,7 +1802,7 @@ runInEachFileSystem(() => { it('should handle factory functions not wrapped in parentheses', () => { loadTestFiles(IMPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const variableNode = getDeclaration(bundle.program, _('/file_c.js'), 'c', isNamedVariableDeclaration); const identifier = @@ -1800,7 +1812,7 @@ runInEachFileSystem(() => { null; expect(identifier).not.toBe(null); - const importOfIdent = host.getImportOfIdentifier(identifier !); + const importOfIdent = host.getImportOfIdentifier(identifier!); expect(importOfIdent).toEqual({name: 'a', from: './file_a'}); }); }); @@ -1808,24 +1820,25 @@ runInEachFileSystem(() => { describe('getDeclarationOfIdentifier', () => { // Helpers const createTestForTsHelper = - (host: UmdReflectionHost, factoryFn: ts.FunctionExpression, + (host: NgccReflectionHost, factoryFn: ts.FunctionExpression, getHelperDeclaration: (factoryFn: ts.FunctionExpression, name: string) => ts.Declaration) => (varName: string, helperName: string, knownAs: KnownDeclaration, - viaModule: string | null = null) => { + viaModule: string|null = null) => { const node = getVariableDeclaration(factoryFn, varName); const helperIdentifier = getIdentifierFromCallExpression(node); const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); expect(helperDeclaration).toEqual({ known: knownAs, - node: getHelperDeclaration(factoryFn, helperName), viaModule, + node: getHelperDeclaration(factoryFn, helperName), + viaModule, }); }; const getFunctionDeclaration = (factoryFn: ts.FunctionExpression, name: string) => factoryFn.body.statements.filter(ts.isFunctionDeclaration) - .find(decl => (decl.name !== undefined) && (decl.name.text === name)) !; + .find(decl => (decl.name !== undefined) && (decl.name.text === name))!; const getIdentifierFromCallExpression = (decl: ts.VariableDeclaration) => { if (decl.initializer !== undefined && ts.isCallExpression(decl.initializer)) { @@ -1839,16 +1852,16 @@ runInEachFileSystem(() => { const getVariableDeclaration = (factoryFn: ts.FunctionExpression, name: string) => factoryFn.body.statements.filter(ts.isVariableStatement) .map(stmt => stmt.declarationList.declarations[0]) - .find(decl => ts.isIdentifier(decl.name) && (decl.name.text === name)) !; + .find(decl => ts.isIdentifier(decl.name) && (decl.name.text === name))!; it('should return the declaration of a locally defined identifier', () => { loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const ctrDecorators = host.getConstructorParameters(classNode) !; - const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference !as{ + const ctrDecorators = host.getConstructorParameters(classNode)!; + const identifierOfViewContainerRef = (ctrDecorators[0].typeValueReference! as { local: true, expression: ts.Identifier, defaultImportStatement: null, @@ -1859,8 +1872,8 @@ runInEachFileSystem(() => { isNamedVariableDeclaration); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfViewContainerRef); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe(null); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe(null); }); it('should return the correct declaration for an outer alias identifier', () => { @@ -1884,7 +1897,7 @@ runInEachFileSystem(() => { loadTestFiles([PROGRAM_FILE]); const bundle = makeTestBundleProgram(PROGRAM_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const expectedDeclaration = getDeclaration( bundle.program, PROGRAM_FILE.name, 'AliasedClass', isNamedVariableDeclaration); @@ -1895,28 +1908,29 @@ runInEachFileSystem(() => { expect(aliasIdentifier.getText()).toBe('AliasedClass_1'); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclaration); + expect(actualDeclaration!.node).toBe(expectedDeclaration); }); it('should return the source-file of an import namespace', () => { loadFakeCore(getFileSystem()); loadTestFiles([SOME_DIRECTIVE_FILE]); const bundle = makeTestBundleProgram(SOME_DIRECTIVE_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration( bundle.program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', isNamedVariableDeclaration); - const classDecorators = host.getDecoratorsOfDeclaration(classNode) !; - const identifierOfDirective = (((classDecorators[0].node as ts.ObjectLiteralExpression) - .properties[0] as ts.PropertyAssignment) - .initializer as ts.PropertyAccessExpression) - .expression as ts.Identifier; + const classDecorators = host.getDecoratorsOfDeclaration(classNode)!; + const identifierOfDirective = + (((classDecorators[0].node as ts.ObjectLiteralExpression).properties[0] as + ts.PropertyAssignment) + .initializer as ts.PropertyAccessExpression) + .expression as ts.Identifier; const expectedDeclarationNode = getSourceFileOrError(bundle.program, _('/node_modules/@angular/core/index.d.ts')); const actualDeclaration = host.getDeclarationOfIdentifier(identifierOfDirective); expect(actualDeclaration).not.toBe(null); - expect(actualDeclaration !.node).toBe(expectedDeclarationNode); - expect(actualDeclaration !.viaModule).toBe('@angular/core'); + expect(actualDeclaration!.node).toBe(expectedDeclarationNode); + expect(actualDeclaration!.viaModule).toBe('@angular/core'); }); it('should recognize TypeScript helpers (as function declarations)', () => { @@ -1940,9 +1954,9 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0]) !; + getSourceFileOrError(bundle.program, file.name).statements[0])!; const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); @@ -1972,9 +1986,9 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0]) !; + getSourceFileOrError(bundle.program, file.name).statements[0])!; const testForHelper = createTestForTsHelper(host, factoryFn, getFunctionDeclaration); @@ -2004,9 +2018,9 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0]) !; + getSourceFileOrError(bundle.program, file.name).statements[0])!; const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); @@ -2036,9 +2050,9 @@ runInEachFileSystem(() => { }; loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, file.name).statements[0]) !; + getSourceFileOrError(bundle.program, file.name).statements[0])!; const testForHelper = createTestForTsHelper(host, factoryFn, getVariableDeclaration); @@ -2076,9 +2090,9 @@ runInEachFileSystem(() => { const [testFile, tslibFile] = files; const bundle = makeTestBundleProgram(testFile.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const {factoryFn} = parseStatementForUmdModule( - getSourceFileOrError(bundle.program, testFile.name).statements[0]) !; + getSourceFileOrError(bundle.program, testFile.name).statements[0])!; const tslibSourceFile = getSourceFileOrError(bundle.program, tslibFile.name); const testForHelper = createTestForTsHelper(host, factoryFn, () => tslibSourceFile); @@ -2087,6 +2101,84 @@ runInEachFileSystem(() => { testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread, 'tslib'); testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays, 'tslib'); }); + + it('should recognize undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : + (factory(global.test)); + }(this, (function (exports) { 'use strict'; + var a = __assign({foo: 'bar'}, {baz: 'qux'}); + var b = __spread(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays(['foo', 'bar'], ['baz', 'qux']); + }))); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; + + const testForHelper = (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = getVariableDeclaration(factoryFn, varName); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + known: knownAs, + expression: helperIdentifier, + node: null, + viaModule: null, + }); + }; + + testForHelper('a', '__assign', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays', KnownDeclaration.TsHelperSpreadArrays); + }); + + it('should recognize suffixed, undeclared, unimported TypeScript helpers (by name)', () => { + const file: TestFile = { + name: _('/test.js'), + contents: ` + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define('test', ['exports'], factory) : + (factory(global.test)); + }(this, (function (exports) { 'use strict'; + var a = __assign$1({foo: 'bar'}, {baz: 'qux'}); + var b = __spread$2(['foo', 'bar'], ['baz', 'qux']); + var c = __spreadArrays$3(['foo', 'bar'], ['baz', 'qux']); + }))); + `, + }; + loadTestFiles([file]); + const bundle = makeTestBundleProgram(file.name); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); + const {factoryFn} = parseStatementForUmdModule( + getSourceFileOrError(bundle.program, file.name).statements[0])!; + + const testForHelper = (varName: string, helperName: string, knownAs: KnownDeclaration) => { + const node = getVariableDeclaration(factoryFn, varName); + const helperIdentifier = getIdentifierFromCallExpression(node); + const helperDeclaration = host.getDeclarationOfIdentifier(helperIdentifier); + + expect(helperDeclaration).toEqual({ + known: knownAs, + expression: helperIdentifier, + node: null, + viaModule: null, + }); + }; + + testForHelper('a', '__assign$1', KnownDeclaration.TsHelperAssign); + testForHelper('b', '__spread$2', KnownDeclaration.TsHelperSpread); + testForHelper('c', '__spreadArrays$3', KnownDeclaration.TsHelperSpreadArrays); + }); }); describe('getExportsOfModule()', () => { @@ -2094,12 +2186,12 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/b_module.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, '@angular/core'], ['a', `a = 'a'`, '/a_module'], @@ -2122,12 +2214,12 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], ['a', `a = 'a'`, _('/b_module')], @@ -2150,13 +2242,13 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], ['a', `a = 'a'`, _('/b_module')], @@ -2179,11 +2271,11 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles([INLINE_EXPORT_FILE]); const bundle = makeTestBundleProgram(INLINE_EXPORT_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, INLINE_EXPORT_FILE.name); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - const decl = exportDeclarations !.get('directives') as InlineDeclaration; + const decl = exportDeclarations!.get('directives') as InlineDeclaration; expect(decl).not.toBeUndefined(); expect(decl.node).toBeNull(); expect(decl.expression).toBeDefined(); @@ -2201,9 +2293,9 @@ runInEachFileSystem(() => { }; loadTestFiles([tslib]); const bundle = makeTestBundleProgram(tslib.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const sf = getSourceFileOrError(bundle.program, tslib.name); - const exportDeclarations = host.getExportsOfModule(sf) !; + const exportDeclarations = host.getExportsOfModule(sf)!; expect([...exportDeclarations].map(([exportName, {known}]) => [exportName, known])) .toEqual([ @@ -2219,26 +2311,26 @@ runInEachFileSystem(() => { it('should return the class symbol for an ES2015 class', () => { loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); const classSymbol = host.getClassSymbol(node); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(node); - expect(classSymbol !.implementation.valueDeclaration).toBe(node); + expect(classSymbol!.declaration.valueDeclaration).toBe(node); + expect(classSymbol!.implementation.valueDeclaration).toBe(node); }); it('should handle wildcard re-exports of other modules (with emitted helpers)', () => { loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], ['a', `a = 'a'`, _('/b_module')], @@ -2261,13 +2353,13 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports_imported_helpers.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], ['a', `a = 'a'`, _('/b_module')], @@ -2290,12 +2382,12 @@ runInEachFileSystem(() => { loadFakeCore(getFileSystem()); loadTestFiles(EXPORTS_FILES); const bundle = makeTestBundleProgram(_('/index.js')); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/wildcard_reexports_with_require.js')); const exportDeclarations = host.getExportsOfModule(file); expect(exportDeclarations).not.toBe(null); - expect(Array.from(exportDeclarations !.entries()) - .map(entry => [entry[0], entry[1].node !.getText(), entry[1].viaModule])) + expect(Array.from(exportDeclarations!.entries()) + .map(entry => [entry[0], entry[1].node!.getText(), entry[1].viaModule])) .toEqual([ ['Directive', `Directive: FnWithArg<(clazz: any) => any>`, _('/b_module')], ['a', `a = 'a'`, _('/b_module')], @@ -2317,42 +2409,42 @@ runInEachFileSystem(() => { it('should return the class symbol for an ES5 class (outer variable declaration)', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the class symbol for an ES5 class (inner function declaration)', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(innerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the same class symbol (of the outer declaration) for outer and inner declarations', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; - const innerSymbol = host.getClassSymbol(innerNode) !; - const outerSymbol = host.getClassSymbol(outerNode) !; + const innerSymbol = host.getClassSymbol(innerNode)!; + const outerSymbol = host.getClassSymbol(outerNode)!; expect(innerSymbol.declaration).toBe(outerSymbol.declaration); expect(innerSymbol.implementation).toBe(outerSymbol.implementation); }); @@ -2361,37 +2453,37 @@ runInEachFileSystem(() => { () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return the class symbol for an ES5 class whose IIFE is not wrapped with inner parens', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass', isNamedVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; const classSymbol = host.getClassSymbol(outerNode); expect(classSymbol).toBeDefined(); - expect(classSymbol !.declaration.valueDeclaration).toBe(outerNode); - expect(classSymbol !.implementation.valueDeclaration).toBe(innerNode); + expect(classSymbol!.declaration.valueDeclaration).toBe(outerNode); + expect(classSymbol!.implementation.valueDeclaration).toBe(innerNode); }); it('should return undefined if node is not an ES5 class', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); const classSymbol = host.getClassSymbol(node); @@ -2406,7 +2498,7 @@ runInEachFileSystem(() => { }; loadTestFiles([testFile]); const bundle = makeTestBundleProgram(testFile.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration(bundle.program, testFile.name, 'MyClass', isNamedVariableDeclaration); const classSymbol = host.getClassSymbol(node); @@ -2419,7 +2511,7 @@ runInEachFileSystem(() => { it('should return true if a given node is a TS class declaration', () => { loadTestFiles([SIMPLE_ES2015_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_ES2015_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_ES2015_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); expect(host.isClass(node)).toBe(true); @@ -2428,7 +2520,7 @@ runInEachFileSystem(() => { it('should return true if a given node is the outer variable declaration of a class', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); expect(host.isClass(node)).toBe(true); @@ -2437,17 +2529,17 @@ runInEachFileSystem(() => { it('should return true if a given node is the inner variable declaration of a class', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const outerNode = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); - const innerNode = getIifeBody(outerNode) !.statements.find(isNamedFunctionDeclaration) !; + const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!; expect(host.isClass(innerNode)).toBe(true); }); it('should return false if a given node is a function declaration', () => { loadTestFiles([FOO_FUNCTION_FILE]); const bundle = makeTestBundleProgram(FOO_FUNCTION_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const node = getDeclaration( bundle.program, FOO_FUNCTION_FILE.name, 'foo', isNamedFunctionDeclaration); expect(host.isClass(node)).toBe(false); @@ -2463,7 +2555,7 @@ runInEachFileSystem(() => { loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); return host.hasBaseClass(classNode); @@ -2510,7 +2602,7 @@ runInEachFileSystem(() => { loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); const expression = host.getBaseClassExpression(classNode); @@ -2532,7 +2624,7 @@ runInEachFileSystem(() => { function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier !.text).toBe('BaseClass'); + expect(identifier!.text).toBe('BaseClass'); }); it('should find the base class of an IIFE with a unique name generated for the _super parameter', @@ -2547,7 +2639,7 @@ runInEachFileSystem(() => { function TestClass() {} return TestClass; }(BaseClass));`); - expect(identifier !.text).toBe('BaseClass'); + expect(identifier!.text).toBe('BaseClass'); }); it('should not find a base class for an IIFE without parameter', () => { @@ -2582,10 +2674,10 @@ runInEachFileSystem(() => { loadTestFiles([file]); const bundle = makeTestBundleProgram(file.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const classNode = getDeclaration(bundle.program, file.name, 'TestClass', isNamedVariableDeclaration); - const expression = host.getBaseClassExpression(classNode) !; + const expression = host.getBaseClassExpression(classNode)!; expect(expression.getText()).toBe('foo()'); }); }); @@ -2594,7 +2686,7 @@ runInEachFileSystem(() => { it('should return an array of all classes in the given source file', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); @@ -2612,21 +2704,21 @@ runInEachFileSystem(() => { it('should return decorators of class symbol', () => { loadTestFiles(DECORATED_FILES); const bundle = makeTestBundleProgram(DECORATED_FILES[0].name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const primaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[0].name); const secondaryFile = getSourceFileOrError(bundle.program, DECORATED_FILES[1].name); const classSymbolsPrimary = host.findClassSymbols(primaryFile); const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s)); expect(classDecoratorsPrimary.length).toEqual(2); - expect(classDecoratorsPrimary[0] !.map(d => d.name)).toEqual(['Directive']); - expect(classDecoratorsPrimary[1] !.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[0]!.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsPrimary[1]!.map(d => d.name)).toEqual(['Directive']); const classSymbolsSecondary = host.findClassSymbols(secondaryFile); const classDecoratorsSecondary = classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s)); expect(classDecoratorsSecondary.length).toEqual(1); - expect(classDecoratorsSecondary[0] !.map(d => d.name)).toEqual(['Directive']); + expect(classDecoratorsSecondary[0]!.map(d => d.name)).toEqual(['Directive']); }); }); @@ -2639,10 +2731,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/class1.js'), 'Class1', ts.isVariableDeclaration); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find the dts declaration for exported functions', () => { @@ -2652,10 +2745,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(_('/ep/typings/func1.d.ts')); const mooFn = getDeclaration( bundle.program, _('/ep/src/func1.js'), 'mooFn', ts.isFunctionDeclaration); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(mooFn); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/func1.d.ts')); }); it('should return null if there is no matching class in the matching dts file', () => { @@ -2665,7 +2759,8 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const missingClass = getDeclaration( bundle.program, _('/ep/src/class1.js'), 'MissingClass1', ts.isVariableDeclaration); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBe(null); }); @@ -2677,7 +2772,8 @@ runInEachFileSystem(() => { const missingClass = getDeclaration( bundle.program, _('/ep/src/missing-class.js'), 'MissingClass2', ts.isVariableDeclaration); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); expect(host.getDtsDeclaration(missingClass)).toBe(null); }); @@ -2690,10 +2786,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find the dts file that contains a matching class declaration, even if the source files do not match', @@ -2704,10 +2801,11 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const class1 = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'Class1', ts.isVariableDeclaration); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(class1); - expect(dtsDeclaration !.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); + expect(dtsDeclaration!.getSourceFile().fileName).toEqual(_('/ep/typings/class1.d.ts')); }); it('should find aliased exports', () => { @@ -2717,7 +2815,8 @@ runInEachFileSystem(() => { const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); const sourceClass = getDeclaration( bundle.program, _('/ep/src/flat-file.js'), 'SourceClass', ts.isVariableDeclaration); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); const dtsDeclaration = host.getDtsDeclaration(sourceClass); if (dtsDeclaration === null) { @@ -2737,18 +2836,19 @@ runInEachFileSystem(() => { loadTestFiles(TYPINGS_DTS_FILES); const bundle = makeTestBundleProgram(getRootFiles(TYPINGS_SRC_FILES)[0]); const dts = makeTestBundleProgram(getRootFiles(TYPINGS_DTS_FILES)[0]); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); const class2 = getDeclaration( bundle.program, _('/ep/src/class2.js'), 'Class2', isNamedVariableDeclaration); const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration !.getSourceFile().fileName) + expect(class2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/class2.d.ts')); const internalClass2 = getDeclaration( bundle.program, _('/ep/src/internal.js'), 'Class2', isNamedVariableDeclaration); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration !.getSourceFile().fileName) + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/internal.d.ts')); }); @@ -2762,14 +2862,15 @@ runInEachFileSystem(() => { bundle.program, _('/ep/src/class2.js'), 'Class2', ts.isVariableDeclaration); const internalClass2 = getDeclaration( bundle.program, _('/ep/src/internal.js'), 'Class2', ts.isVariableDeclaration); - const host = new UmdReflectionHost(new MockLogger(), false, bundle, dts); + const host = + createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle, dts)); const class2DtsDeclaration = host.getDtsDeclaration(class2); - expect(class2DtsDeclaration !.getSourceFile().fileName) + expect(class2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/class2.d.ts')); const internalClass2DtsDeclaration = host.getDtsDeclaration(internalClass2); - expect(internalClass2DtsDeclaration !.getSourceFile().fileName) + expect(internalClass2DtsDeclaration!.getSourceFile().fileName) .toEqual(_('/ep/typings/internal.d.ts')); }); }); @@ -2778,7 +2879,7 @@ runInEachFileSystem(() => { it('should return the name of the inner class declaration', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const emptyClass = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); @@ -2802,7 +2903,7 @@ runInEachFileSystem(() => { it('should return the name of the inner class declaration', () => { loadTestFiles([SIMPLE_CLASS_FILE]); const bundle = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const emptyClass = getDeclaration( bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); @@ -2827,10 +2928,10 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(getRootFiles(MODULE_WITH_PROVIDERS_PROGRAM)[0]); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/functions.js')); const fns = host.getModuleWithProvidersFunctions(file); - expect(fns.map(fn => [fn.declaration.name !.getText(), fn.ngModule.node.name.text])) + expect(fns.map(fn => [fn.declaration.name!.getText(), fn.ngModule.node.name.text])) .toEqual([ ['ngModuleIdentifier', 'InternalModule'], ['ngModuleWithEmptyProviders', 'InternalModule'], @@ -2843,7 +2944,7 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(getRootFiles(MODULE_WITH_PROVIDERS_PROGRAM)[0]); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/methods.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ @@ -2870,7 +2971,7 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(getRootFiles(MODULE_WITH_PROVIDERS_PROGRAM)[0]); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/outer_aliased_class.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ @@ -2883,7 +2984,7 @@ runInEachFileSystem(() => { () => { loadTestFiles(MODULE_WITH_PROVIDERS_PROGRAM); const bundle = makeTestBundleProgram(getRootFiles(MODULE_WITH_PROVIDERS_PROGRAM)[0]); - const host = new UmdReflectionHost(new MockLogger(), false, bundle); + const host = createHost(bundle, new UmdReflectionHost(new MockLogger(), false, bundle)); const file = getSourceFileOrError(bundle.program, _('/src/inner_aliased_class.js')); const fn = host.getModuleWithProvidersFunctions(file); expect(fn.map(fn => [fn.declaration.getText(), fn.ngModule.node.name.text])).toEqual([ diff --git a/packages/compiler-cli/ngcc/test/host/util.ts b/packages/compiler-cli/ngcc/test/host/util.ts index 7e6a9de5ee1c0..f461a85a040b4 100644 --- a/packages/compiler-cli/ngcc/test/host/util.ts +++ b/packages/compiler-cli/ngcc/test/host/util.ts @@ -14,9 +14,8 @@ import {CtorParameter} from '../../../src/ngtsc/reflection'; * names. */ export function expectTypeValueReferencesForParameters( - parameters: CtorParameter[], expectedParams: (string | null)[], - fromModule: string | null = null) { - parameters !.forEach((param, idx) => { + parameters: CtorParameter[], expectedParams: (string|null)[], fromModule: string|null = null) { + parameters!.forEach((param, idx) => { const expected = expectedParams[idx]; if (expected !== null) { if (param.typeValueReference === null) { @@ -32,8 +31,8 @@ export function expectTypeValueReferencesForParameters( expect(param.typeValueReference.expression.text).toEqual(expected); } } else if (param.typeValueReference !== null) { - expect(param.typeValueReference.moduleName).toBe(fromModule !); - expect(param.typeValueReference.name).toBe(expected); + expect(param.typeValueReference.moduleName).toBe(fromModule!); + expect(param.typeValueReference.importedName).toBe(expected); } } }); diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index 888c854415b25..a4c6ae2d31e3e 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -8,8 +8,9 @@ /// <reference types="node" /> import * as os from 'os'; -import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, join} from '../../../src/ngtsc/file_system'; -import {Folder, MockFileSystem, TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; + +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, join} from '../../../src/ngtsc/file_system'; +import {Folder, MockFileSystem, runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {loadStandardTestFiles, loadTestFiles} from '../../../test/helpers'; import {getLockFilePath} from '../../src/locking/lock_file'; import {mainNgcc} from '../../src/main'; @@ -19,6 +20,7 @@ import {EntryPointManifestFile} from '../../src/packages/entry_point_manifest'; import {Transformer} from '../../src/packages/transformer'; import {DirectPackageJsonUpdater, PackageJsonUpdater} from '../../src/writing/package_json_updater'; import {MockLogger} from '../helpers/mock_logger'; + import {compileIntoApf, compileIntoFlatEs5Package} from './util'; const testFiles = loadStandardTestFiles({fakeCore: false, rxjs: true}); @@ -36,7 +38,7 @@ runInEachFileSystem(() => { initMockFileSystem(fs, testFiles); // Force single-process execution in unit tests by mocking available CPUs to 1. - spyOn(os, 'cpus').and.returnValue([{model: 'Mock CPU'}]); + spyOn(os, 'cpus').and.returnValue([{model: 'Mock CPU'} as any]); }); it('should run ngcc without errors for esm2015', () => { @@ -145,7 +147,8 @@ runInEachFileSystem(() => { }); ['esm5', 'esm2015'].forEach(target => { - it(`should be able to process spread operator inside objects for ${target} format (imported helpers)`, + it(`should be able to process spread operator inside objects for ${ + target} format (imported helpers)`, () => { compileIntoApf( 'test-package', { @@ -184,7 +187,8 @@ runInEachFileSystem(() => { expect(jsContents).toContain('ngcc0.ɵɵclassProp("a", true)("b", true)("c", false)'); }); - it(`should be able to process emitted spread operator inside objects for ${target} format (emitted helpers)`, + it(`should be able to process emitted spread operator inside objects for ${ + target} format (emitted helpers)`, () => { compileIntoApf( 'test-package', { @@ -326,7 +330,7 @@ runInEachFileSystem(() => { }); const before = fs.readFile(_(`/node_modules/test-package/index.js`)); - const originalProp = /ɵprov[^;]+/.exec(before) ![0]; + const originalProp = /ɵprov[^;]+/.exec(before)![0]; mainNgcc({ basePath: '/node_modules', targetEntryPointPath: 'test-package', @@ -399,9 +403,22 @@ runInEachFileSystem(() => { expect(dtsContents) .toContain(`export declare class ${exportedName} extends PlatformLocation`); // And that ngcc's modifications to that class use the correct (exported) name - expect(dtsContents).toContain(`static ɵfac: ɵngcc0.ɵɵFactoryDef<${exportedName}>`); + expect(dtsContents).toContain(`static ɵfac: ɵngcc0.ɵɵFactoryDef<${exportedName}, never>`); }); + it('should include constructor metadata in factory definitions', () => { + mainNgcc({ + basePath: '/node_modules', + targetEntryPointPath: '@angular/common', + propertiesToConsider: ['esm2015'] + }); + + const dtsContents = fs.readFile(_('/node_modules/@angular/common/common.d.ts')); + expect(dtsContents) + .toContain( + `static ɵfac: ɵngcc0.ɵɵFactoryDef<NgPluralCase, [{ attribute: "ngPluralCase"; }, null, null, { host: true; }]>`); + }); + it('should add generic type for ModuleWithProviders and generate exports for private modules', () => { compileIntoApf('test-package', { @@ -483,7 +500,7 @@ runInEachFileSystem(() => { }); describe('in async mode', () => { - it('should run ngcc without errors for fesm2015', async() => { + it('should run ngcc without errors for fesm2015', async () => { const promise = mainNgcc({ basePath: '/node_modules', propertiesToConsider: ['fesm2015'], @@ -494,7 +511,7 @@ runInEachFileSystem(() => { await promise; }); - it('should reject, if some of the entry-points are unprocessable', async() => { + it('should reject, if some of the entry-points are unprocessable', async () => { const createEntryPoint = (name: string, prop: EntryPointJsonProperty): TestFile[] => { return [ { @@ -528,7 +545,7 @@ runInEachFileSystem(() => { ` - ${_('/dist/unprocessable-3')}`))); }); - it('should reject, if an error happens during processing', async() => { + it('should reject, if an error happens during processing', async () => { spyOn(Transformer.prototype, 'transform').and.throwError('Test error.'); const promise = mainNgcc({ @@ -638,7 +655,8 @@ runInEachFileSystem(() => { markPropertiesAsProcessed('@angular/common/http/testing', SUPPORTED_FORMAT_PROPERTIES); mainNgcc({ basePath: '/node_modules', - targetEntryPointPath: '@angular/common/http/testing', logger, + targetEntryPointPath: '@angular/common/http/testing', + logger, }); expect(logger.logs.debug).toContain([ 'The target entry-point has already been processed' @@ -652,7 +670,8 @@ runInEachFileSystem(() => { mainNgcc({ basePath: '/node_modules', targetEntryPointPath: '@angular/common/http/testing', - propertiesToConsider: ['fesm2015', 'esm5', 'esm2015'], logger, + propertiesToConsider: ['fesm2015', 'esm5', 'esm2015'], + logger, }); expect(logger.logs.debug).not.toContain([ 'The target entry-point has already been processed' @@ -669,7 +688,8 @@ runInEachFileSystem(() => { basePath: '/node_modules', targetEntryPointPath: '@angular/common/http/testing', propertiesToConsider: ['esm5', 'esm2015'], - compileAllFormats: false, logger, + compileAllFormats: false, + logger, }); expect(logger.logs.debug).not.toContain([ @@ -686,7 +706,8 @@ runInEachFileSystem(() => { targetEntryPointPath: '@angular/common/http/testing', // Simulate a property that does not exist on the package.json and will be ignored. propertiesToConsider: ['missing', 'esm2015', 'esm5'], - compileAllFormats: false, logger, + compileAllFormats: false, + logger, }); expect(logger.logs.debug).toContain([ @@ -704,7 +725,8 @@ runInEachFileSystem(() => { targetEntryPointPath: '@angular/common/http/testing', // Simulate a property that does not exist on the package.json and will be ignored. propertiesToConsider: ['missing', 'esm2015', 'esm5'], - compileAllFormats: false, logger, + compileAllFormats: false, + logger, }); expect(logger.logs.debug).toContain([ @@ -739,7 +761,7 @@ runInEachFileSystem(() => { // Now hack the files to look like it was processed by an outdated version of ngcc const packageJson = loadPackage('test-package', _('/node_modules')); - packageJson.__processed_by_ivy_ngcc__ !.typings = '8.0.0'; + packageJson.__processed_by_ivy_ngcc__!.typings = '8.0.0'; packageJson.main_ivy_ngcc = '__ivy_ngcc__/main.js'; fs.writeFile(_('/node_modules/test-package/package.json'), JSON.stringify(packageJson)); fs.writeFile(_('/node_modules/test-package/x.js'), 'processed content'); @@ -771,7 +793,7 @@ runInEachFileSystem(() => { const propertiesToConsider = ['es1337', 'fesm42']; const errorMessage = 'No supported format property to consider among [es1337, fesm42]. Supported ' + - 'properties: fesm2015, fesm5, es2015, esm2015, esm5, main, module'; + 'properties: fesm2015, fesm5, es2015, esm2015, esm5, main, module, browser'; expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider})) .toThrowError(errorMessage); @@ -825,7 +847,8 @@ runInEachFileSystem(() => { // `fesm2015` and `es2015` map to the same file: `./fesm2015/common.js` mainNgcc({ basePath: '/node_modules/@angular/common', - propertiesToConsider: ['fesm2015'], logger, + propertiesToConsider: ['fesm2015'], + logger, }); expect(logs).not.toContain(['Skipping @angular/common : es2015 (already compiled).']); @@ -838,7 +861,8 @@ runInEachFileSystem(() => { // Now, compiling `es2015` should be a no-op. mainNgcc({ basePath: '/node_modules/@angular/common', - propertiesToConsider: ['es2015'], logger, + propertiesToConsider: ['es2015'], + logger, }); expect(logs).toContain(['Skipping @angular/common : es2015 (already compiled).']); @@ -938,7 +962,8 @@ runInEachFileSystem(() => { .toMatch(ANGULAR_CORE_IMPORT_REGEX); // Copies over files (unchanged) that did not need compiling - expect(fs.exists(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`))); + expect(fs.exists(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`))) + .toBeTrue(); expect(fs.readFile(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`))) .toEqual(fs.readFile(_(`/node_modules/@angular/common/esm5/src/version.js`))); @@ -984,21 +1009,21 @@ runInEachFileSystem(() => { expectNotToHaveProp(pkg, 'esm5_ivy_ngcc'); expectToHaveProp(pkg, 'fesm2015_ivy_ngcc'); expectNotToHaveProp(pkg, 'fesm5_ivy_ngcc'); - expectToHaveProp(pkg.__processed_by_ivy_ngcc__ !, 'fesm2015'); + expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'fesm2015'); // Process `fesm5` and update `package.json`. pkg = processFormatAndUpdatePackageJson('fesm5'); expectNotToHaveProp(pkg, 'esm5_ivy_ngcc'); expectToHaveProp(pkg, 'fesm2015_ivy_ngcc'); expectToHaveProp(pkg, 'fesm5_ivy_ngcc'); - expectToHaveProp(pkg.__processed_by_ivy_ngcc__ !, 'fesm5'); + expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'fesm5'); // Process `esm5` and update `package.json`. pkg = processFormatAndUpdatePackageJson('esm5'); expectToHaveProp(pkg, 'esm5_ivy_ngcc'); expectToHaveProp(pkg, 'fesm2015_ivy_ngcc'); expectToHaveProp(pkg, 'fesm5_ivy_ngcc'); - expectToHaveProp(pkg.__processed_by_ivy_ngcc__ !, 'esm5'); + expectToHaveProp(pkg.__processed_by_ivy_ngcc__!, 'esm5'); // Ensure the properties are in deterministic order (regardless of processing order). const pkgKeys = stringifyKeys(pkg); @@ -1013,7 +1038,7 @@ runInEachFileSystem(() => { // For example: // - `fesm2015` <=> `es2015` // - `fesm5` <=> `module` - expect(stringifyKeys(pkg.__processed_by_ivy_ngcc__ !)) + expect(stringifyKeys(pkg.__processed_by_ivy_ngcc__!)) .toBe('|es2015|esm5|fesm2015|fesm5|module|typings|'); // Helpers @@ -1021,7 +1046,8 @@ runInEachFileSystem(() => { expect(obj.hasOwnProperty(prop)) .toBe( false, - `Expected object not to have property '${prop}': ${JSON.stringify(obj, null, 2)}`); + `Expected object not to have property '${prop}': ${ + JSON.stringify(obj, null, 2)}`); } function expectToHaveProp(obj: object, prop: string) { @@ -1040,7 +1066,9 @@ runInEachFileSystem(() => { return loadPackage('@angular/core'); } - function stringifyKeys(obj: object) { return `|${Object.keys(obj).join('|')}|`; } + function stringifyKeys(obj: object) { + return `|${Object.keys(obj).join('|')}|`; + } }); }); @@ -1051,7 +1079,7 @@ runInEachFileSystem(() => { // Populate the manifest file mainNgcc( {basePath: '/node_modules', propertiesToConsider: ['esm5'], logger: new MockLogger()}); - // Check that common/testings ES5 was processed + // Check that common/testing ES5 was processed let commonTesting = JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json'))); expect(hasBeenProcessed(commonTesting, 'esm5')).toBe(true); @@ -1059,8 +1087,8 @@ runInEachFileSystem(() => { // Modify the manifest to test that is has no effect let manifest: EntryPointManifestFile = JSON.parse(fs.readFile(_('/node_modules/__ngcc_entry_points__.json'))); - manifest.entryPointPaths = manifest.entryPointPaths.filter( - paths => paths[1] !== _('/node_modules/@angular/common/testing')); + manifest.entryPointPaths = + manifest.entryPointPaths.filter(paths => paths[1] !== '@angular/common/testing'); fs.writeFile(_('/node_modules/__ngcc_entry_points__.json'), JSON.stringify(manifest)); // Now run ngcc again ignoring this manifest but trying to process ES2015, which are not yet // processed. @@ -1079,7 +1107,12 @@ runInEachFileSystem(() => { // had removed earlier. manifest = JSON.parse(fs.readFile(_('/node_modules/__ngcc_entry_points__.json'))); expect(manifest.entryPointPaths).toContain([ - _('/node_modules/@angular/common'), _('/node_modules/@angular/common/testing') + '@angular/common', + '@angular/common/testing', + [ + _('/node_modules/@angular/core'), _('/node_modules/@angular/common'), + _('/node_modules/rxjs') + ], ]); }); }); @@ -1193,7 +1226,8 @@ runInEachFileSystem(() => { mainNgcc({ basePath: '/node_modules', propertiesToConsider: ['es2015'], - errorOnFailedEntryPoint: false, logger, + errorOnFailedEntryPoint: false, + logger, }); expect(logger.logs.error.length).toEqual(1); const message = logger.logs.error[0][0]; @@ -1223,28 +1257,182 @@ runInEachFileSystem(() => { const logger = new MockLogger(); mainNgcc({ basePath: '/node_modules', - propertiesToConsider: ['esm2015'], logger, + propertiesToConsider: ['esm2015'], + logger, }); expect(logger.logs.info).toContain(['Compiling @angular/common/http : esm2015 as esm2015']); }); }); describe('with pathMappings', () => { - it('should find and compile packages accessible via the pathMappings', () => { + it('should infer the @app pathMapping from a local tsconfig.json path', () => { + fs.writeFile( + _('/tsconfig.json'), + JSON.stringify({compilerOptions: {paths: {'@app/*': ['dist/*']}, baseUrl: './'}})); + const logger = new MockLogger(); + mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015'], logger}); + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + expect(loadPackage('local-package-2', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + // The local-package-3 and local-package-4 will not be processed because there is no path + // mappings for `@x` and plain local imports. + expect(loadPackage('local-package-3', _('/dist')).__processed_by_ivy_ngcc__) + .toBeUndefined(); + expect(logger.logs.debug).toContain([ + `Invalid entry-point ${_('/dist/local-package-3')}.`, + 'It is missing required dependencies:\n - @x/local-package' + ]); + expect(loadPackage('local-package-4', _('/dist')).__processed_by_ivy_ngcc__) + .toBeUndefined(); + expect(logger.logs.debug).toContain([ + `Invalid entry-point ${_('/dist/local-package-4')}.`, + 'It is missing required dependencies:\n - local-package' + ]); + }); + + it('should read the @x pathMapping from a specified tsconfig.json path', () => { + fs.writeFile( + _('/tsconfig.app.json'), + JSON.stringify({compilerOptions: {paths: {'@x/*': ['dist/*']}, baseUrl: './'}})); + const logger = new MockLogger(); mainNgcc({ - basePath: '/node_modules', + basePath: '/dist', propertiesToConsider: ['es2015'], - pathMappings: {paths: {'*': ['dist/*']}, baseUrl: '/'}, + tsConfigPath: _('/tsconfig.app.json'), + logger }); - expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + expect(loadPackage('local-package-3', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + // The local-package-2 and local-package-4 will not be processed because there is no path + // mappings for `@app` and plain local imports. + expect(loadPackage('local-package-2', _('/dist')).__processed_by_ivy_ngcc__) + .toBeUndefined(); + expect(logger.logs.debug).toContain([ + `Invalid entry-point ${_('/dist/local-package-2')}.`, + 'It is missing required dependencies:\n - @app/local-package' + ]); + expect(loadPackage('local-package-4', _('/dist')).__processed_by_ivy_ngcc__) + .toBeUndefined(); + expect(logger.logs.debug).toContain([ + `Invalid entry-point ${_('/dist/local-package-4')}.`, + 'It is missing required dependencies:\n - local-package' + ]); + }); + + it('should use the explicit `pathMappings`, ignoring the local tsconfig.json settings', + () => { + const logger = new MockLogger(); + fs.writeFile( + _('/tsconfig.json'), + JSON.stringify({compilerOptions: {paths: {'@app/*': ['dist/*']}, baseUrl: './'}})); + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['es2015'], + pathMappings: {paths: {'*': ['dist/*']}, baseUrl: '/'}, + logger + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + fesm2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + expect(loadPackage('local-package-4', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + // The local-package-2 and local-package-3 will not be processed because there is no path + // mappings for `@app` and `@x` local imports. + expect(loadPackage('local-package-2', _('/dist')).__processed_by_ivy_ngcc__) + .toBeUndefined(); + expect(logger.logs.debug).toContain([ + `Invalid entry-point ${_('/dist/local-package-2')}.`, + 'It is missing required dependencies:\n - @app/local-package' + ]); + expect(loadPackage('local-package-3', _('/dist')).__processed_by_ivy_ngcc__) + .toBeUndefined(); + expect(logger.logs.debug).toContain([ + `Invalid entry-point ${_('/dist/local-package-3')}.`, + 'It is missing required dependencies:\n - @x/local-package' + ]); + }); + + it('should not use pathMappings from a local tsconfig.json path if tsConfigPath is null', + () => { + const logger = new MockLogger(); + fs.writeFile( + _('/tsconfig.json'), + JSON.stringify({compilerOptions: {paths: {'@app/*': ['dist/*']}, baseUrl: './'}})); + mainNgcc({ + basePath: '/dist', + propertiesToConsider: ['es2015'], + tsConfigPath: null, + logger, + }); + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + // Since the tsconfig is not loaded, the `@app/local-package` import in `local-package-2` + // is not path-mapped correctly, and so it fails to be processed. + expect(loadPackage('local-package-2', _('/dist')).__processed_by_ivy_ngcc__) + .toBeUndefined(); + expect(logger.logs.debug).toContain([ + `Invalid entry-point ${_('/dist/local-package-2')}.`, + 'It is missing required dependencies:\n - @app/local-package' + ]); + }); + }); + + describe('whitespace preservation', () => { + it('should default not to preserve whitespace', () => { + mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']}); + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_('/dist/local-package/index.js'))) + .toMatch(/ɵɵtext\(\d+, " Hello\\n"\);/); + }); + + it('should preserve whitespace if set in a loaded tsconfig.json', () => { + fs.writeFile( + _('/tsconfig.json'), + JSON.stringify({angularCompilerOptions: {preserveWhitespaces: true}})); + mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']}); + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ es2015: '0.0.0-PLACEHOLDER', - fesm2015: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER', }); + expect(fs.readFile(_('/dist/local-package/index.js'))) + .toMatch(/ɵɵtext\(\d+, "\\n Hello\\n"\);/); + }); + + it('should not preserve whitespace if set to false in a loaded tsconfig.json', () => { + fs.writeFile( + _('/tsconfig.json'), + JSON.stringify({angularCompilerOptions: {preserveWhitespaces: false}})); + mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']}); expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ es2015: '0.0.0-PLACEHOLDER', typings: '0.0.0-PLACEHOLDER', }); + expect(fs.readFile(_('/dist/local-package/index.js'))) + .toMatch(/ɵɵtext\(\d+, " Hello\\n"\);/); }); }); @@ -1477,7 +1665,7 @@ runInEachFileSystem(() => { const dtsContents = fs.readFile(_(`/node_modules/test-package/index.d.ts`)); expect(dtsContents) .toContain( - 'static ɵcmp: ɵngcc0.ɵɵComponentDefWithMeta<DerivedCmp, "[base]", never, {}, {}, never>;'); + 'static ɵcmp: ɵngcc0.ɵɵComponentDefWithMeta<DerivedCmp, "[base]", never, {}, {}, never, never>;'); }); it('should generate directive definitions with CopyDefinitionFeature for undecorated child directives in a long inheritance chain', @@ -1658,7 +1846,7 @@ runInEachFileSystem(() => { expect(jsContents).toContain('exports.ɵngExportɵFooModuleɵFoo = ɵngcc1.Foo;'); expect(dtsContents) .toContain(`export {Foo as ɵngExportɵFooModuleɵFoo} from './directive';`); - expect(dtsContents.match(/ɵngExportɵFooModuleɵFoo/g) !.length).toBe(1); + expect(dtsContents.match(/ɵngExportɵFooModuleɵFoo/g)!.length).toBe(1); expect(dtsContents).not.toContain(`ɵngExportɵFooModuleɵLocalDir`); }); }); @@ -1748,7 +1936,7 @@ runInEachFileSystem(() => { }, ]); - // An Angular package that has been built locally and stored in the `dist` directory. + // Angular packages that have been built locally and stored in the `dist` directory. loadTestFiles([ { name: _('/dist/local-package/package.json'), @@ -1758,12 +1946,60 @@ runInEachFileSystem(() => { { name: _('/dist/local-package/index.js'), contents: - `import {Component} from '@angular/core';\nexport class AppComponent {};\nAppComponent.decorators = [\n{ type: Component, args: [{selector: 'app', template: '<h2>Hello</h2>'}] }\n];` + `import {Component} from '@angular/core';\nexport class AppComponent {};\nAppComponent.decorators = [\n{ type: Component, args: [{selector: 'app', template: '<h2>\\n Hello\\n</h2>'}] }\n];` }, { name: _('/dist/local-package/index.d.ts'), contents: `export declare class AppComponent {};` }, + // local-package-2 depends upon local-package, via an `@app` aliased import. + { + name: _('/dist/local-package-2/package.json'), + contents: '{"name": "local-package-2", "es2015": "./index.js", "typings": "./index.d.ts"}' + }, + {name: _('/dist/local-package-2/index.metadata.json'), contents: 'DUMMY DATA'}, + { + name: _('/dist/local-package-2/index.js'), + contents: + `import {Component} from '@angular/core';\nexport {AppComponent} from '@app/local-package';` + }, + { + name: _('/dist/local-package-2/index.d.ts'), + contents: + `import {Component} from '@angular/core';\nexport {AppComponent} from '@app/local-package';` + }, + // local-package-3 depends upon local-package, via an `@x` aliased import. + { + name: _('/dist/local-package-3/package.json'), + contents: '{"name": "local-package-3", "es2015": "./index.js", "typings": "./index.d.ts"}' + }, + {name: _('/dist/local-package-3/index.metadata.json'), contents: 'DUMMY DATA'}, + { + name: _('/dist/local-package-3/index.js'), + contents: + `import {Component} from '@angular/core';\nexport {AppComponent} from '@x/local-package';` + }, + { + name: _('/dist/local-package-3/index.d.ts'), + contents: + `import {Component} from '@angular/core';\nexport {AppComponent} from '@x/local-package';` + }, + // local-package-4 depends upon local-package, via a plain import. + { + name: _('/dist/local-package-4/package.json'), + contents: '{"name": "local-package-", "es2015": "./index.js", "typings": "./index.d.ts"}' + }, + {name: _('/dist/local-package-4/index.metadata.json'), contents: 'DUMMY DATA'}, + { + name: _('/dist/local-package-4/index.js'), + contents: + `import {Component} from '@angular/core';\nexport {AppComponent} from 'local-package';` + }, + { + name: _('/dist/local-package-4/index.d.ts'), + contents: + `import {Component} from '@angular/core';\nexport {AppComponent} from 'local-package';` + }, ]); // An Angular package that has a missing dependency diff --git a/packages/compiler-cli/ngcc/test/integration/util.ts b/packages/compiler-cli/ngcc/test/integration/util.ts index e0880074831f5..735b33d13bc12 100644 --- a/packages/compiler-cli/ngcc/test/integration/util.ts +++ b/packages/compiler-cli/ngcc/test/integration/util.ts @@ -234,11 +234,21 @@ class MockCompilerHost implements ts.CompilerHost { this.fs.writeFile(this.fs.resolve(fileName), data); } - getCurrentDirectory(): string { return this.fs.pwd(); } - getCanonicalFileName(fileName: string): string { return fileName; } - useCaseSensitiveFileNames(): boolean { return true; } - getNewLine(): string { return '\n'; } - fileExists(fileName: string): boolean { return this.fs.exists(this.fs.resolve(fileName)); } + getCurrentDirectory(): string { + return this.fs.pwd(); + } + getCanonicalFileName(fileName: string): string { + return fileName; + } + useCaseSensitiveFileNames(): boolean { + return true; + } + getNewLine(): string { + return '\n'; + } + fileExists(fileName: string): boolean { + return this.fs.exists(this.fs.resolve(fileName)); + } readFile(fileName: string): string|undefined { const abs = this.fs.resolve(fileName); return this.fs.exists(abs) ? this.fs.readFile(abs) : undefined; diff --git a/packages/compiler-cli/ngcc/test/locking/async_locker_spec.ts b/packages/compiler-cli/ngcc/test/locking/async_locker_spec.ts index 0922c92f358ff..a77b34c5d823f 100644 --- a/packages/compiler-cli/ngcc/test/locking/async_locker_spec.ts +++ b/packages/compiler-cli/ngcc/test/locking/async_locker_spec.ts @@ -14,13 +14,13 @@ import {MockLogger} from '../helpers/mock_logger'; runInEachFileSystem(() => { describe('AsyncLocker', () => { describe('lock()', () => { - it('should guard the `fn()` with calls to `write()` and `remove()`', async() => { + it('should guard the `fn()` with calls to `write()` and `remove()`', async () => { const fs = getFileSystem(); const log: string[] = []; const lockFile = new MockLockFile(fs, log); const locker = new AsyncLocker(lockFile, new MockLogger(), 100, 10); - await locker.lock(async() => { + await locker.lock(async () => { log.push('fn() - before'); // This promise forces node to do a tick in this function, ensuring that we are truly // testing an async scenario. @@ -31,7 +31,7 @@ runInEachFileSystem(() => { }); it('should guard the `fn()` with calls to `write()` and `remove()`, even if it throws', - async() => { + async () => { let error: string = ''; const fs = getFileSystem(); const log: string[] = []; @@ -39,7 +39,7 @@ runInEachFileSystem(() => { const locker = new AsyncLocker(lockFile, new MockLogger(), 100, 10); try { - await locker.lock(async() => { + await locker.lock(async () => { log.push('fn()'); throw new Error('ERROR'); }); @@ -50,7 +50,7 @@ runInEachFileSystem(() => { expect(log).toEqual(['write()', 'fn()', 'remove()']); }); - it('should retry if another process is locking', async() => { + it('should retry if another process is locking', async () => { const fs = getFileSystem(); const log: string[] = []; const lockFile = new MockLockFile(fs, log); @@ -66,10 +66,13 @@ runInEachFileSystem(() => { }); spyOn(lockFile, 'read').and.callFake(() => { log.push('read() => ' + lockFileContents); + if (lockFileContents === null) { + throw {code: 'ENOENT'}; + } return lockFileContents; }); - const promise = locker.lock(async() => log.push('fn()')); + const promise = locker.lock(async () => log.push('fn()')); // The lock is now waiting on the lock-file becoming free, so no `fn()` in the log. expect(log).toEqual(['write()', 'read() => 188']); expect(logger.logs.info).toEqual([[ @@ -83,7 +86,7 @@ runInEachFileSystem(() => { expect(log).toEqual(['write()', 'read() => 188', 'write()', 'fn()', 'remove()']); }); - it('should extend the retry timeout if the other process locking the file changes', async() => { + it('should extend the retry timeout if the other process locking the file changes', async () => { const fs = getFileSystem(); const log: string[] = []; const lockFile = new MockLockFile(fs, log); @@ -99,10 +102,13 @@ runInEachFileSystem(() => { }); spyOn(lockFile, 'read').and.callFake(() => { log.push('read() => ' + lockFileContents); + if (lockFileContents === null) { + throw {code: 'ENOENT'}; + } return lockFileContents; }); - const promise = locker.lock(async() => log.push('fn()')); + const promise = locker.lock(async () => log.push('fn()')); // The lock is now waiting on the lock-file becoming free, so no `fn()` in the log. expect(log).toEqual(['write()', 'read() => 188']); expect(logger.logs.info).toEqual([[ @@ -132,7 +138,7 @@ runInEachFileSystem(() => { }); it('should error if another process does not release the lock-file before this times out', - async() => { + async () => { const fs = getFileSystem(); const log: string[] = []; const lockFile = new MockLockFile(fs, log); @@ -148,10 +154,13 @@ runInEachFileSystem(() => { }); spyOn(lockFile, 'read').and.callFake(() => { log.push('read() => ' + lockFileContents); + if (lockFileContents === null) { + throw {code: 'ENOENT'}; + } return lockFileContents; }); - const promise = locker.lock(async() => log.push('fn()')); + const promise = locker.lock(async () => log.push('fn()')); // The lock is now waiting on the lock-file becoming free, so no `fn()` in the log. expect(log).toEqual(['write()', 'read() => 188']); @@ -159,11 +168,12 @@ runInEachFileSystem(() => { let error: Error; await promise.catch(e => error = e); expect(log).toEqual(['write()', 'read() => 188', 'write()', 'read() => 188']); - expect(error !.message) + expect(error!.message) .toEqual( `Timed out waiting 0.2s for another ngcc process, with id 188, to complete.\n` + - `(If you are sure no ngcc process is running then you should delete the lock-file at ${lockFile.path}.)`); + `(If you are sure no ngcc process is running then you should delete the lock-file at ${ + lockFile.path}.)`); }); }); }); -}); \ No newline at end of file +}); diff --git a/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/index_spec.ts b/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/index_spec.ts index ae7287b917ba2..1f1c85720b065 100644 --- a/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/index_spec.ts +++ b/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/index_spec.ts @@ -8,7 +8,7 @@ import {ChildProcess} from 'child_process'; import * as process from 'process'; -import {CachedFileSystem, FileSystem, getFileSystem} from '../../../../src/ngtsc/file_system'; +import {FileSystem, getFileSystem} from '../../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../../src/ngtsc/file_system/testing'; import {getLockFilePath} from '../../../src/locking/lock_file'; import {LockFileWithChildProcess} from '../../../src/locking/lock_file_with_child_process'; @@ -23,7 +23,7 @@ runInEachFileSystem(() => { class LockFileUnderTest extends LockFileWithChildProcess { // Note that log is initialized in the `createUnlocker()` function that is called from // super(), so we can't initialize it here. - log !: string[]; + log!: string[]; constructor(fs: FileSystem) { super(fs, new MockLogger()); fs.ensureDir(fs.dirname(this.path)); @@ -47,7 +47,11 @@ runInEachFileSystem(() => { const log = this.log; // Normally this would fork a child process and return it. // But we don't want to do that in these tests. - return <any>{disconnect() { log.push('unlocker.disconnect()'); }}; + return <any>{ + disconnect() { + log.push('unlocker.disconnect()'); + } + }; } } @@ -93,18 +97,6 @@ runInEachFileSystem(() => { const lockFile = new LockFileUnderTest(fs); expect(lockFile.read()).toEqual('{unknown}'); }); - - it('should not read file from the cache, since the file may have been modified externally', - () => { - const rawFs = getFileSystem(); - const fs = new CachedFileSystem(rawFs); - const lockFile = new LockFileUnderTest(fs); - rawFs.writeFile(lockFile.path, '' + process.pid); - expect(lockFile.read()).toEqual('' + process.pid); - // We need to write to the rawFs to ensure that we don't update the cache at this point - rawFs.writeFile(lockFile.path, '444'); - expect(lockFile.read()).toEqual('444'); - }); }); describe('remove()', () => { diff --git a/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/unlocker_spec.ts b/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/unlocker_spec.ts index d6d5d697ce7af..80ad758291e08 100644 --- a/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/unlocker_spec.ts +++ b/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/unlocker_spec.ts @@ -12,6 +12,8 @@ describe('unlocker', () => { it('should attach a handler to the `disconnect` event', () => { spyOn(process, 'on'); require('../../../src/locking/lock_file_with_child_process/unlocker'); - expect(process.on).toHaveBeenCalledWith('disconnect', jasmine.any(Function)); + // TODO: @JiaLiPassion, need to wait for @types/jasmine to handle the override case + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42455 + expect(process.on).toHaveBeenCalledWith('disconnect' as any, jasmine.any(Function)); }); }); diff --git a/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/util_spec.ts b/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/util_spec.ts index b0ffe4c672330..171d4625a445c 100644 --- a/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/util_spec.ts +++ b/packages/compiler-cli/ngcc/test/locking/lockfile_with_child_process/util_spec.ts @@ -5,7 +5,7 @@ * 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 {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem} from '../../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../../src/ngtsc/file_system/testing'; import {removeLockFile} from '../../../src/locking/lock_file_with_child_process/util'; import {MockLogger} from '../../helpers/mock_logger'; @@ -24,8 +24,9 @@ runInEachFileSystem(() => { }); describe('removeLockFile()', () => { - it('should do nothing if there is no file to remove', - () => { removeLockFile(fs, logger, absoluteFrom('/lockfile/path'), '1234'); }); + it('should do nothing if there is no file to remove', () => { + removeLockFile(fs, logger, absoluteFrom('/lockfile/path'), '1234'); + }); it('should do nothing if the pid does not match', () => { fs.writeFile(lockFilePath, '888'); diff --git a/packages/compiler-cli/ngcc/test/locking/sync_locker_spec.ts b/packages/compiler-cli/ngcc/test/locking/sync_locker_spec.ts index 8cd9687d07121..05e18987d78ee 100644 --- a/packages/compiler-cli/ngcc/test/locking/sync_locker_spec.ts +++ b/packages/compiler-cli/ngcc/test/locking/sync_locker_spec.ts @@ -50,7 +50,9 @@ runInEachFileSystem(() => { const lockFile = new MockLockFile(fs, log); const locker = new SyncLocker(lockFile); - spyOn(lockFile, 'write').and.callFake(() => { throw {code: 'EEXIST'}; }); + spyOn(lockFile, 'write').and.callFake(() => { + throw {code: 'EEXIST'}; + }); spyOn(lockFile, 'read').and.returnValue('188'); expect(() => locker.lock(() => {})) @@ -58,7 +60,8 @@ runInEachFileSystem(() => { `ngcc is already running at process with id 188.\n` + `If you are running multiple builds in parallel then you should pre-process your node_modules via the command line ngcc tool before starting the builds;\n` + `See https://v9.angular.io/guide/ivy#speeding-up-ngcc-compilation.\n` + - `(If you are sure no ngcc process is running then you should delete the lock-file at ${lockFile.path}.)`); + `(If you are sure no ngcc process is running then you should delete the lock-file at ${ + lockFile.path}.)`); }); }); }); diff --git a/packages/compiler-cli/ngcc/test/migrations/missing_injectable_migration_spec.ts b/packages/compiler-cli/ngcc/test/migrations/missing_injectable_migration_spec.ts index 8ae41906d41b0..f8553bda29e92 100644 --- a/packages/compiler-cli/ngcc/test/migrations/missing_injectable_migration_spec.ts +++ b/packages/compiler-cli/ngcc/test/migrations/missing_injectable_migration_spec.ts @@ -7,14 +7,14 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {absoluteFrom, AbsoluteFsPath, getFileSystem} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {DecorationAnalyses} from '../../src/analysis/types'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; -import {MissingInjectableMigration, getAngularCoreDecoratorName} from '../../src/migrations/missing_injectable_migration'; +import {getAngularCoreDecoratorName, MissingInjectableMigration} from '../../src/migrations/missing_injectable_migration'; import {MockLogger} from '../helpers/mock_logger'; import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils'; @@ -58,7 +58,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'ServiceA')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceB')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceC')).toBe(false); @@ -66,7 +66,7 @@ runInEachFileSystem(() => { }); function runTests( - type: 'NgModule' | 'Directive' | 'Component', propName: 'providers' | 'viewProviders') { + type: 'NgModule'|'Directive'|'Component', propName: 'providers'|'viewProviders') { const args = type === 'Component' ? 'template: "", ' : ''; it(`should migrate type provider in ${type}`, () => { @@ -85,7 +85,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'OtherService')).toBe(false); }); @@ -106,12 +106,12 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'OtherService')).toBe(false); }); - it(`should migrate object literal provider with forwardRef in ${type}`, async() => { + it(`should migrate object literal provider with forwardRef in ${type}`, async () => { const {program, analysis} = setUpAndAnalyzeProgram([{ name: INDEX_FILENAME, contents: ` @@ -121,12 +121,13 @@ runInEachFileSystem(() => { export class TestClass {} TestClass.decorators = [ - { type: ${type}, args: [{${args}${propName}: [{provide: forwardRef(() => MyService) }]}] } + { type: ${type}, args: [{${args}${ + propName}: [{provide: forwardRef(() => MyService) }]}] } ]; `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(true); }); @@ -140,12 +141,13 @@ runInEachFileSystem(() => { export class TestClass {} TestClass.decorators = [ - { type: ${type}, args: [{${args}${propName}: [{provide: MyService, useValue: null }]}] } + { type: ${type}, args: [{${args}${ + propName}: [{provide: MyService, useValue: null }]}] } ]; `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(false); }); @@ -159,12 +161,13 @@ runInEachFileSystem(() => { export class TestClass {} TestClass.decorators = [ - { type: ${type}, args: [{${args}${propName}: [{provide: MyService, useFactory: () => null }]}] } + { type: ${type}, args: [{${args}${ + propName}: [{provide: MyService, useFactory: () => null }]}] } ]; `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(false); }); @@ -189,7 +192,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'MyToken')).toBe(false); expect(hasInjectableDecorator(index, analysis, 'MyTokenAlias')).toBe(false); @@ -206,12 +209,13 @@ runInEachFileSystem(() => { export class TestClass {} TestClass.decorators = [ - { type: ${type}, args: [{${args}${propName}: [{provide: MyToken, useClass: MyService}]}] } + { type: ${type}, args: [{${args}${ + propName}: [{provide: MyToken, useClass: MyService}]}] } ]; `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'MyToken')).toBe(false); }); @@ -234,7 +238,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(getInjectableDecorators(index, analysis, 'MyService').length).toBe(1); }); @@ -256,7 +260,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(false); }); @@ -278,7 +282,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(false); }); @@ -300,7 +304,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(false); }); @@ -320,7 +324,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'ServiceA')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceB')).toBe(true); }); @@ -348,7 +352,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'ServiceA')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceB')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceC')).toBe(true); @@ -381,7 +385,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'ServiceA')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceB')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceC')).toBe(true); @@ -407,7 +411,7 @@ runInEachFileSystem(() => { ` }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'ServiceA')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceB')).toBe(true); expect(hasInjectableDecorator(index, analysis, 'ServiceC')).toBe(false); @@ -434,7 +438,7 @@ runInEachFileSystem(() => { ` }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(getInjectableDecorators(index, analysis, 'ServiceA').length).toBe(1); expect(getInjectableDecorators(index, analysis, 'ServiceB').length).toBe(1); }); @@ -454,7 +458,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'ServiceA')).toBe(false); }); @@ -481,7 +485,7 @@ runInEachFileSystem(() => { } ]); - const index = program.getSourceFile(SERVICE_FILENAME) !; + const index = program.getSourceFile(SERVICE_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(true); }); @@ -508,7 +512,7 @@ runInEachFileSystem(() => { } ]); - const index = program.getSourceFile(SERVICE_FILENAME) !; + const index = program.getSourceFile(SERVICE_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(false); }); @@ -527,7 +531,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(true); }); @@ -546,7 +550,7 @@ runInEachFileSystem(() => { `, }]); - const index = program.getSourceFile(INDEX_FILENAME) !; + const index = program.getSourceFile(INDEX_FILENAME)!; expect(hasInjectableDecorator(index, analysis, 'MyService')).toBe(false); }); } diff --git a/packages/compiler-cli/ngcc/test/migrations/undecorated_parent_migration_spec.ts b/packages/compiler-cli/ngcc/test/migrations/undecorated_parent_migration_spec.ts index 91ca55a152a9f..064aa26b96ff2 100644 --- a/packages/compiler-cli/ngcc/test/migrations/undecorated_parent_migration_spec.ts +++ b/packages/compiler-cli/ngcc/test/migrations/undecorated_parent_migration_spec.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; + +import {absoluteFrom, AbsoluteFsPath, getFileSystem} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; @@ -34,7 +35,7 @@ runInEachFileSystem(() => { ` }]); expect(errors).toEqual([]); - const file = analysis.get(program.getSourceFile(INDEX_FILENAME) !); + const file = analysis.get(program.getSourceFile(INDEX_FILENAME)!); expect(file).toBeUndefined(); }); @@ -56,7 +57,7 @@ runInEachFileSystem(() => { ` }]); expect(errors).toEqual([]); - const file = analysis.get(program.getSourceFile(INDEX_FILENAME) !) !; + const file = analysis.get(program.getSourceFile(INDEX_FILENAME)!)!; expect(file.compiledClasses.find(c => c.name === 'DerivedClass')).toBeDefined(); expect(file.compiledClasses.find(c => c.name === 'BaseClass')).toBeUndefined(); }); @@ -81,15 +82,15 @@ runInEachFileSystem(() => { ` }]); expect(errors).toEqual([]); - const file = analysis.get(program.getSourceFile(INDEX_FILENAME) !) !; + const file = analysis.get(program.getSourceFile(INDEX_FILENAME)!)!; expect(file.compiledClasses.find(c => c.name === 'DerivedClass')).toBeDefined(); - const baseClass = file.compiledClasses.find(c => c.name === 'BaseClass') !; - expect(baseClass.decorators !.length).toEqual(1); - const decorator = baseClass.decorators ![0]; + const baseClass = file.compiledClasses.find(c => c.name === 'BaseClass')!; + expect(baseClass.decorators!.length).toEqual(1); + const decorator = baseClass.decorators![0]; expect(decorator.name).toEqual('Directive'); expect(decorator.identifier).toBeNull('The decorator must be synthesized'); expect(decorator.import).toEqual({from: '@angular/core', name: 'Directive'}); - expect(decorator.args !.length).toEqual(0); + expect(decorator.args!.length).toEqual(0); }); it('should not add a decorator to a base class that is already decorated', () => { @@ -114,11 +115,11 @@ runInEachFileSystem(() => { ` }]); expect(errors).toEqual([]); - const file = analysis.get(program.getSourceFile(INDEX_FILENAME) !) !; + const file = analysis.get(program.getSourceFile(INDEX_FILENAME)!)!; expect(file.compiledClasses.find(c => c.name === 'DerivedClass')).toBeDefined(); - const baseClass = file.compiledClasses.find(c => c.name === 'BaseClass') !; - expect(baseClass.decorators !.length).toEqual(1); - const decorator = baseClass.decorators ![0]; + const baseClass = file.compiledClasses.find(c => c.name === 'BaseClass')!; + expect(baseClass.decorators!.length).toEqual(1); + const decorator = baseClass.decorators![0]; expect(decorator.name).toEqual('Directive'); expect(decorator.identifier).not.toBeNull('The decorator must not be synthesized'); }); @@ -145,25 +146,25 @@ runInEachFileSystem(() => { ` }]); expect(errors).toEqual([]); - const file = analysis.get(program.getSourceFile(INDEX_FILENAME) !) !; + const file = analysis.get(program.getSourceFile(INDEX_FILENAME)!)!; expect(file.compiledClasses.find(c => c.name === 'DerivedClass')).toBeDefined(); expect(file.compiledClasses.find(c => c.name === 'RealBaseClass')).toBeUndefined(); - const intermediateClass = file.compiledClasses.find(c => c.name === 'IntermediateClass') !; - expect(intermediateClass.decorators !.length).toEqual(1); - const intermediateDecorator = intermediateClass.decorators ![0]; + const intermediateClass = file.compiledClasses.find(c => c.name === 'IntermediateClass')!; + expect(intermediateClass.decorators!.length).toEqual(1); + const intermediateDecorator = intermediateClass.decorators![0]; expect(intermediateDecorator.name).toEqual('Directive'); expect(intermediateDecorator.identifier).toBeNull('The decorator must be synthesized'); expect(intermediateDecorator.import).toEqual({from: '@angular/core', name: 'Directive'}); - expect(intermediateDecorator.args !.length).toEqual(0); + expect(intermediateDecorator.args!.length).toEqual(0); - const baseClass = file.compiledClasses.find(c => c.name === 'BaseClass') !; - expect(baseClass.decorators !.length).toEqual(1); - const baseDecorator = baseClass.decorators ![0]; + const baseClass = file.compiledClasses.find(c => c.name === 'BaseClass')!; + expect(baseClass.decorators!.length).toEqual(1); + const baseDecorator = baseClass.decorators![0]; expect(baseDecorator.name).toEqual('Directive'); expect(baseDecorator.identifier).toBeNull('The decorator must be synthesized'); expect(baseDecorator.import).toEqual({from: '@angular/core', name: 'Directive'}); - expect(baseDecorator.args !.length).toEqual(0); + expect(baseDecorator.args!.length).toEqual(0); }); it('should handle the base class being in a different file (same package) as the derived class', @@ -195,14 +196,14 @@ runInEachFileSystem(() => { } ]); expect(errors).toEqual([]); - const file = analysis.get(program.getSourceFile(BASE_FILENAME) !) !; - const baseClass = file.compiledClasses.find(c => c.name === 'BaseClass') !; - expect(baseClass.decorators !.length).toEqual(1); - const decorator = baseClass.decorators ![0]; + const file = analysis.get(program.getSourceFile(BASE_FILENAME)!)!; + const baseClass = file.compiledClasses.find(c => c.name === 'BaseClass')!; + expect(baseClass.decorators!.length).toEqual(1); + const decorator = baseClass.decorators![0]; expect(decorator.name).toEqual('Directive'); expect(decorator.identifier).toBeNull('The decorator must be synthesized'); expect(decorator.import).toEqual({from: '@angular/core', name: 'Directive'}); - expect(decorator.args !.length).toEqual(0); + expect(decorator.args!.length).toEqual(0); }); it('should skip the base class if it is in a different package from the derived class', () => { diff --git a/packages/compiler-cli/ngcc/test/packages/build_marker_spec.ts b/packages/compiler-cli/ngcc/test/packages/build_marker_spec.ts index e6637cf99d1e4..ff9f414c7ac6a 100644 --- a/packages/compiler-cli/ngcc/test/packages/build_marker_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/build_marker_spec.ts @@ -8,7 +8,7 @@ import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; -import {NGCC_VERSION, cleanPackageJson, hasBeenProcessed, markAsProcessed, needsCleaning} from '../../src/packages/build_marker'; +import {cleanPackageJson, hasBeenProcessed, markAsProcessed, needsCleaning, NGCC_VERSION} from '../../src/packages/build_marker'; import {EntryPointPackageJson} from '../../src/packages/entry_point'; import {DirectPackageJsonUpdater} from '../../src/writing/package_json_updater'; @@ -192,8 +192,9 @@ runInEachFileSystem(() => { .toBe(false); }); - it('should return false if no markers exist', - () => { expect(hasBeenProcessed({name: 'test'}, 'module')).toBe(false); }); + it('should return false if no markers exist', () => { + expect(hasBeenProcessed({name: 'test'}, 'module')).toBe(false); + }); }); describe('needsCleaning()', () => { diff --git a/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts b/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts index c74a2d2f87b56..edf2a718ccf50 100644 --- a/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts @@ -7,7 +7,7 @@ */ import {createHash} from 'crypto'; -import {FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; import {DEFAULT_NGCC_CONFIG, NgccConfiguration} from '../../src/packages/configuration'; @@ -27,8 +27,8 @@ runInEachFileSystem(() => { it('should error if a project level config file is badly formatted', () => { loadTestFiles([{name: _Abs('/project-1/ngcc.config.js'), contents: `bad js code`}]); expect(() => new NgccConfiguration(fs, _Abs('/project-1'))) - .toThrowError( - `Invalid project configuration file at "${_Abs('/project-1/ngcc.config.js')}": Unexpected identifier`); + .toThrowError(`Invalid project configuration file at "${ + _Abs('/project-1/ngcc.config.js')}": Unexpected identifier`); }); }); @@ -50,8 +50,8 @@ runInEachFileSystem(() => { };` }]); const project1Conf = new NgccConfiguration(fs, project1); - const expectedProject1Config = - `{"packages":{"${project1Package1}":[{"entryPoints":{"${project1Package1EntryPoint1}":{}},"versionRange":"*"}]}}`; + const expectedProject1Config = `{"packages":{"${project1Package1}":[{"entryPoints":{"${ + project1Package1EntryPoint1}":{}},"versionRange":"*"}]}}`; expect(project1Conf.hash) .toEqual(createHash('md5').update(expectedProject1Config).digest('hex')); @@ -71,8 +71,8 @@ runInEachFileSystem(() => { };` }]); const project2Conf = new NgccConfiguration(fs, project2); - const expectedProject2Config = - `{"packages":{"${project2Package1}":[{"entryPoints":{"${project2Package1EntryPoint1}":{"ignore":true}},"versionRange":"*"}]}}`; + const expectedProject2Config = `{"packages":{"${project2Package1}":[{"entryPoints":{"${ + project2Package1EntryPoint1}":{"ignore":true}},"versionRange":"*"}]}}`; expect(project2Conf.hash) .toEqual(createHash('md5').update(expectedProject2Config).digest('hex')); }); @@ -102,6 +102,20 @@ runInEachFileSystem(() => { .toHaveBeenCalledWith(_Abs('/project-1/node_modules/package-1/ngcc.config.js')); }); + it('should read extra package config from package level file', () => { + loadTestFiles(packageWithConfigFiles( + 'package-1', 'entry-point-1', '1.0.0', 'ignorableDeepImportMatchers: [ /xxx/ ]')); + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); + + expect(config).toEqual({ + versionRange: '1.0.0', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}, + ignorableDeepImportMatchers: [/xxx/], + }); + }); + it('should used cached configuration for a package if available', () => { loadTestFiles(packageWithConfigFiles('package-1', 'entry-point-1', '1.0.0')); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); @@ -136,8 +150,9 @@ runInEachFileSystem(() => { }]); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); expect(() => configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0')) - .toThrowError( - `Invalid package configuration file at "${_Abs('/project-1/node_modules/package-1/ngcc.config.js')}": Unexpected identifier`); + .toThrowError(`Invalid package configuration file at "${ + _Abs( + '/project-1/node_modules/package-1/ngcc.config.js')}": Unexpected identifier`); }); }); @@ -168,6 +183,31 @@ runInEachFileSystem(() => { }); }); + it('should return configuration for a package found in a project level file', () => { + loadTestFiles([{ + name: _Abs('/project-1/ngcc.config.js'), + contents: ` + module.exports = { + packages: { + 'package-1': { + entryPoints: { + './entry-point-1': {} + }, + ignorableDeepImportMatchers: [ /xxx/ ], + }, + }, + };` + }]); + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); + expect(config).toEqual({ + versionRange: '*', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}, + ignorableDeepImportMatchers: [/xxx/], + }); + }); + it('should return configuration for the correct version of a package found in a project level file', () => { loadTestFiles([{ @@ -214,6 +254,114 @@ runInEachFileSystem(() => { .toEqual({versionRange: '*', entryPoints: {}}); }); + it('should correctly handle pre-release versions and version ranges', () => { + loadTestFiles([ + { + name: _Abs('/project-1/ngcc.config.js'), + contents: ` + module.exports = { + packages: { + 'package-1': { + entryPoints: { + './entry-point-1': {}, + }, + }, + 'package-2@1.0.0-beta.2': { + entryPoints: { + './entry-point-2': {}, + }, + }, + 'package-3@>=1.0.0-beta.2': { + entryPoints: { + './entry-point-3': {}, + }, + }, + }, + }; + `, + }, + ]); + + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + const getConfig = (packageName: string, version: string|null) => + configuration.getConfig(_Abs(`/project-1/node_modules/${packageName}`), version); + + // Default version range: * + expect(getConfig('package-1', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '*', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}, + }, + 'Config for package-1@1.0.0-beta.2'); + + // Version range: 1.0.0-beta.2 + expect(getConfig('package-2', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-2/entry-point-2')]: {}}, + }, + 'Config for package-2@1.0.0-beta.2'); + + expect(getConfig('package-2', '1.0.0')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-2@1.0.0'); + + expect(getConfig('package-2', null)) + .toEqual( + { + versionRange: '1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-2/entry-point-2')]: {}}, + }, + 'Config for package-2@null'); + + // Version range: >=1.0.0-beta.2 + expect(getConfig('package-3', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@1.0.0-beta.2'); + + expect(getConfig('package-3', '1.0.0')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@1.0.0'); + + expect(getConfig('package-3', '2.0.0')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@2.0.0'); + + expect(getConfig('package-3', '1.0.0-beta.1')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-3@1.0.0-beta.1'); + + expect(getConfig('package-3', '0.9.99')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-3@0.9.99'); + }); + it('should not get confused by the @ in namespaced packages', () => { loadTestFiles([{ name: _Abs('/project-1/ngcc.config.js'), @@ -235,7 +383,6 @@ runInEachFileSystem(() => { versionRange: '*', entryPoints: {[_Abs('/project-1/node_modules/@angular/common')]: {}} }); - }); it('should override package level config with project level config per package', () => { @@ -287,13 +434,13 @@ runInEachFileSystem(() => { }); describe('at the default level', () => { - const originalDefaultConfig = DEFAULT_NGCC_CONFIG.packages['package-1']; + const originalDefaultConfig = JSON.stringify(DEFAULT_NGCC_CONFIG.packages); beforeEach(() => { DEFAULT_NGCC_CONFIG.packages['package-1'] = { entryPoints: {'./default-level-entry-point': {}}, }; }); - afterEach(() => { DEFAULT_NGCC_CONFIG.packages['package-1'] = originalDefaultConfig; }); + afterEach(() => DEFAULT_NGCC_CONFIG.packages = JSON.parse(originalDefaultConfig)); it('should return configuration for a package found in the default config', () => { const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); @@ -304,8 +451,7 @@ runInEachFileSystem(() => { configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); expect(config).toEqual({ versionRange: '*', - entryPoints: - {[_Abs('/project-1/node_modules/package-1/default-level-entry-point')]: {}} + entryPoints: {[_Abs('/project-1/node_modules/package-1/default-level-entry-point')]: {}} }); }); @@ -319,8 +465,7 @@ runInEachFileSystem(() => { // merge entry-points. expect(config).toEqual({ versionRange: '1.0.0', - entryPoints: - {[_Abs('/project-1/node_modules/package-1/package-level-entry-point')]: {}} + entryPoints: {[_Abs('/project-1/node_modules/package-1/package-level-entry-point')]: {}} }); }); @@ -357,19 +502,122 @@ runInEachFileSystem(() => { // merge entry-points. expect(config).toEqual({ versionRange: '*', - entryPoints: - {[_Abs('/project-1/node_modules/package-1/project-level-entry-point')]: {}} + entryPoints: {[_Abs('/project-1/node_modules/package-1/project-level-entry-point')]: {}} }); }); + + it('should correctly handle pre-release versions and version ranges', () => { + Object.assign(DEFAULT_NGCC_CONFIG.packages, { + 'package-1': { + entryPoints: { + './entry-point-1': {}, + }, + }, + 'package-2@1.0.0-beta.2': { + entryPoints: { + './entry-point-2': {}, + }, + }, + 'package-3@>=1.0.0-beta.2': { + entryPoints: { + './entry-point-3': {}, + }, + }, + }); + + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + const getConfig = (packageName: string, version: string|null) => + configuration.getConfig(_Abs(`/project-1/node_modules/${packageName}`), version); + + // Default version range: * + expect(getConfig('package-1', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '*', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}, + }, + 'Config for package-1@1.0.0-beta.2'); + + // Version range: 1.0.0-beta.2 + expect(getConfig('package-2', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-2/entry-point-2')]: {}}, + }, + 'Config for package-2@1.0.0-beta.2'); + + expect(getConfig('package-2', '1.0.0')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-2@1.0.0'); + + expect(getConfig('package-2', null)) + .toEqual( + { + versionRange: '1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-2/entry-point-2')]: {}}, + }, + 'Config for package-2@null'); + + // Version range: >=1.0.0-beta.2 + expect(getConfig('package-3', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@1.0.0-beta.2'); + + expect(getConfig('package-3', '1.0.0')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@1.0.0'); + + expect(getConfig('package-3', '2.0.0')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@2.0.0'); + + expect(getConfig('package-3', '1.0.0-beta.1')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-3@1.0.0-beta.1'); + + expect(getConfig('package-3', '0.9.99')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-3@0.9.99'); + }); }); }); }); - function packageWithConfigFiles(packageName: string, entryPointName: string, version: string) { + function packageWithConfigFiles( + packageName: string, entryPointName: string, version: string, extraConfig: string = '') { return [ { name: _Abs(`/project-1/node_modules/${packageName}/ngcc.config.js`), - contents: `module.exports = {entryPoints: { './${entryPointName}': {}}}` + contents: ` + module.exports = { + entryPoints: { './${entryPointName}': {} }, + ${extraConfig} + };` }, { name: _Abs(`/project-1/node_modules/${packageName}/package.json`), diff --git a/packages/compiler-cli/ngcc/test/packages/entry_point_bundle_spec.ts b/packages/compiler-cli/ngcc/test/packages/entry_point_bundle_spec.ts index 9bec20881bde8..8f9b1df83d23c 100644 --- a/packages/compiler-cli/ngcc/test/packages/entry_point_bundle_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/entry_point_bundle_spec.ts @@ -13,7 +13,6 @@ import {makeEntryPointBundle} from '../../src/packages/entry_point_bundle'; runInEachFileSystem(() => { describe('entry point bundle', () => { - function setupMockFileSystem(): void { const _ = absoluteFrom; loadTestFiles([ @@ -196,7 +195,7 @@ runInEachFileSystem(() => { '/node_modules/other/index.d.ts', ].map(p => absoluteFrom(p).toString()))); - expect(esm5bundle.dts !.program.getSourceFiles().map(sf => sf.fileName)) + expect(esm5bundle.dts!.program.getSourceFiles().map(sf => sf.fileName)) .toEqual(jasmine.arrayWithExactContents([ // All modules in the dts program should be declaration files '/node_modules/test/index.d.ts', @@ -231,7 +230,7 @@ runInEachFileSystem(() => { /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/test/internal.js')); - expect(esm5bundle.dts !.program.getSourceFiles().map(sf => sf.fileName)) + expect(esm5bundle.dts!.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/test/internal.d.ts')); }); @@ -253,7 +252,7 @@ runInEachFileSystem(() => { /* pathMappings */ undefined, /* mirrorDtsFromSrc */ true); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/internal/esm2015/src/internal.js')); - expect(esm5bundle.dts !.program.getSourceFiles().map(sf => sf.fileName)) + expect(esm5bundle.dts!.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/internal/src/internal.d.ts')); }); @@ -275,7 +274,7 @@ runInEachFileSystem(() => { /* pathMappings */ undefined, /* mirrorDtsFromSrc */ false); expect(esm5bundle.src.program.getSourceFiles().map(sf => sf.fileName)) .toContain(absoluteFrom('/node_modules/test/internal.js')); - expect(esm5bundle.dts !.program.getSourceFiles().map(sf => sf.fileName)) + expect(esm5bundle.dts!.program.getSourceFiles().map(sf => sf.fileName)) .not.toContain(absoluteFrom('/node_modules/test/internal.d.ts')); }); }); diff --git a/packages/compiler-cli/ngcc/test/packages/entry_point_manifest_spec.ts b/packages/compiler-cli/ngcc/test/packages/entry_point_manifest_spec.ts index 928c32d9019a1..afed5fe6b134d 100644 --- a/packages/compiler-cli/ngcc/test/packages/entry_point_manifest_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/entry_point_manifest_spec.ts @@ -7,12 +7,12 @@ */ import {createHash} from 'crypto'; -import {FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, FileSystem, getFileSystem, relativeFrom} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; +import {EntryPointWithDependencies} from '../../src/dependencies/dependency_host'; import {NGCC_VERSION} from '../../src/packages/build_marker'; import {NgccConfiguration} from '../../src/packages/configuration'; -import {EntryPoint} from '../../src/packages/entry_point'; import {EntryPointManifest, EntryPointManifestFile} from '../../src/packages/entry_point_manifest'; import {MockLogger} from '../helpers/mock_logger'; @@ -129,20 +129,48 @@ runInEachFileSystem(() => { ]); manifestFile.entryPointPaths.push([ _Abs('/project/node_modules/some_package'), - _Abs('/project/node_modules/some_package/valid_entry_point') + _Abs('/project/node_modules/some_package/valid_entry_point'), + [ + _Abs('/project/node_modules/other_package_1'), + _Abs('/project/node_modules/other_package_2'), + ], + [ + _Abs('/project/node_modules/missing_1'), + relativeFrom('missing_2'), + ], + [ + _Abs('/project/node_modules/deep/import/path'), + ], ]); fs.writeFile( _Abs('/project/node_modules/__ngcc_entry_points__.json'), JSON.stringify(manifestFile)); const entryPoints = manifest.readEntryPointsUsingManifest(_Abs('/project/node_modules')); expect(entryPoints).toEqual([{ - name: 'some_package/valid_entry_point', packageJson: jasmine.any(Object), - package: _Abs('/project/node_modules/some_package'), - path: _Abs('/project/node_modules/some_package/valid_entry_point'), - typings: _Abs( - '/project/node_modules/some_package/valid_entry_point/valid_entry_point.d.ts'), - compiledByAngular: true, ignoreMissingDependencies: false, - generateDeepReexports: false, - } as any]); + entryPoint: { + name: 'some_package/valid_entry_point', + packageJson: jasmine.any(Object), + package: _Abs('/project/node_modules/some_package'), + path: _Abs('/project/node_modules/some_package/valid_entry_point'), + typings: + _Abs('/project/node_modules/some_package/valid_entry_point/valid_entry_point.d.ts'), + compiledByAngular: true, + ignoreMissingDependencies: false, + generateDeepReexports: false, + } as any, + depInfo: { + dependencies: new Set([ + _Abs('/project/node_modules/other_package_1'), + _Abs('/project/node_modules/other_package_2'), + ]), + missing: new Set([ + _Abs('/project/node_modules/missing_1'), + relativeFrom('missing_2'), + ]), + deepImports: new Set([ + _Abs('/project/node_modules/deep/import/path'), + ]) + } + }]); }); it('should return null if any of the entry-points are not valid', () => { @@ -150,7 +178,7 @@ runInEachFileSystem(() => { fs.writeFile(_Abs('/project/yarn.lock'), 'LOCK FILE CONTENTS'); manifestFile.entryPointPaths.push([ _Abs('/project/node_modules/some_package'), - _Abs('/project/node_modules/some_package/valid_entry_point') + _Abs('/project/node_modules/some_package/valid_entry_point'), [], [], [] ]); fs.writeFile( _Abs('/project/node_modules/__ngcc_entry_points__.json'), JSON.stringify(manifestFile)); @@ -165,6 +193,12 @@ runInEachFileSystem(() => { expect(fs.exists(_Abs('/project/node_modules/__ngcc_entry_points__.json'))).toBe(false); }); + it('should do nothing if the basePath is not node_modules', () => { + fs.writeFile(_Abs('/project/yarn.lock'), 'LOCK FILE CONTENTS'); + manifest.writeEntryPointManifest(_Abs('/project/dist'), []); + expect(fs.exists(_Abs('/project/dist/__ngcc_entry_points__.json'))).toBe(false); + }); + it('should write an __ngcc_entry_points__.json file below the base path if there is a yarn.lock file', () => { fs.writeFile(_Abs('/project/yarn.lock'), 'LOCK FILE CONTENTS'); @@ -215,25 +249,59 @@ runInEachFileSystem(() => { it('should write the package path and entry-point path of each entry-point provided', () => { fs.writeFile(_Abs('/project/package-lock.json'), 'LOCK FILE CONTENTS'); - const entryPoint1 = { - package: _Abs('/project/node_modules/package-1/'), - path: _Abs('/project/node_modules/package-1/'), - } as unknown as EntryPoint; - const entryPoint2 = { - package: _Abs('/project/node_modules/package-2/'), - path: _Abs('/project/node_modules/package-2/entry-point'), - } as unknown as EntryPoint; + const entryPoint1: EntryPointWithDependencies = { + entryPoint: { + package: _Abs('/project/node_modules/package-1/'), + path: _Abs('/project/node_modules/package-1/'), + } as any, + depInfo: { + dependencies: new Set([ + _Abs('/project/node_modules/other_package_1'), + _Abs('/project/node_modules/other_package_2'), + ]), + missing: new Set(), + deepImports: new Set() + } + }; + const entryPoint2: EntryPointWithDependencies = { + entryPoint: { + package: _Abs('/project/node_modules/package-2/'), + path: _Abs('/project/node_modules/package-2/entry-point'), + } as any, + depInfo: { + dependencies: new Set(), + missing: new Set([ + _Abs('/project/node_modules/missing_1'), + relativeFrom('missing_2'), + ]), + deepImports: new Set([ + _Abs('/project/node_modules/deep/import/path'), + ]) + } + }; manifest.writeEntryPointManifest(_Abs('/project/node_modules'), [entryPoint1, entryPoint2]); const file: EntryPointManifestFile = JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))); expect(file.entryPointPaths).toEqual([ [ - _Abs('/project/node_modules/package-1/'), - _Abs('/project/node_modules/package-1/'), + 'package-1', + 'package-1', + [ + _Abs('/project/node_modules/other_package_1'), + _Abs('/project/node_modules/other_package_2'), + ], ], [ - _Abs('/project/node_modules/package-2/'), - _Abs('/project/node_modules/package-2/entry-point'), + 'package-2', + 'package-2/entry-point', + [], + [ + _Abs('/project/node_modules/missing_1'), + relativeFrom('missing_2'), + ], + [ + _Abs('/project/node_modules/deep/import/path'), + ], ] ]); }); diff --git a/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts b/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts index f3917ff7736b7..4fed2fcd40fe5 100644 --- a/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/entry_point_spec.ts @@ -6,11 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; import {NgccConfiguration} from '../../src/packages/configuration'; -import {EntryPoint, INVALID_ENTRY_POINT, NO_ENTRY_POINT, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat, getEntryPointInfo} from '../../src/packages/entry_point'; +import {EntryPoint, EntryPointJsonProperty, getEntryPointFormat, getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT, SUPPORTED_FORMAT_PROPERTIES} from '../../src/packages/entry_point'; import {MockLogger} from '../helpers/mock_logger'; runInEachFileSystem(() => { @@ -69,9 +69,8 @@ runInEachFileSystem(() => { ]); const config = new NgccConfiguration(fs, _('/project')); spyOn(config, 'getConfig').and.returnValue({ - entryPoints: - {[_('/project/node_modules/some_package/valid_entry_point')]: {ignore: true}} - }); + entryPoints: {[_('/project/node_modules/some_package/valid_entry_point')]: {ignore: true}} + } as any); const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, _('/project/node_modules/some_package/valid_entry_point')); @@ -96,14 +95,16 @@ runInEachFileSystem(() => { esm2015: './some_other.js', }; spyOn(config, 'getConfig').and.returnValue({ - entryPoints: {[_('/project/node_modules/some_package/valid_entry_point')]: {override}} + entryPoints: {[_('/project/node_modules/some_package/valid_entry_point')]: {override}}, + versionRange: '*' }); const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, _('/project/node_modules/some_package/valid_entry_point')); const overriddenPackageJson = { - ...loadPackageJson(fs, '/project/node_modules/some_package/valid_entry_point'), - ...override}; + ...loadPackageJson(fs, '/project/node_modules/some_package/valid_entry_point'), + ...override + }; expect(entryPoint).toEqual({ name: 'some_package/valid_entry_point', package: SOME_PACKAGE, @@ -146,7 +147,8 @@ runInEachFileSystem(() => { JSON.parse(createPackageJson('missing_package_json', {excludes: ['name']})); spyOn(config, 'getConfig').and.returnValue({ entryPoints: - {[_('/project/node_modules/some_package/missing_package_json')]: {override}} + {[_('/project/node_modules/some_package/missing_package_json')]: {override}}, + versionRange: '*' }); const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, @@ -165,7 +167,7 @@ runInEachFileSystem(() => { }); - it('should return `INVALID_ENTRY_POINT` if there is no typings or types field in the package.json', + it('should return `INCOMPATIBLE_ENTRY_POINT` if there is no typings or types field in the package.json', () => { loadTestFiles([ { @@ -182,10 +184,10 @@ runInEachFileSystem(() => { const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, _('/project/node_modules/some_package/missing_typings')); - expect(entryPoint).toBe(INVALID_ENTRY_POINT); + expect(entryPoint).toBe(INCOMPATIBLE_ENTRY_POINT); }); - it('should return `INVALID_ENTRY_POINT` if the typings or types field is not a string in the package.json', + it('should return `INCOMPATIBLE_ENTRY_POINT` if the typings or types field is not a string in the package.json', () => { loadTestFiles([ { @@ -202,46 +204,47 @@ runInEachFileSystem(() => { const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, _('/project/node_modules/some_package/typings_array')); - expect(entryPoint).toBe(INVALID_ENTRY_POINT); + expect(entryPoint).toBe(INCOMPATIBLE_ENTRY_POINT); }); for (let prop of SUPPORTED_FORMAT_PROPERTIES) { // Ignore the UMD format - if (prop === 'main') continue; + if (prop === 'main' || prop === 'browser') continue; // Let's give 'module' a specific path, otherwise compute it based on the property. const typingsPath = prop === 'module' ? 'index' : `${prop}/missing_typings`; - it(`should return an object if it can guess the typings path from the "${prop}" field`, () => { - loadTestFiles([ - { - name: _('/project/node_modules/some_package/missing_typings/package.json'), - contents: createPackageJson('missing_typings', {excludes: ['typings']}), - }, - { - name: _( - `/project/node_modules/some_package/missing_typings/${typingsPath}.metadata.json`), - contents: 'some meta data', - }, - { - name: _(`/project/node_modules/some_package/missing_typings/${typingsPath}.d.ts`), - contents: '// some typings file', - }, - ]); - const config = new NgccConfiguration(fs, _('/project')); - const entryPoint = getEntryPointInfo( - fs, config, new MockLogger(), SOME_PACKAGE, - _('/project/node_modules/some_package/missing_typings')); - expect(entryPoint).toEqual({ - name: 'some_package/missing_typings', - package: SOME_PACKAGE, - path: _('/project/node_modules/some_package/missing_typings'), - typings: _(`/project/node_modules/some_package/missing_typings/${typingsPath}.d.ts`), - packageJson: loadPackageJson(fs, '/project/node_modules/some_package/missing_typings'), - compiledByAngular: true, - ignoreMissingDependencies: false, - generateDeepReexports: false, - }); - }); + it(`should return an object if it can guess the typings path from the "${prop}" field`, + () => { + loadTestFiles([ + { + name: _('/project/node_modules/some_package/missing_typings/package.json'), + contents: createPackageJson('missing_typings', {excludes: ['typings']}), + }, + { + name: _(`/project/node_modules/some_package/missing_typings/${ + typingsPath}.metadata.json`), + contents: 'some meta data', + }, + { + name: _(`/project/node_modules/some_package/missing_typings/${typingsPath}.d.ts`), + contents: '// some typings file', + }, + ]); + const config = new NgccConfiguration(fs, _('/project')); + const entryPoint = getEntryPointInfo( + fs, config, new MockLogger(), SOME_PACKAGE, + _('/project/node_modules/some_package/missing_typings')); + expect(entryPoint).toEqual({ + name: 'some_package/missing_typings', + package: SOME_PACKAGE, + path: _('/project/node_modules/some_package/missing_typings'), + typings: _(`/project/node_modules/some_package/missing_typings/${typingsPath}.d.ts`), + packageJson: loadPackageJson(fs, '/project/node_modules/some_package/missing_typings'), + compiledByAngular: true, + ignoreMissingDependencies: false, + generateDeepReexports: false, + }); + }); } it('should return an object with `compiledByAngular` set to false if there is no metadata.json file next to the typing file', @@ -279,7 +282,8 @@ runInEachFileSystem(() => { ]); const config = new NgccConfiguration(fs, _('/project')); spyOn(config, 'getConfig').and.returnValue({ - entryPoints: {[_('/project/node_modules/some_package/missing_metadata')]: {}} + entryPoints: {[_('/project/node_modules/some_package/missing_metadata')]: {}}, + versionRange: '*' }); const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, @@ -359,7 +363,7 @@ runInEachFileSystem(() => { }); }); - it('should return `INVALID_ENTRY_POINT` if the package.json is not valid JSON', () => { + it('should return `INCOMPATIBLE_ENTRY_POINT` if the package.json is not valid JSON', () => { loadTestFiles([ // package.json might not be a valid JSON // for example, @schematics/angular contains a package.json blueprint @@ -373,7 +377,7 @@ runInEachFileSystem(() => { const entryPoint = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, _('/project/node_modules/some_package/unexpected_symbols')); - expect(entryPoint).toBe(INVALID_ENTRY_POINT); + expect(entryPoint).toBe(INCOMPATIBLE_ENTRY_POINT); }); }); @@ -395,75 +399,110 @@ runInEachFileSystem(() => { const result = getEntryPointInfo( fs, config, new MockLogger(), SOME_PACKAGE, _('/project/node_modules/some_package/valid_entry_point')); - if (result === NO_ENTRY_POINT || result === INVALID_ENTRY_POINT) { + if (result === NO_ENTRY_POINT || result === INCOMPATIBLE_ENTRY_POINT) { return fail(`Expected an entry point but got ${result}`); } - entryPoint = result; + entryPoint = result as any; + }); + + it('should return `esm2015` format for `fesm2015` property', () => { + expect(getEntryPointFormat(fs, entryPoint, 'fesm2015')).toBe('esm2015'); + }); + + it('should return `esm5` format for `fesm5` property', () => { + expect(getEntryPointFormat(fs, entryPoint, 'fesm5')).toBe('esm5'); }); - it('should return `esm2015` format for `fesm2015` property', - () => { expect(getEntryPointFormat(fs, entryPoint, 'fesm2015')).toBe('esm2015'); }); + it('should return `esm2015` format for `es2015` property', () => { + expect(getEntryPointFormat(fs, entryPoint, 'es2015')).toBe('esm2015'); + }); + + it('should return `esm2015` format for `esm2015` property', () => { + expect(getEntryPointFormat(fs, entryPoint, 'esm2015')).toBe('esm2015'); + }); + + it('should return `esm5` format for `esm5` property', () => { + expect(getEntryPointFormat(fs, entryPoint, 'esm5')).toBe('esm5'); + }); - it('should return `esm5` format for `fesm5` property', - () => { expect(getEntryPointFormat(fs, entryPoint, 'fesm5')).toBe('esm5'); }); + it('should return `esm5` format for `module` property', () => { + expect(getEntryPointFormat(fs, entryPoint, 'module')).toBe('esm5'); + }); - it('should return `esm2015` format for `es2015` property', - () => { expect(getEntryPointFormat(fs, entryPoint, 'es2015')).toBe('esm2015'); }); + (['browser', 'main'] as EntryPointJsonProperty[]).forEach(browserOrMain => { + it('should return `esm5` for `' + browserOrMain + + '` if the file contains import or export statements', + () => { + const name = _( + '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'); + loadTestFiles([{name, contents: `import * as core from '@angular/core;`}]); + expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5'); - it('should return `esm2015` format for `esm2015` property', - () => { expect(getEntryPointFormat(fs, entryPoint, 'esm2015')).toBe('esm2015'); }); + loadTestFiles([{name, contents: `import {Component} from '@angular/core;`}]); + expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5'); - it('should return `esm5` format for `esm5` property', - () => { expect(getEntryPointFormat(fs, entryPoint, 'esm5')).toBe('esm5'); }); + loadTestFiles([{name, contents: `export function foo() {}`}]); + expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5'); - it('should return `esm5` format for `module` property', - () => { expect(getEntryPointFormat(fs, entryPoint, 'module')).toBe('esm5'); }); + loadTestFiles([{name, contents: `export * from 'abc';`}]); + expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5'); + }); - it('should return `umd` for `main` if the file contains a UMD wrapper function', () => { - loadTestFiles([{ - name: _( - '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'), - contents: ` + it('should return `umd` for `' + browserOrMain + + '` if the file contains a UMD wrapper function', + () => { + loadTestFiles([{ + name: _( + '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) : (global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core)); }(this, function (exports, core) { 'use strict'; })); ` - }]); - expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('umd'); - }); + }]); + expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd'); + }); - it('should return `commonjs` for `main` if the file does not contain a UMD wrapper function', () => { - loadTestFiles([{ - name: _( - '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'), - contents: ` + it('should return `commonjs` for `' + browserOrMain + + '` if the file does not contain a UMD wrapper function', + () => { + loadTestFiles([{ + name: _( + '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'), + contents: ` const core = require('@angular/core); module.exports = {}; ` - }]); - expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('commonjs'); - }); + }]); + expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('commonjs'); + }); - it('should resolve the format path with suitable postfixes', () => { - loadTestFiles([{ - name: _( - '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'), - contents: ` + it('should resolve the format path with suitable postfixes', () => { + loadTestFiles([{ + name: _( + '/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'), + contents: ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) : (global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core)); }(this, function (exports, core) { 'use strict'; })); ` - }]); + }]); - entryPoint.packageJson.main = './bundles/valid_entry_point/index'; - expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('umd'); + entryPoint.packageJson.main = './bundles/valid_entry_point/index'; + expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd'); + + entryPoint.packageJson.main = './bundles/valid_entry_point'; + expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd'); + }); + }); - entryPoint.packageJson.main = './bundles/valid_entry_point'; - expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('umd'); + it('should return `undefined` if the `browser` property is not a string', () => { + entryPoint.packageJson.browser = {} as any; + expect(getEntryPointFormat(fs, entryPoint, 'browser')).toBeUndefined(); }); }); }); @@ -481,6 +520,7 @@ export function createPackageJson( fesm5: `./fesm5/${packageName}.js`, esm5: `./esm5/${packageName}.js`, main: `./bundles/${packageName}/index.js`, + browser: `./bundles/${packageName}/index.js`, module: './index.js', }; if (excludes) { diff --git a/packages/compiler-cli/ngcc/test/rendering/commonjs_rendering_formatter_spec.ts b/packages/compiler-cli/ngcc/test/rendering/commonjs_rendering_formatter_spec.ts index 2071a8555e324..914208bedcd39 100644 --- a/packages/compiler-cli/ngcc/test/rendering/commonjs_rendering_formatter_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/commonjs_rendering_formatter_spec.ts @@ -8,19 +8,20 @@ import {DeclareVarStmt, LiteralExpr, StmtModifier} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; + +import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {NoopImportRewriter} from '../../../src/ngtsc/imports'; -import {AbsoluteFsPath, getFileSystem, getSourceFileOrError, absoluteFrom, absoluteFromSourceFile} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {loadTestFiles} from '../../../test/helpers'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {ImportManager} from '../../../src/ngtsc/translator'; +import {loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {CommonJsReflectionHost} from '../../src/host/commonjs_host'; import {CommonJsRenderingFormatter} from '../../src/rendering/commonjs_rendering_formatter'; -import {makeTestEntryPointBundle} from '../helpers/utils'; import {MockLogger} from '../helpers/mock_logger'; +import {makeTestEntryPointBundle} from '../helpers/utils'; runInEachFileSystem(() => { describe('CommonJsRenderingFormatter', () => { @@ -273,7 +274,7 @@ var A = (function() {`); const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js')); const output = new MagicString(PROGRAM.contents); renderer.rewriteSwitchableDeclarations( - output, file, switchMarkerAnalyses.get(sourceFile) !.declarations); + output, file, switchMarkerAnalyses.get(sourceFile)!.declarations); expect(output.toString()) .not.toContain(`var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`); expect(output.toString()) @@ -295,7 +296,7 @@ var A = (function() {`); const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); expect(output.toString()).toContain(` A.prototype.ngDoCheck = function() { @@ -314,15 +315,16 @@ SOME DEFINITION TEXT program, absoluteFromSourceFile(sourceFile), 'NoIife', ts.isFunctionDeclaration); const mockNoIifeClass: any = {declaration: noIifeDeclaration, name: 'NoIife'}; expect(() => renderer.addDefinitions(output, mockNoIifeClass, 'SOME DEFINITION TEXT')) - .toThrowError( - `Compiled class declaration is not inside an IIFE: NoIife in ${_('/node_modules/test-package/some/file.js')}`); + .toThrowError(`Compiled class declaration is not inside an IIFE: NoIife in ${ + _('/node_modules/test-package/some/file.js')}`); const badIifeDeclaration = getDeclaration( program, absoluteFromSourceFile(sourceFile), 'BadIife', ts.isVariableDeclaration); const mockBadIifeClass: any = {declaration: badIifeDeclaration, name: 'BadIife'}; expect(() => renderer.addDefinitions(output, mockBadIifeClass, 'SOME DEFINITION TEXT')) .toThrowError( - `Compiled class wrapper IIFE does not have a return statement: BadIife in ${_('/node_modules/test-package/some/file.js')}`); + `Compiled class wrapper IIFE does not have a return statement: BadIife in ${ + _('/node_modules/test-package/some/file.js')}`); }); }); @@ -347,8 +349,8 @@ SOME DEFINITION TEXT const program = {name: _('/node_modules/test-package/some/file.js'), contents}; const {renderer, decorationAnalyses, sourceFile} = setup(program); const output = new MagicString(contents); - const compiledClass = decorationAnalyses.get(sourceFile) !.compiledClasses.find( - c => c.name === 'SomeDirective') !; + const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find( + c => c.name === 'SomeDirective')!; renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS'); expect(output.toString()) .toContain( @@ -364,8 +366,8 @@ SOME DEFINITION TEXT const program = {name: _('/node_modules/test-package/some/file.js'), contents}; const {renderer, decorationAnalyses, sourceFile} = setup(program); const output = new MagicString(contents); - const compiledClass = decorationAnalyses.get(sourceFile) !.compiledClasses.find( - c => c.name === 'SomeDirective') !; + const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find( + c => c.name === 'SomeDirective')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITIONS'); renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS'); const definitionsPosition = output.toString().indexOf('SOME DEFINITIONS'); @@ -377,16 +379,15 @@ SOME DEFINITION TEXT }); describe('removeDecorators', () => { - it('should delete the decorator (and following comma) that was matched in the analysis', () => { const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()) .not.toContain(`{ type: core.Directive, args: [{ selector: '[a]' }] },`); @@ -404,10 +405,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()) .toContain(`{ type: core.Directive, args: [{ selector: '[a]' }] },`); @@ -425,10 +426,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); expect(output.toString()) @@ -441,7 +442,6 @@ SOME DEFINITION TEXT .toContain(`function C() {}\nSOME DEFINITION TEXT\n return C;`); expect(output.toString()).not.toContain(`C.decorators`); }); - }); describe('[__decorate declarations]', () => { @@ -450,10 +450,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).not.toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -467,10 +467,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -485,10 +485,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); diff --git a/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts index ac4294d51fb22..101427f31cdb9 100644 --- a/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/dts_renderer_spec.ts @@ -7,21 +7,22 @@ */ import MagicString from 'magic-string'; import * as ts from 'typescript'; + import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {Reexport} from '../../../src/ngtsc/imports'; -import {loadTestFiles} from '../../../test/helpers'; import {Import, ImportManager} from '../../../src/ngtsc/translator'; +import {loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; -import {CompiledClass} from '../../src/analysis/types'; -import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {ModuleWithProvidersAnalyzer, ModuleWithProvidersInfo} from '../../src/analysis/module_with_providers_analyzer'; -import {PrivateDeclarationsAnalyzer, ExportInfo} from '../../src/analysis/private_declarations_analyzer'; +import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; +import {ExportInfo, PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer'; +import {CompiledClass} from '../../src/analysis/types'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; -import {RenderingFormatter, RedundantDecoratorMap} from '../../src/rendering/rendering_formatter'; import {DtsRenderer} from '../../src/rendering/dts_renderer'; +import {RedundantDecoratorMap, RenderingFormatter} from '../../src/rendering/rendering_formatter'; import {MockLogger} from '../helpers/mock_logger'; -import {makeTestEntryPointBundle, getRootFiles} from '../helpers/utils'; +import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils'; class TestRenderingFormatter implements RenderingFormatter { addImports(output: MagicString, imports: Import[], sf: ts.SourceFile) { @@ -53,7 +54,9 @@ class TestRenderingFormatter implements RenderingFormatter { importManager: ImportManager): void { output.prepend('\n// ADD MODUlE WITH PROVIDERS PARAMS\n'); } - printStatement(): string { return 'IGNORED'; } + printStatement(): string { + return 'IGNORED'; + } } function createTestRenderer( @@ -92,12 +95,14 @@ function createTestRenderer( const renderer = new DtsRenderer(testFormatter, fs, logger, host, bundle); - return {renderer, - testFormatter, - decorationAnalyses, - moduleWithProvidersAnalyses, - privateDeclarationsAnalyses, - bundle}; + return { + renderer, + testFormatter, + decorationAnalyses, + moduleWithProvidersAnalyses, + privateDeclarationsAnalyses, + bundle + }; } runInEachFileSystem(() => { @@ -120,35 +125,44 @@ runInEachFileSystem(() => { }); it('should render extract types into typings files', () => { - const {renderer, decorationAnalyses, privateDeclarationsAnalyses, - moduleWithProvidersAnalyses} = - createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]); + const { + renderer, + decorationAnalyses, + privateDeclarationsAnalyses, + moduleWithProvidersAnalyses + } = createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]); const result = renderer.renderProgram( decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses); const typingsFile = - result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts')) !; + result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts'))!; expect(typingsFile.contents) .toContain( - 'foo(x: number): number;\n static ɵfac: ɵngcc0.ɵɵFactoryDef<A>;\n static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta'); + 'foo(x: number): number;\n static ɵfac: ɵngcc0.ɵɵFactoryDef<A, never>;\n static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta'); }); it('should render imports into typings files', () => { - const {renderer, decorationAnalyses, privateDeclarationsAnalyses, - moduleWithProvidersAnalyses} = - createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]); + const { + renderer, + decorationAnalyses, + privateDeclarationsAnalyses, + moduleWithProvidersAnalyses + } = createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]); const result = renderer.renderProgram( decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses); const typingsFile = - result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts')) !; + result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts'))!; expect(typingsFile.contents).toContain(`\n// ADD IMPORTS\n`); }); it('should render exports into typings files', () => { - const {renderer, decorationAnalyses, privateDeclarationsAnalyses, - moduleWithProvidersAnalyses} = - createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]); + const { + renderer, + decorationAnalyses, + privateDeclarationsAnalyses, + moduleWithProvidersAnalyses + } = createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]); // Add a mock export to trigger export rendering privateDeclarationsAnalyses.push({ @@ -161,20 +175,23 @@ runInEachFileSystem(() => { decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses); const typingsFile = - result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts')) !; + result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts'))!; expect(typingsFile.contents).toContain(`\n// ADD EXPORTS\n`); }); it('should render ModuleWithProviders type params', () => { - const {renderer, decorationAnalyses, privateDeclarationsAnalyses, - moduleWithProvidersAnalyses} = - createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]); + const { + renderer, + decorationAnalyses, + privateDeclarationsAnalyses, + moduleWithProvidersAnalyses + } = createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]); const result = renderer.renderProgram( decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses); const typingsFile = - result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts')) !; + result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts'))!; expect(typingsFile.contents).toContain(`\n// ADD MODUlE WITH PROVIDERS PARAMS\n`); }); }); diff --git a/packages/compiler-cli/ngcc/test/rendering/esm5_rendering_formatter_spec.ts b/packages/compiler-cli/ngcc/test/rendering/esm5_rendering_formatter_spec.ts index 8a4c4e0bc5888..72edb0a8408fb 100644 --- a/packages/compiler-cli/ngcc/test/rendering/esm5_rendering_formatter_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/esm5_rendering_formatter_spec.ts @@ -8,20 +8,21 @@ import {DeclareVarStmt, LiteralExpr, StmtModifier} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; + +import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {NoopImportRewriter} from '../../../src/ngtsc/imports'; -import {AbsoluteFsPath, absoluteFrom, absoluteFromSourceFile, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {getDeclaration} from '../../../src/ngtsc/testing'; import {ImportManager} from '../../../src/ngtsc/translator'; -import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {loadTestFiles} from '../../../test/helpers'; +import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {IMPORT_PREFIX} from '../../src/constants'; import {Esm5ReflectionHost} from '../../src/host/esm5_host'; import {Esm5RenderingFormatter} from '../../src/rendering/esm5_rendering_formatter'; -import {makeTestEntryPointBundle} from '../helpers/utils'; import {MockLogger} from '../helpers/mock_logger'; +import {makeTestEntryPointBundle} from '../helpers/utils'; function setup(file: {name: AbsoluteFsPath, contents: string}) { loadTestFiles([file]); @@ -39,13 +40,16 @@ function setup(file: {name: AbsoluteFsPath, contents: string}) { return { host, program: bundle.src.program, - sourceFile: bundle.src.file, renderer, decorationAnalyses, switchMarkerAnalyses, importManager + sourceFile: bundle.src.file, + renderer, + decorationAnalyses, + switchMarkerAnalyses, + importManager }; } runInEachFileSystem(() => { describe('Esm5RenderingFormatter', () => { - let _: typeof absoluteFrom; let PROGRAM: TestFile; let PROGRAM_DECORATE_HELPER: TestFile; @@ -276,7 +280,7 @@ var A = (function() {`); const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js')); const output = new MagicString(PROGRAM.contents); renderer.rewriteSwitchableDeclarations( - output, file, switchMarkerAnalyses.get(sourceFile) !.declarations); + output, file, switchMarkerAnalyses.get(sourceFile)!.declarations); expect(output.toString()) .not.toContain(`var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`); expect(output.toString()) @@ -298,7 +302,7 @@ var A = (function() {`); const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); expect(output.toString()).toContain(` A.prototype.ngDoCheck = function() { @@ -317,15 +321,16 @@ SOME DEFINITION TEXT program, absoluteFromSourceFile(sourceFile), 'NoIife', ts.isFunctionDeclaration); const mockNoIifeClass: any = {declaration: noIifeDeclaration, name: 'NoIife'}; expect(() => renderer.addDefinitions(output, mockNoIifeClass, 'SOME DEFINITION TEXT')) - .toThrowError( - `Compiled class declaration is not inside an IIFE: NoIife in ${_('/node_modules/test-package/some/file.js')}`); + .toThrowError(`Compiled class declaration is not inside an IIFE: NoIife in ${ + _('/node_modules/test-package/some/file.js')}`); const badIifeDeclaration = getDeclaration( program, absoluteFromSourceFile(sourceFile), 'BadIife', ts.isVariableDeclaration); const mockBadIifeClass: any = {declaration: badIifeDeclaration, name: 'BadIife'}; expect(() => renderer.addDefinitions(output, mockBadIifeClass, 'SOME DEFINITION TEXT')) .toThrowError( - `Compiled class wrapper IIFE does not have a return statement: BadIife in ${_('/node_modules/test-package/some/file.js')}`); + `Compiled class wrapper IIFE does not have a return statement: BadIife in ${ + _('/node_modules/test-package/some/file.js')}`); }); }); @@ -350,8 +355,8 @@ SOME DEFINITION TEXT const program = {name: _('/node_modules/test-package/some/file.js'), contents}; const {renderer, decorationAnalyses, sourceFile} = setup(program); const output = new MagicString(contents); - const compiledClass = decorationAnalyses.get(sourceFile) !.compiledClasses.find( - c => c.name === 'SomeDirective') !; + const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find( + c => c.name === 'SomeDirective')!; renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS'); expect(output.toString()) .toContain( @@ -367,8 +372,8 @@ SOME DEFINITION TEXT const program = {name: _('/node_modules/test-package/some/file.js'), contents}; const {renderer, decorationAnalyses, sourceFile} = setup(program); const output = new MagicString(contents); - const compiledClass = decorationAnalyses.get(sourceFile) !.compiledClasses.find( - c => c.name === 'SomeDirective') !; + const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find( + c => c.name === 'SomeDirective')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITIONS'); renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS'); const definitionsPosition = output.toString().indexOf('SOME DEFINITIONS'); @@ -381,16 +386,15 @@ SOME DEFINITION TEXT describe('removeDecorators', () => { - it('should delete the decorator (and following comma) that was matched in the analysis', () => { const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()) .not.toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`); @@ -406,10 +410,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`); expect(output.toString()).toContain(`{ type: OtherA }`); @@ -425,10 +429,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); expect(output.toString()).toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`); @@ -447,10 +451,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).not.toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -464,10 +468,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -481,10 +485,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -500,10 +504,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'E') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'E')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).not.toContain(`Directive({ selector: '[e]' })`); expect(output.toString()).not.toContain(`E = tslib_1.__decorate([`); @@ -515,10 +519,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'F') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'F')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).not.toContain(`Directive({ selector: '[f]' })`); expect(output.toString()).not.toContain(`F = tslib_1.__decorate([`); diff --git a/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts b/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts index 275c42e5c7c44..43f39e3080c53 100644 --- a/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts @@ -8,20 +8,21 @@ import {DeclareVarStmt, LiteralExpr, StmtModifier} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {NoopImportRewriter} from '../../../src/ngtsc/imports'; + import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {loadTestFiles, loadFakeCore} from '../../../test/helpers'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; +import {NoopImportRewriter} from '../../../src/ngtsc/imports'; import {ImportManager} from '../../../src/ngtsc/translator'; +import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; +import {ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {IMPORT_PREFIX} from '../../src/constants'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {EsmRenderingFormatter} from '../../src/rendering/esm_rendering_formatter'; -import {makeTestEntryPointBundle, getRootFiles} from '../helpers/utils'; import {MockLogger} from '../helpers/mock_logger'; -import {ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer'; +import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils'; function setup(files: TestFile[], dtsFiles?: TestFile[]) { const fs = getFileSystem(); @@ -32,7 +33,7 @@ function setup(files: TestFile[], dtsFiles?: TestFile[]) { } const logger = new MockLogger(); const bundle = makeTestEntryPointBundle( - 'test-package', 'esm2015', false, getRootFiles(files), dtsFiles && getRootFiles(dtsFiles)) !; + 'test-package', 'esm2015', false, getRootFiles(files), dtsFiles && getRootFiles(dtsFiles))!; const host = new Esm2015ReflectionHost(logger, false, bundle.src, bundle.dts); const referencesRegistry = new NgccReferencesRegistry(host); const decorationAnalyses = @@ -45,13 +46,16 @@ function setup(files: TestFile[], dtsFiles?: TestFile[]) { host, bundle, program: bundle.src.program, - sourceFile: bundle.src.file, renderer, decorationAnalyses, switchMarkerAnalyses, importManager, + sourceFile: bundle.src.file, + renderer, + decorationAnalyses, + switchMarkerAnalyses, + importManager, }; } runInEachFileSystem(() => { describe('EsmRenderingFormatter', () => { - let _: typeof absoluteFrom; let PROGRAM: TestFile; @@ -195,7 +199,7 @@ export class A {`); const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js')); const output = new MagicString(PROGRAM.contents); renderer.rewriteSwitchableDeclarations( - output, file, switchMarkerAnalyses.get(sourceFile) !.declarations); + output, file, switchMarkerAnalyses.get(sourceFile)!.declarations); expect(output.toString()) .not.toContain(`let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`); expect(output.toString()) @@ -216,7 +220,7 @@ export class A {`); const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM]); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); expect(output.toString()).toContain(` export class A {} @@ -230,7 +234,7 @@ A.decorators = [ const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM]); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); expect(output.toString()).toContain(` let C = C_1 = class C {}; @@ -259,8 +263,8 @@ C.decorators = [ const program = {name: _('/node_modules/test-package/some/file.js'), contents}; const {renderer, decorationAnalyses, sourceFile} = setup([program]); const output = new MagicString(contents); - const compiledClass = decorationAnalyses.get(sourceFile) !.compiledClasses.find( - c => c.name === 'SomeDirective') !; + const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find( + c => c.name === 'SomeDirective')!; renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS'); expect(output.toString()) .toContain( @@ -275,8 +279,8 @@ C.decorators = [ const program = {name: _('/node_modules/test-package/some/file.js'), contents}; const {renderer, decorationAnalyses, sourceFile} = setup([program]); const output = new MagicString(contents); - const compiledClass = decorationAnalyses.get(sourceFile) !.compiledClasses.find( - c => c.name === 'SomeDirective') !; + const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find( + c => c.name === 'SomeDirective')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITIONS'); renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS'); const definitionsPosition = output.toString().indexOf('SOME DEFINITIONS'); @@ -294,10 +298,10 @@ C.decorators = [ const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()) .not.toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`); @@ -315,10 +319,10 @@ C.decorators = [ const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()) .toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`); @@ -343,10 +347,10 @@ A.decorators = [ const {decorationAnalyses, sourceFile, renderer} = setup([file]); const output = new MagicString(text); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); // The decorator should have been removed correctly. expect(output.toString()).toContain('A.decorators = [ { type: OtherA }'); @@ -358,10 +362,10 @@ A.decorators = [ const {decorationAnalyses, sourceFile, renderer} = setup([PROGRAM]); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()) .toContain(`{ type: Directive, args: [{ selector: '[a]' }] },`); @@ -424,10 +428,10 @@ export { D }; const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).not.toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -441,10 +445,10 @@ export { D }; const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -459,10 +463,10 @@ export { D }; const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM_DECORATE_HELPER]); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -573,8 +577,8 @@ export { D }; new ModuleWithProvidersAnalyzer(host, referencesRegistry, true) .analyzeProgram(bundle.src.program); const typingsFile = getSourceFileOrError( - bundle.dts !.program, _('/node_modules/test-package/typings/index.d.ts')); - const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile) !; + bundle.dts!.program, _('/node_modules/test-package/typings/index.d.ts')); + const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile)!; const output = new MagicString(MODULE_WITH_PROVIDERS_DTS_PROGRAM[0].contents); const importManager = new ImportManager(new NoopImportRewriter(), 'i'); @@ -610,8 +614,8 @@ export { D }; new ModuleWithProvidersAnalyzer(host, referencesRegistry, true) .analyzeProgram(bundle.src.program); const typingsFile = getSourceFileOrError( - bundle.dts !.program, _('/node_modules/test-package/typings/module.d.ts')); - const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile) !; + bundle.dts!.program, _('/node_modules/test-package/typings/module.d.ts')); + const moduleWithProvidersInfo = moduleWithProvidersAnalyses.get(typingsFile)!; const output = new MagicString(MODULE_WITH_PROVIDERS_DTS_PROGRAM[1].contents); const importManager = new ImportManager(new NoopImportRewriter(), 'i'); diff --git a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts index dc3d144d181e5..ff450182c4f93 100644 --- a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts @@ -6,27 +6,28 @@ * found in the LICENSE file at https://angular.io/license */ import {Statement} from '@angular/compiler'; -import {SourceMapMappings, encode} from 'sourcemap-codec'; +import {fromObject, generateMapFileComment, SourceMapConverter} from 'convert-source-map'; import MagicString from 'magic-string'; +import {encode, SourceMapMappings} from 'sourcemap-codec'; import * as ts from 'typescript'; -import {fromObject, generateMapFileComment, SourceMapConverter} from 'convert-source-map'; + import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; import {NOOP_DEFAULT_IMPORT_RECORDER, Reexport} from '../../../src/ngtsc/imports'; -import {loadTestFiles} from '../../../test/helpers'; import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator'; +import {loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; -import {CompiledClass} from '../../src/analysis/types'; -import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {ModuleWithProvidersInfo} from '../../src/analysis/module_with_providers_analyzer'; -import {PrivateDeclarationsAnalyzer, ExportInfo} from '../../src/analysis/private_declarations_analyzer'; +import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; +import {ExportInfo, PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; +import {CompiledClass} from '../../src/analysis/types'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {Esm5ReflectionHost} from '../../src/host/esm5_host'; import {Renderer} from '../../src/rendering/renderer'; +import {RedundantDecoratorMap, RenderingFormatter} from '../../src/rendering/rendering_formatter'; import {MockLogger} from '../helpers/mock_logger'; -import {RenderingFormatter, RedundantDecoratorMap} from '../../src/rendering/rendering_formatter'; -import {makeTestEntryPointBundle, getRootFiles} from '../helpers/utils'; +import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils'; class TestRenderingFormatter implements RenderingFormatter { private printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed}); @@ -106,12 +107,14 @@ function createTestRenderer( const renderer = new Renderer(host, testFormatter, fs, logger, bundle); - return {renderer, - testFormatter, - decorationAnalyses, - switchMarkerAnalyses, - privateDeclarationsAnalyses, - bundle}; + return { + renderer, + testFormatter, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + bundle + }; } runInEachFileSystem(() => { @@ -227,8 +230,13 @@ runInEachFileSystem(() => { it('should render as JavaScript', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('test-package', [COMPONENT_PROGRAM]); + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [COMPONENT_PROGRAM]); renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy; @@ -253,8 +261,13 @@ A.ɵcmp = ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, v describe('calling RenderingFormatter methods', () => { it('should call addImports with the source code and info about the core Angular library.', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('test-package', [JS_CONTENT]); + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [JS_CONTENT]); const result = renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addImportsSpy = testFormatter.addImports as jasmine.Spy; @@ -266,8 +279,13 @@ A.ɵcmp = ɵngcc0.ɵɵdefineComponent({ type: A, selectors: [["a"]], decls: 1, v it('should call addDefinitions with the source code, the analyzed class and the rendered definitions.', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('test-package', [JS_CONTENT]); + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [JS_CONTENT]); renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy; @@ -284,8 +302,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` it('should call addAdjacentStatements with the source code, the analyzed class and the rendered statements', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('test-package', [JS_CONTENT]); + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [JS_CONTENT]); renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addAdjacentStatementsSpy = testFormatter.addAdjacentStatements as jasmine.Spy; @@ -303,8 +326,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` it('should call removeDecorators with the source code, a map of class decorators that have been analyzed', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('test-package', [JS_CONTENT]); + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [JS_CONTENT]); renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const removeDecoratorsSpy = testFormatter.removeDecorators as jasmine.Spy; @@ -326,8 +354,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` }); it('should render definitions as static fields', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('test-package', [NGMODULE_PROGRAM]); + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [NGMODULE_PROGRAM]); renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy; @@ -337,8 +370,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` }); it('should render adjacent statements', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('test-package', [NGMODULE_PROGRAM]); + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [NGMODULE_PROGRAM]); renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addAdjacentStatementsSpy = testFormatter.addAdjacentStatements as jasmine.Spy; @@ -347,8 +385,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` }); it('should render directives using the inner class name if different from outer', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer( 'test-package', [{ name: _('/node_modules/test-package/src/file.js'), @@ -379,8 +422,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` }); it('should render injectables using the inner class name if different from outer', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer( 'test-package', [{ name: _('/node_modules/test-package/src/file.js'), @@ -411,8 +459,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` }); it('should render ng-modules using the inner class name if different from outer', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer( 'test-package', [{ name: _('/node_modules/test-package/src/file.js'), @@ -448,8 +501,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` }); it('should render pipes using the inner class name if different from outer', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer( 'test-package', [{ name: _('/node_modules/test-package/src/file.js'), @@ -479,9 +537,13 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` }); it('should render classes without decorators if class fields are decorated', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = - createTestRenderer('test-package', [{ + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [{ name: _('/node_modules/test-package/src/file.js'), contents: ` import { Directive, ViewChild } from '@angular/core'; @@ -514,8 +576,13 @@ UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, vie it('should call renderImports after other abstract methods', () => { // This allows the other methods to add additional imports if necessary - const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('test-package', [JS_CONTENT]); + const { + renderer, + decorationAnalyses, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('test-package', [JS_CONTENT]); const addExportsSpy = testFormatter.addExports as jasmine.Spy; const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy; const addAdjacentStatementsSpy = testFormatter.addAdjacentStatements as jasmine.Spy; @@ -537,8 +604,12 @@ UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, vie name: JS_CONTENT.name, contents: JS_CONTENT.contents + '\n' + JS_CONTENT_MAP.toComment() }]; - const {decorationAnalyses, renderer, switchMarkerAnalyses, - privateDeclarationsAnalyses} = createTestRenderer('test-package', sourceFiles); + const { + decorationAnalyses, + renderer, + switchMarkerAnalyses, + privateDeclarationsAnalyses + } = createTestRenderer('test-package', sourceFiles); const [sourceFile, mapFile] = renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); expect(sourceFile.path).toEqual(_('/node_modules/test-package/src/file.js')); @@ -555,9 +626,12 @@ UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, vie }]; const mappingFiles: TestFile[] = [{name: _(JS_CONTENT.name + '.map'), contents: JS_CONTENT_MAP.toJSON()}]; - const {decorationAnalyses, renderer, switchMarkerAnalyses, - privateDeclarationsAnalyses} = - createTestRenderer('test-package', sourceFiles, undefined, mappingFiles); + const { + decorationAnalyses, + renderer, + switchMarkerAnalyses, + privateDeclarationsAnalyses + } = createTestRenderer('test-package', sourceFiles, undefined, mappingFiles); const [sourceFile, mapFile] = renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); expect(sourceFile.path).toEqual(_('/node_modules/test-package/src/file.js')); @@ -582,8 +656,13 @@ UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, vie contents: `export const NgModule = () => null;` }; // The package name of `@angular/core` indicates that we are compiling the core library. - const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('@angular/core', [CORE_FILE, R3_SYMBOLS_FILE]); + const { + decorationAnalyses, + renderer, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('@angular/core', [CORE_FILE, R3_SYMBOLS_FILE]); renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addAdjacentStatementsSpy = testFormatter.addAdjacentStatements as jasmine.Spy; @@ -602,8 +681,13 @@ UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, vie export class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n` }; - const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses, - testFormatter} = createTestRenderer('@angular/core', [CORE_FILE]); + const { + decorationAnalyses, + renderer, + switchMarkerAnalyses, + privateDeclarationsAnalyses, + testFormatter + } = createTestRenderer('@angular/core', [CORE_FILE]); renderer.renderProgram( decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addAdjacentStatementsSpy = testFormatter.addAdjacentStatements as jasmine.Spy; diff --git a/packages/compiler-cli/ngcc/test/rendering/umd_rendering_formatter_spec.ts b/packages/compiler-cli/ngcc/test/rendering/umd_rendering_formatter_spec.ts index e56f77751115d..b260b54b993f6 100644 --- a/packages/compiler-cli/ngcc/test/rendering/umd_rendering_formatter_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/umd_rendering_formatter_spec.ts @@ -8,16 +8,17 @@ import {DeclareVarStmt, LiteralExpr, StmtModifier} from '@angular/compiler'; import MagicString from 'magic-string'; import * as ts from 'typescript'; -import {NoopImportRewriter} from '../../../src/ngtsc/imports'; + import {absoluteFrom, absoluteFromSourceFile, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; -import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {loadTestFiles} from '../../../test/helpers'; +import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing'; +import {NoopImportRewriter} from '../../../src/ngtsc/imports'; import {getDeclaration} from '../../../src/ngtsc/testing'; +import {ImportManager} from '../../../src/ngtsc/translator'; +import {loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {UmdReflectionHost} from '../../src/host/umd_host'; -import {ImportManager} from '../../../src/ngtsc/translator'; import {UmdRenderingFormatter} from '../../src/rendering/umd_rendering_formatter'; import {MockLogger} from '../helpers/mock_logger'; import {makeTestEntryPointBundle} from '../helpers/utils'; @@ -40,14 +41,15 @@ function setup(file: TestFile) { decorationAnalyses, host, importManager, - program: src.program, renderer, - sourceFile: src.file, switchMarkerAnalyses + program: src.program, + renderer, + sourceFile: src.file, + switchMarkerAnalyses }; } runInEachFileSystem(() => { describe('UmdRenderingFormatter', () => { - let _: typeof absoluteFrom; let PROGRAM: TestFile; let PROGRAM_DECORATE_HELPER: TestFile; @@ -438,7 +440,7 @@ var A = (function() {`); const file = getSourceFileOrError(program, _('/node_modules/test-package/some/file.js')); const output = new MagicString(PROGRAM.contents); renderer.rewriteSwitchableDeclarations( - output, file, switchMarkerAnalyses.get(sourceFile) !.declarations); + output, file, switchMarkerAnalyses.get(sourceFile)!.declarations); expect(output.toString()) .not.toContain(`var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`); expect(output.toString()) @@ -460,7 +462,7 @@ var A = (function() {`); const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); expect(output.toString()).toContain(` A.prototype.ngDoCheck = function() { @@ -479,15 +481,16 @@ SOME DEFINITION TEXT program, absoluteFromSourceFile(sourceFile), 'NoIife', ts.isFunctionDeclaration); const mockNoIifeClass: any = {declaration: noIifeDeclaration, name: 'NoIife'}; expect(() => renderer.addDefinitions(output, mockNoIifeClass, 'SOME DEFINITION TEXT')) - .toThrowError( - `Compiled class declaration is not inside an IIFE: NoIife in ${_('/node_modules/test-package/some/file.js')}`); + .toThrowError(`Compiled class declaration is not inside an IIFE: NoIife in ${ + _('/node_modules/test-package/some/file.js')}`); const badIifeDeclaration = getDeclaration( program, absoluteFromSourceFile(sourceFile), 'BadIife', ts.isVariableDeclaration); const mockBadIifeClass: any = {declaration: badIifeDeclaration, name: 'BadIife'}; expect(() => renderer.addDefinitions(output, mockBadIifeClass, 'SOME DEFINITION TEXT')) .toThrowError( - `Compiled class wrapper IIFE does not have a return statement: BadIife in ${_('/node_modules/test-package/some/file.js')}`); + `Compiled class wrapper IIFE does not have a return statement: BadIife in ${ + _('/node_modules/test-package/some/file.js')}`); }); }); @@ -518,8 +521,8 @@ SOME DEFINITION TEXT const program = {name: _('/node_modules/test-package/some/file.js'), contents}; const {renderer, decorationAnalyses, sourceFile} = setup(program); const output = new MagicString(contents); - const compiledClass = decorationAnalyses.get(sourceFile) !.compiledClasses.find( - c => c.name === 'SomeDirective') !; + const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find( + c => c.name === 'SomeDirective')!; renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS'); expect(output.toString()) .toContain( @@ -535,8 +538,8 @@ SOME DEFINITION TEXT const program = {name: _('/node_modules/test-package/some/file.js'), contents}; const {renderer, decorationAnalyses, sourceFile} = setup(program); const output = new MagicString(contents); - const compiledClass = decorationAnalyses.get(sourceFile) !.compiledClasses.find( - c => c.name === 'SomeDirective') !; + const compiledClass = decorationAnalyses.get(sourceFile)!.compiledClasses.find( + c => c.name === 'SomeDirective')!; renderer.addDefinitions(output, compiledClass, 'SOME DEFINITIONS'); renderer.addAdjacentStatements(output, compiledClass, 'SOME STATEMENTS'); const definitionsPosition = output.toString().indexOf('SOME DEFINITIONS'); @@ -548,16 +551,15 @@ SOME DEFINITION TEXT }); describe('removeDecorators', () => { - it('should delete the decorator (and following comma) that was matched in the analysis', () => { const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()) .not.toContain(`{ type: core.Directive, args: [{ selector: '[a]' }] },`); @@ -575,10 +577,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()) .toContain(`{ type: core.Directive, args: [{ selector: '[a]' }] },`); @@ -596,10 +598,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM); const output = new MagicString(PROGRAM.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; - const decorator = compiledClass.decorators ![0]; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; + const decorator = compiledClass.decorators![0]; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); expect(output.toString()) @@ -610,7 +612,6 @@ SOME DEFINITION TEXT expect(output.toString()).toContain(`{ type: OtherB }`); expect(output.toString()).not.toContain(`C.decorators`); }); - }); describe('[__decorate declarations]', () => { @@ -619,10 +620,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'A')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).not.toContain(`core.Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -636,10 +637,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'B')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`core.Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); @@ -654,10 +655,10 @@ SOME DEFINITION TEXT const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER); const output = new MagicString(PROGRAM_DECORATE_HELPER.contents); const compiledClass = - decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; - const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !; + decorationAnalyses.get(sourceFile)!.compiledClasses.find(c => c.name === 'C')!; + const decorator = compiledClass.decorators!.find(d => d.name === 'Directive')!; const decoratorsToRemove = new Map<ts.Node, ts.Node[]>(); - decoratorsToRemove.set(decorator.node !.parent !, [decorator.node !]); + decoratorsToRemove.set(decorator.node!.parent!, [decorator.node!]); renderer.removeDecorators(output, decoratorsToRemove); expect(output.toString()).toContain(`core.Directive({ selector: '[a]' }),`); expect(output.toString()).toContain(`OtherA()`); diff --git a/packages/compiler-cli/ngcc/test/sourcemaps/source_file_loader_spec.ts b/packages/compiler-cli/ngcc/test/sourcemaps/source_file_loader_spec.ts index 51d656601f4f2..47caddf9fa88e 100644 --- a/packages/compiler-cli/ngcc/test/sourcemaps/source_file_loader_spec.ts +++ b/packages/compiler-cli/ngcc/test/sourcemaps/source_file_loader_spec.ts @@ -5,22 +5,25 @@ * 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 {FileSystem, absoluteFrom, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {absoluteFrom, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; import {fromObject} from 'convert-source-map'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {RawSourceMap} from '../../src/sourcemaps/raw_source_map'; import {SourceFileLoader as SourceFileLoader} from '../../src/sourcemaps/source_file_loader'; +import {MockLogger} from '../helpers/mock_logger'; runInEachFileSystem(() => { describe('SourceFileLoader', () => { let fs: FileSystem; + let logger: MockLogger; let _: typeof absoluteFrom; let registry: SourceFileLoader; beforeEach(() => { fs = getFileSystem(); + logger = new MockLogger(); _ = absoluteFrom; - registry = new SourceFileLoader(fs); + registry = new SourceFileLoader(fs, logger); }); describe('loadSourceFile', () => { @@ -75,7 +78,8 @@ runInEachFileSystem(() => { const encodedSourceMap = Buffer.from(JSON.stringify(sourceMap)).toString('base64'); const sourceFile = registry.loadSourceFile( _('/foo/src/index.js'), - `some inline content\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,${encodedSourceMap}`); + `some inline content\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,${ + encodedSourceMap}`); if (sourceFile === null) { return fail('Expected source file to be defined'); } @@ -139,26 +143,26 @@ runInEachFileSystem(() => { expect(sourceFile.sources.length).toEqual(3); - expect(sourceFile.sources[0] !.contents).toEqual('x content'); - expect(sourceFile.sources[0] !.sourcePath).toEqual(_('/foo/src/x.js')); - expect(sourceFile.sources[0] !.rawMap).toEqual(null); - expect(sourceFile.sources[0] !.sources).toEqual([]); + expect(sourceFile.sources[0]!.contents).toEqual('x content'); + expect(sourceFile.sources[0]!.sourcePath).toEqual(_('/foo/src/x.js')); + expect(sourceFile.sources[0]!.rawMap).toEqual(null); + expect(sourceFile.sources[0]!.sources).toEqual([]); - expect(sourceFile.sources[1] !.contents).toEqual('y content'); - expect(sourceFile.sources[1] !.sourcePath).toEqual(_('/foo/src/y.js')); - expect(sourceFile.sources[1] !.rawMap).toEqual(ySourceMap); + expect(sourceFile.sources[1]!.contents).toEqual('y content'); + expect(sourceFile.sources[1]!.sourcePath).toEqual(_('/foo/src/y.js')); + expect(sourceFile.sources[1]!.rawMap).toEqual(ySourceMap); - expect(sourceFile.sources[1] !.sources.length).toEqual(1); - expect(sourceFile.sources[1] !.sources[0] !.contents).toEqual('a content'); - expect(sourceFile.sources[1] !.sources[0] !.sourcePath).toEqual(_('/foo/src/a.js')); - expect(sourceFile.sources[1] !.sources[0] !.rawMap).toEqual(null); - expect(sourceFile.sources[1] !.sources[0] !.sources).toEqual([]); + expect(sourceFile.sources[1]!.sources.length).toEqual(1); + expect(sourceFile.sources[1]!.sources[0]!.contents).toEqual('a content'); + expect(sourceFile.sources[1]!.sources[0]!.sourcePath).toEqual(_('/foo/src/a.js')); + expect(sourceFile.sources[1]!.sources[0]!.rawMap).toEqual(null); + expect(sourceFile.sources[1]!.sources[0]!.sources).toEqual([]); - expect(sourceFile.sources[2] !.contents).toEqual('z content'); - expect(sourceFile.sources[2] !.sourcePath).toEqual(_('/foo/src/z.js')); - expect(sourceFile.sources[2] !.rawMap).toEqual(null); - expect(sourceFile.sources[2] !.sources).toEqual([]); + expect(sourceFile.sources[2]!.contents).toEqual('z content'); + expect(sourceFile.sources[2]!.sourcePath).toEqual(_('/foo/src/z.js')); + expect(sourceFile.sources[2]!.rawMap).toEqual(null); + expect(sourceFile.sources[2]!.sources).toEqual([]); }); it('should handle a missing source file referenced from a source-map', () => { @@ -181,35 +185,83 @@ runInEachFileSystem(() => { }); }); - it('should fail if there is a cyclic dependency in files loaded from disk', () => { - fs.ensureDir(_('/foo/src')); - - const aPath = _('/foo/src/a.js'); - fs.writeFile( - aPath, 'a content\n' + - fromObject(createRawSourceMap({file: 'a.js', sources: ['b.js']})).toComment()); - - const bPath = _('/foo/src/b.js'); - fs.writeFile( - bPath, 'b content\n' + - fromObject(createRawSourceMap({file: 'b.js', sources: ['c.js']})).toComment()); - - const cPath = _('/foo/src/c.js'); - fs.writeFile( - cPath, 'c content\n' + - fromObject(createRawSourceMap({file: 'c.js', sources: ['a.js']})).toComment()); - - expect(() => registry.loadSourceFile(aPath)) - .toThrowError( - `Circular source file mapping dependency: ${aPath} -> ${bPath} -> ${cPath} -> ${aPath}`); - }); + it('should log a warning if there is a cyclic dependency in source files loaded from disk', + () => { + fs.ensureDir(_('/foo/src')); + + const aMap = createRawSourceMap({file: 'a.js', sources: ['b.js']}); + + const aPath = _('/foo/src/a.js'); + fs.writeFile(aPath, 'a content\n' + fromObject(aMap).toComment()); + + const bPath = _('/foo/src/b.js'); + fs.writeFile( + bPath, + 'b content\n' + + fromObject(createRawSourceMap({file: 'b.js', sources: ['c.js']})).toComment()); + + const cPath = _('/foo/src/c.js'); + fs.writeFile( + cPath, + 'c content\n' + + fromObject(createRawSourceMap({file: 'c.js', sources: ['a.js']})).toComment()); + + const sourceFile = registry.loadSourceFile(aPath)!; + expect(sourceFile).not.toBe(null!); + expect(sourceFile.contents).toEqual('a content\n'); + expect(sourceFile.sourcePath).toEqual(_('/foo/src/a.js')); + expect(sourceFile.rawMap).toEqual(aMap); + expect(sourceFile.sources.length).toEqual(1); + + expect(logger.logs.warn[0][0]) + .toContain( + `Circular source file mapping dependency: ` + + `${aPath} -> ${bPath} -> ${cPath} -> ${aPath}`); + }); + + it('should log a warning if there is a cyclic dependency in source maps loaded from disk', + () => { + fs.ensureDir(_('/foo/src')); + + // Create a self-referencing source-map + const aMap = createRawSourceMap({ + file: 'a.js', + sources: ['a.js'], + sourcesContent: ['inline a.js content\n//# sourceMappingURL=a.js.map'] + }); + const aMapPath = _('/foo/src/a.js.map'); + fs.writeFile(aMapPath, JSON.stringify(aMap)); + + const aPath = _('/foo/src/a.js'); + fs.writeFile(aPath, 'a.js content\n//# sourceMappingURL=a.js.map'); + + const sourceFile = registry.loadSourceFile(aPath)!; + expect(sourceFile).not.toBe(null!); + expect(sourceFile.contents).toEqual('a.js content\n'); + expect(sourceFile.sourcePath).toEqual(_('/foo/src/a.js')); + expect(sourceFile.rawMap).toEqual(aMap); + expect(sourceFile.sources.length).toEqual(1); + + expect(logger.logs.warn[0][0]) + .toContain( + `Circular source file mapping dependency: ` + + `${aPath} -> ${aMapPath} -> ${aMapPath}`); + + const innerSourceFile = sourceFile.sources[0]!; + expect(innerSourceFile).not.toBe(null!); + expect(innerSourceFile.contents).toEqual('inline a.js content\n'); + expect(innerSourceFile.sourcePath).toEqual(_('/foo/src/a.js')); + expect(innerSourceFile.rawMap).toEqual(null); + expect(innerSourceFile.sources.length).toEqual(0); + }); it('should not fail if there is a cyclic dependency in filenames of inline sources', () => { fs.ensureDir(_('/foo/src')); const aPath = _('/foo/src/a.js'); fs.writeFile( - aPath, 'a content\n' + + aPath, + 'a content\n' + fromObject(createRawSourceMap({file: 'a.js', sources: ['b.js']})).toComment()); const bPath = _('/foo/src/b.js'); @@ -238,6 +290,7 @@ function createRawSourceMap(custom: Partial<RawSourceMap>): RawSourceMap { 'sources': [], 'sourcesContent': [], 'names': [], - 'mappings': '', ...custom + 'mappings': '', + ...custom }; } \ No newline at end of file diff --git a/packages/compiler-cli/ngcc/test/sourcemaps/source_file_spec.ts b/packages/compiler-cli/ngcc/test/sourcemaps/source_file_spec.ts index 5c0456bc39f99..7b7f44c5292e3 100644 --- a/packages/compiler-cli/ngcc/test/sourcemaps/source_file_spec.ts +++ b/packages/compiler-cli/ngcc/test/sourcemaps/source_file_spec.ts @@ -11,13 +11,15 @@ import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {RawSourceMap} from '../../src/sourcemaps/raw_source_map'; import {SegmentMarker} from '../../src/sourcemaps/segment_marker'; -import {Mapping, SourceFile, computeStartOfLinePositions, ensureOriginalSegmentLinks, extractOriginalSegments, findLastMappingIndexBefore, parseMappings} from '../../src/sourcemaps/source_file'; +import {computeStartOfLinePositions, ensureOriginalSegmentLinks, extractOriginalSegments, findLastMappingIndexBefore, Mapping, parseMappings, SourceFile} from '../../src/sourcemaps/source_file'; runInEachFileSystem(() => { describe('SourceFile and utilities', () => { let _: typeof absoluteFrom; - beforeEach(() => { _ = absoluteFrom; }); + beforeEach(() => { + _ = absoluteFrom; + }); describe('parseMappings()', () => { it('should be an empty array for source files with no source map', () => { @@ -118,7 +120,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 35, position: 35, next: undefined}; const index = findLastMappingIndexBefore(mappings, marker, /* exclusive */ false, 0); @@ -133,7 +135,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 35, position: 35, next: undefined}; const index = findLastMappingIndexBefore(mappings, marker, /* exclusive */ false, 0); @@ -147,7 +149,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 60, position: 60, next: undefined}; @@ -162,7 +164,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 5, position: 5, next: undefined}; @@ -180,7 +182,7 @@ runInEachFileSystem(() => { const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const index = findLastMappingIndexBefore(mappings, marker3, /* exclusive */ false, 0); expect(index).toEqual(2); }); @@ -194,7 +196,7 @@ runInEachFileSystem(() => { const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const index = findLastMappingIndexBefore(mappings, marker3, /* exclusive */ false, 0); expect(index).toEqual(3); }); @@ -209,7 +211,7 @@ runInEachFileSystem(() => { const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const index = findLastMappingIndexBefore(mappings, marker3, /* exclusive */ true, 0); expect(index).toEqual(1); }); @@ -223,7 +225,7 @@ runInEachFileSystem(() => { const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const index = findLastMappingIndexBefore(mappings, marker3, /* exclusive */ false, 0); expect(index).toEqual(3); }); @@ -238,7 +240,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 35, position: 35, next: undefined}; const index = findLastMappingIndexBefore(mappings, marker, /* exclusive */ false, 1); @@ -253,7 +255,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 30, position: 30, next: undefined}; const index = findLastMappingIndexBefore(mappings, marker, /* exclusive */ false, 2); @@ -268,7 +270,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 30, position: 30, next: undefined}; const index = findLastMappingIndexBefore(mappings, marker, /* exclusive */ false, 3); @@ -282,7 +284,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 25, position: 25, next: undefined}; @@ -298,7 +300,7 @@ runInEachFileSystem(() => { const marker2: SegmentMarker = {line: 0, column: 20, position: 20, next: marker3}; const marker1: SegmentMarker = {line: 0, column: 10, position: 10, next: marker2}; const mappings: Mapping[] = [marker1, marker2, marker3, marker4, marker5].map( - marker => ({ generatedSegment: marker } as Mapping)); + marker => ({generatedSegment: marker} as Mapping)); const marker: SegmentMarker = {line: 0, column: 30, position: 30, next: undefined}; diff --git a/packages/compiler-cli/ngcc/test/utils_spec.ts b/packages/compiler-cli/ngcc/test/utils_spec.ts index e6c292df724ce..d01b1e3e9a08c 100644 --- a/packages/compiler-cli/ngcc/test/utils_spec.ts +++ b/packages/compiler-cli/ngcc/test/utils_spec.ts @@ -7,6 +7,8 @@ */ import * as ts from 'typescript'; +import {absoluteFrom as _abs} from '../../src/ngtsc/file_system'; +import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing'; import {KnownDeclaration} from '../../src/ngtsc/reflection'; import {FactoryMap, getTsHelperFnFromDeclaration, getTsHelperFnFromIdentifier, isRelativePath, stripExtension} from '../src/utils'; @@ -126,7 +128,7 @@ describe('getTsHelperFnFromDeclaration()', () => { const classDecl = ts.createClassDeclaration(undefined, undefined, '__assign', undefined, undefined, []); - expect(classDecl.name !.text).toBe('__assign'); + expect(classDecl.name!.text).toBe('__assign'); expect(getTsHelperFnFromDeclaration(classDecl)).toBe(null); }); }); @@ -167,30 +169,36 @@ describe('getTsHelperFnFromIdentifier()', () => { }); }); -describe('isRelativePath()', () => { - it('should return true for relative paths', () => { - expect(isRelativePath('.')).toBe(true); - expect(isRelativePath('..')).toBe(true); - expect(isRelativePath('./')).toBe(true); - expect(isRelativePath('../')).toBe(true); - expect(isRelativePath('./abc/xyz')).toBe(true); - expect(isRelativePath('../abc/xyz')).toBe(true); - }); - - it('should return true for absolute paths', () => { - expect(isRelativePath('/')).toBe(true); - expect(isRelativePath('/abc/xyz')).toBe(true); - }); - - it('should return false for other paths', () => { - expect(isRelativePath('abc')).toBe(false); - expect(isRelativePath('abc/xyz')).toBe(false); - expect(isRelativePath('.abc')).toBe(false); - expect(isRelativePath('..abc')).toBe(false); - expect(isRelativePath('@abc')).toBe(false); - expect(isRelativePath('.abc/xyz')).toBe(false); - expect(isRelativePath('..abc/xyz')).toBe(false); - expect(isRelativePath('@abc/xyz')).toBe(false); +runInEachFileSystem(() => { + describe('isRelativePath()', () => { + it('should return true for relative paths', () => { + expect(isRelativePath('.')).toBe(true); + expect(isRelativePath('..')).toBe(true); + expect(isRelativePath('./')).toBe(true); + expect(isRelativePath('.\\')).toBe(true); + expect(isRelativePath('../')).toBe(true); + expect(isRelativePath('..\\')).toBe(true); + expect(isRelativePath('./abc/xyz')).toBe(true); + expect(isRelativePath('.\\abc\\xyz')).toBe(true); + expect(isRelativePath('../abc/xyz')).toBe(true); + expect(isRelativePath('..\\abc\\xyz')).toBe(true); + }); + + it('should return true for absolute paths', () => { + expect(isRelativePath(_abs('/'))).toBe(true); + expect(isRelativePath(_abs('/abc/xyz'))).toBe(true); + }); + + it('should return false for other paths', () => { + expect(isRelativePath('abc')).toBe(false); + expect(isRelativePath('abc/xyz')).toBe(false); + expect(isRelativePath('.abc')).toBe(false); + expect(isRelativePath('..abc')).toBe(false); + expect(isRelativePath('@abc')).toBe(false); + expect(isRelativePath('.abc/xyz')).toBe(false); + expect(isRelativePath('..abc/xyz')).toBe(false); + expect(isRelativePath('@abc/xyz')).toBe(false); + }); }); }); diff --git a/packages/compiler-cli/ngcc/test/writing/cleaning/cleaning_strategies_spec.ts b/packages/compiler-cli/ngcc/test/writing/cleaning/cleaning_strategies_spec.ts index e8b2b5ec71ea9..5678d41a64421 100644 --- a/packages/compiler-cli/ngcc/test/writing/cleaning/cleaning_strategies_spec.ts +++ b/packages/compiler-cli/ngcc/test/writing/cleaning/cleaning_strategies_spec.ts @@ -5,7 +5,7 @@ * 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 {AbsoluteFsPath, FileSystem, PathSegment, absoluteFrom, getFileSystem} from '../../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, PathSegment} from '../../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../../src/ngtsc/file_system/testing'; import {EntryPointPackageJson} from '../../../src/packages/entry_point'; import {BackupFileCleaner, NgccDirectoryCleaner, PackageJsonCleaner} from '../../../src/writing/cleaning/cleaning_strategies'; @@ -21,9 +21,10 @@ runInEachFileSystem(() => { }); describe('PackageJsonCleaner', () => { - let packageJsonPath: AbsoluteFsPath; - beforeEach(() => { packageJsonPath = _abs('/node_modules/pkg/package.json'); }); + beforeEach(() => { + packageJsonPath = _abs('/node_modules/pkg/package.json'); + }); describe('canClean()', () => { it('should return true if the basename is package.json', () => { @@ -150,7 +151,6 @@ runInEachFileSystem(() => { }); describe('canClean()', () => { - it('should return true if the file name ends in .__ivy_ngcc_bak and the processed file exists', () => { const strategy = new BackupFileCleaner(fs); @@ -192,7 +192,9 @@ runInEachFileSystem(() => { describe('NgccDirectoryCleaner', () => { let ivyDirectory: AbsoluteFsPath; - beforeEach(() => { ivyDirectory = _abs('/node_modules/pkg/__ivy_ngcc__'); }); + beforeEach(() => { + ivyDirectory = _abs('/node_modules/pkg/__ivy_ngcc__'); + }); describe('canClean()', () => { it('should return true if the path is a directory and is called __ivy_ngcc__', () => { diff --git a/packages/compiler-cli/ngcc/test/writing/cleaning/package_cleaner_spec.ts b/packages/compiler-cli/ngcc/test/writing/cleaning/package_cleaner_spec.ts index 762d024461378..7260845cfe0c1 100644 --- a/packages/compiler-cli/ngcc/test/writing/cleaning/package_cleaner_spec.ts +++ b/packages/compiler-cli/ngcc/test/writing/cleaning/package_cleaner_spec.ts @@ -5,7 +5,7 @@ * 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 {AbsoluteFsPath, FileSystem, PathSegment, absoluteFrom, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, PathSegment} from '@angular/compiler-cli/src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../../src/ngtsc/file_system/testing'; import {CleaningStrategy} from '../../../src/writing/cleaning/cleaning_strategies'; diff --git a/packages/compiler-cli/ngcc/test/writing/in_place_file_writer_spec.ts b/packages/compiler-cli/ngcc/test/writing/in_place_file_writer_spec.ts index 95eeae821f0e3..0b3d249bd35df 100644 --- a/packages/compiler-cli/ngcc/test/writing/in_place_file_writer_spec.ts +++ b/packages/compiler-cli/ngcc/test/writing/in_place_file_writer_spec.ts @@ -14,7 +14,6 @@ import {MockLogger} from '../helpers/mock_logger'; runInEachFileSystem(() => { describe('InPlaceFileWriter', () => { - let _: typeof absoluteFrom; beforeEach(() => { @@ -79,8 +78,8 @@ runInEachFileSystem(() => { () => fileWriter.writeBundle( {} as EntryPointBundle, [{path: absoluteBackupPath, contents: 'MODIFIED BACKED UP'}])) - .toThrowError( - `Tried to overwrite ${absoluteBackupPath}.__ivy_ngcc_bak with an ngcc back up file, which is disallowed.`); + .toThrowError(`Tried to overwrite ${ + absoluteBackupPath}.__ivy_ngcc_bak with an ngcc back up file, which is disallowed.`); }); it('should log an error, and skip writing the file, if the backup file already exists and errorOnFailedEntryPoint is false', @@ -95,7 +94,9 @@ runInEachFileSystem(() => { expect(fs.readFile(absoluteBackupPath)).toEqual('ORIGINAL ALREADY BACKED UP'); expect(fs.readFile(_(absoluteBackupPath + '.__ivy_ngcc_bak'))).toEqual('BACKED UP'); expect(logger.logs.error).toEqual([[ - `Tried to write ${absoluteBackupPath}.__ivy_ngcc_bak with an ngcc back up file but it already exists so not writing, nor backing up, ${absoluteBackupPath}.\n` + + `Tried to write ${ + absoluteBackupPath}.__ivy_ngcc_bak with an ngcc back up file but it already exists so not writing, nor backing up, ${ + absoluteBackupPath}.\n` + `This error may be because two or more entry-points overlap and ngcc has been asked to process some files more than once.\n` + `You should check other entry-points in this package and set up a config to ignore any that you are not using.` ]]); diff --git a/packages/compiler-cli/ngcc/test/writing/new_entry_point_file_writer_spec.ts b/packages/compiler-cli/ngcc/test/writing/new_entry_point_file_writer_spec.ts index f8e1e77d42d0d..5f361cbb4ebf1 100644 --- a/packages/compiler-cli/ngcc/test/writing/new_entry_point_file_writer_spec.ts +++ b/packages/compiler-cli/ngcc/test/writing/new_entry_point_file_writer_spec.ts @@ -5,11 +5,11 @@ * 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 {FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; import {NgccConfiguration} from '../../src/packages/configuration'; -import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, INVALID_ENTRY_POINT, NO_ENTRY_POINT, getEntryPointInfo} from '../../src/packages/entry_point'; +import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from '../../src/packages/entry_point'; import {EntryPointBundle, makeEntryPointBundle} from '../../src/packages/entry_point_bundle'; import {FileWriter} from '../../src/writing/file_writer'; import {NewEntryPointFileWriter} from '../../src/writing/new_entry_point_file_writer'; @@ -19,7 +19,6 @@ import {loadPackageJson} from '../packages/entry_point_spec'; runInEachFileSystem(() => { describe('NewEntryPointFileWriter', () => { - let _: typeof absoluteFrom; let fs: FileSystem; let fileWriter: FileWriter; @@ -107,8 +106,8 @@ runInEachFileSystem(() => { fs, logger, /* errorOnFailedEntryPoint */ true, new DirectPackageJsonUpdater(fs)); const config = new NgccConfiguration(fs, _('/')); const result = getEntryPointInfo( - fs, config, logger, _('/node_modules/test'), _('/node_modules/test')) !; - if (result === NO_ENTRY_POINT || result === INVALID_ENTRY_POINT) { + fs, config, logger, _('/node_modules/test'), _('/node_modules/test'))!; + if (result === NO_ENTRY_POINT || result === INCOMPATIBLE_ENTRY_POINT) { return fail(`Expected an entry point but got ${result}`); } entryPoint = result; @@ -247,8 +246,8 @@ runInEachFileSystem(() => { fs, logger, /* errorOnFailedEntryPoint */ true, new DirectPackageJsonUpdater(fs)); const config = new NgccConfiguration(fs, _('/')); const result = getEntryPointInfo( - fs, config, logger, _('/node_modules/test'), _('/node_modules/test/a')) !; - if (result === NO_ENTRY_POINT || result === INVALID_ENTRY_POINT) { + fs, config, logger, _('/node_modules/test'), _('/node_modules/test/a'))!; + if (result === NO_ENTRY_POINT || result === INCOMPATIBLE_ENTRY_POINT) { return fail(`Expected an entry point but got ${result}`); } entryPoint = result; @@ -376,8 +375,8 @@ runInEachFileSystem(() => { fs, logger, /* errorOnFailedEntryPoint */ true, new DirectPackageJsonUpdater(fs)); const config = new NgccConfiguration(fs, _('/')); const result = getEntryPointInfo( - fs, config, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/b')) !; - if (result === NO_ENTRY_POINT || result === INVALID_ENTRY_POINT) { + fs, config, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/b'))!; + if (result === NO_ENTRY_POINT || result === INCOMPATIBLE_ENTRY_POINT) { return fail(`Expected an entry point but got ${result}`); } entryPoint = result; @@ -501,6 +500,6 @@ runInEachFileSystem(() => { fs: FileSystem, entryPoint: EntryPoint, formatProperty: EntryPointJsonProperty, format: EntryPointFormat): EntryPointBundle { return makeEntryPointBundle( - fs, entryPoint, entryPoint.packageJson[formatProperty] !, false, format, true); + fs, entryPoint, entryPoint.packageJson[formatProperty]!, false, format, true); } }); diff --git a/packages/compiler-cli/ngcc/test/writing/package_json_updater_spec.ts b/packages/compiler-cli/ngcc/test/writing/package_json_updater_spec.ts index 56f6a9a64328e..8efe32c3baf49 100644 --- a/packages/compiler-cli/ngcc/test/writing/package_json_updater_spec.ts +++ b/packages/compiler-cli/ngcc/test/writing/package_json_updater_spec.ts @@ -5,7 +5,7 @@ * 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 {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; import {JsonObject} from '../../src/packages/entry_point'; diff --git a/packages/compiler-cli/src/diagnostics/translate_diagnostics.ts b/packages/compiler-cli/src/diagnostics/translate_diagnostics.ts index 46d98c2177a6c..d44d1f88526f9 100644 --- a/packages/compiler-cli/src/diagnostics/translate_diagnostics.ts +++ b/packages/compiler-cli/src/diagnostics/translate_diagnostics.ts @@ -35,7 +35,8 @@ export function translateDiagnostics( const fileName = span.start.file.url; ng.push({ messageText: diagnosticMessageToString(diagnostic.messageText), - category: diagnostic.category, span, + category: diagnostic.category, + span, source: SOURCE, code: DEFAULT_ERROR_CODE }); @@ -53,6 +54,6 @@ function sourceSpanOf(host: TypeCheckHost, source: ts.SourceFile, start: number) return host.parseSourceSpanOf(source.fileName, line, character); } -function diagnosticMessageToString(message: ts.DiagnosticMessageChain | string): string { +function diagnosticMessageToString(message: ts.DiagnosticMessageChain|string): string { return ts.flattenDiagnosticMessageText(message, '\n'); } diff --git a/packages/compiler-cli/src/language_services.ts b/packages/compiler-cli/src/language_services.ts index a59c053dc0d7b..3a75c881daf33 100644 --- a/packages/compiler-cli/src/language_services.ts +++ b/packages/compiler-cli/src/language_services.ts @@ -16,4 +16,4 @@ to the language service. */ export {MetadataCollector, ModuleMetadata} from './metadata'; export {CompilerOptions} from './transformers/api'; -export {MetadataReaderCache, MetadataReaderHost, createMetadataReaderCache, readMetadata} from './transformers/metadata_reader'; +export {createMetadataReaderCache, MetadataReaderCache, MetadataReaderHost, readMetadata} from './transformers/metadata_reader'; diff --git a/packages/compiler-cli/src/main.ts b/packages/compiler-cli/src/main.ts index 88b2fc37b5475..c57f62fbd003c 100644 --- a/packages/compiler-cli/src/main.ts +++ b/packages/compiler-cli/src/main.ts @@ -24,9 +24,9 @@ import {NodeJSFileSystem, setFileSystem} from './ngtsc/file_system'; export function main( args: string[], consoleError: (s: string) => void = console.error, config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers, programReuse?: { - program: api.Program | undefined, + program: api.Program|undefined, }, - modifiedResourceFiles?: Set<string>| null): number { + modifiedResourceFiles?: Set<string>|null): number { let {project, rootNames, options, errors: configErrors, watch, emitFlags} = config || readNgcCommandLineAndConfiguration(args); if (configErrors.length) { @@ -47,7 +47,9 @@ export function main( options, emitFlags, oldProgram, - emitCallback: createEmitCallback(options), customTransformers, modifiedResourceFiles + emitCallback: createEmitCallback(options), + customTransformers, + modifiedResourceFiles }); if (programReuse !== undefined) { programReuse.program = program; @@ -57,8 +59,8 @@ export function main( export function mainDiagnosticsForTest( args: string[], config?: NgcParsedConfiguration, - programReuse?: {program: api.Program | undefined}, - modifiedResourceFiles?: Set<string>| null): ReadonlyArray<ts.Diagnostic|api.Diagnostic> { + programReuse?: {program: api.Program|undefined}, + modifiedResourceFiles?: Set<string>|null): ReadonlyArray<ts.Diagnostic|api.Diagnostic> { let {project, rootNames, options, errors: configErrors, watch, emitFlags} = config || readNgcCommandLineAndConfiguration(args); if (configErrors.length) { @@ -100,9 +102,10 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un options.emitDecoratorMetadata = true; } const tsickleHost: Pick< - tsickle.TsickleHost, 'shouldSkipTsickleProcessing'|'pathToModuleName'| - 'shouldIgnoreWarningsForPath'|'fileNameToModuleId'|'googmodule'|'untyped'| - 'convertIndexImportShorthand'|'transformDecorators'|'transformTypesToClosure'> = { + tsickle.TsickleHost, + 'shouldSkipTsickleProcessing'|'pathToModuleName'|'shouldIgnoreWarningsForPath'| + 'fileNameToModuleId'|'googmodule'|'untyped'|'convertIndexImportShorthand'| + 'transformDecorators'|'transformTypesToClosure'> = { shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName) || // View Engine's generated files were never intended to be processed with tsickle. (!options.enableIvy && GENERATED_FILES.test(fileName)), @@ -111,7 +114,9 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un fileNameToModuleId: (fileName) => fileName, googmodule: false, untyped: true, - convertIndexImportShorthand: false, transformDecorators, transformTypesToClosure, + convertIndexImportShorthand: false, + transformDecorators, + transformTypesToClosure, }; if (options.annotateForClosureCompiler || options.annotationsAs === 'static fields') { @@ -147,7 +152,9 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un } } -export interface NgcParsedConfiguration extends ParsedConfiguration { watch?: boolean; } +export interface NgcParsedConfiguration extends ParsedConfiguration { + watch?: boolean; +} export function readNgcCommandLineAndConfiguration(args: string[]): NgcParsedConfiguration { const options: api.CompilerOptions = {}; @@ -194,7 +201,8 @@ export function readCommandLineAndConfiguration( } return { project, - rootNames: config.rootNames, options, + rootNames: config.rootNames, + options, errors: config.errors, emitFlags: config.emitFlags }; @@ -237,7 +245,7 @@ export function watchMode( function printDiagnostics( diagnostics: ReadonlyArray<ts.Diagnostic|api.Diagnostic>, - options: api.CompilerOptions | undefined, consoleError: (s: string) => void): void { + options: api.CompilerOptions|undefined, consoleError: (s: string) => void): void { if (diagnostics.length === 0) { return; } diff --git a/packages/compiler-cli/src/metadata/bundle_index_host.ts b/packages/compiler-cli/src/metadata/bundle_index_host.ts index 489c32c4dbd8f..74db9c89797bc 100644 --- a/packages/compiler-cli/src/metadata/bundle_index_host.ts +++ b/packages/compiler-cli/src/metadata/bundle_index_host.ts @@ -47,8 +47,7 @@ function createSyntheticIndexHost<H extends ts.CompilerHost>( newHost.writeFile = (fileName: string, data: string, writeByteOrderMark: boolean, - onError: ((message: string) => void) | undefined, - sourceFiles: Readonly<ts.SourceFile>[]) => { + onError: ((message: string) => void)|undefined, sourceFiles: Readonly<ts.SourceFile>[]) => { delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles); if (fileName.match(DTS) && sourceFiles && sourceFiles.length == 1 && path.normalize(sourceFiles[0].fileName) === normalSyntheticIndexName) { @@ -103,7 +102,7 @@ export function createBundleIndexHost<H extends ts.CompilerHost>( // contents of the flat module index. The bundle produced during emit does use the metadata cache // with associated transforms, so the metadata will have lowered expressions, resource inlining, // etc. - const getMetadataBundle = (cache: MetadataCache | null) => { + const getMetadataBundle = (cache: MetadataCache|null) => { const bundler = new MetadataBundler( indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host, cache, ngOptions), ngOptions.flatModulePrivateSymbolPrefix); @@ -113,7 +112,7 @@ export function createBundleIndexHost<H extends ts.CompilerHost>( // First, produce the bundle with no MetadataCache. const metadataBundle = getMetadataBundle(/* MetadataCache */ null); const name = - path.join(path.dirname(indexModule), ngOptions.flatModuleOutFile !.replace(JS_EXT, '.ts')); + path.join(path.dirname(indexModule), ngOptions.flatModuleOutFile!.replace(JS_EXT, '.ts')); const libraryIndex = `./${path.basename(indexModule)}`; const content = privateEntriesToIndex(libraryIndex, metadataBundle.privates); diff --git a/packages/compiler-cli/src/metadata/bundler.ts b/packages/compiler-cli/src/metadata/bundler.ts index 5c07de49c0d98..e1d3ad407e9a5 100644 --- a/packages/compiler-cli/src/metadata/bundler.ts +++ b/packages/compiler-cli/src/metadata/bundler.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {MetadataCache} from '../transformers/metadata_cache'; import {MetadataCollector} from './collector'; -import {ClassMetadata, ConstructorMetadata, FunctionMetadata, METADATA_VERSION, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicCallExpression, isMetadataSymbolicExpression, isMethodMetadata} from './schema'; +import {ClassMetadata, ConstructorMetadata, FunctionMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicCallExpression, isMetadataSymbolicExpression, isMethodMetadata, MemberMetadata, METADATA_VERSION, MetadataEntry, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata} from './schema'; @@ -59,7 +59,9 @@ interface Symbol { privateName?: string; } -export interface BundleEntries { [name: string]: MetadataEntry; } +export interface BundleEntries { + [name: string]: MetadataEntry; +} export interface BundlePrivateEntry { privateName: string; @@ -77,7 +79,7 @@ export interface MetadataBundlerHost { } type StaticsMetadata = { - [name: string]: MetadataValue | FunctionMetadata; + [name: string]: MetadataValue|FunctionMetadata; }; export class MetadataBundler { @@ -87,7 +89,7 @@ export class MetadataBundler { private rootModule: string; private privateSymbolPrefix: string; // TODO(issue/24571): remove '!'. - private exported !: Set<Symbol>; + private exported!: Set<Symbol>; constructor( private root: string, private importAs: string|undefined, private host: MetadataBundlerHost, @@ -106,14 +108,14 @@ export class MetadataBundler { const privates = Array.from(this.symbolMap.values()) .filter(s => s.referenced && s.isPrivate) .map(s => ({ - privateName: s.privateName !, - name: s.declaration !.name, - module: s.declaration !.module + privateName: s.privateName!, + name: s.declaration!.name, + module: s.declaration!.module })); const origins = Array.from(this.symbolMap.values()) .filter(s => s.referenced && !s.reexport) .reduce<{[name: string]: string}>((p, s) => { - p[s.isPrivate ? s.privateName ! : s.name] = s.declaration !.module; + p[s.isPrivate ? s.privateName! : s.name] = s.declaration!.module; return p; }, {}); const exports = this.getReExports(exportedSymbols); @@ -121,8 +123,10 @@ export class MetadataBundler { metadata: { __symbolic: 'module', version: METADATA_VERSION, - exports: exports.length ? exports : undefined, metadata, origins, - importAs: this.importAs ! + exports: exports.length ? exports : undefined, + metadata, + origins, + importAs: this.importAs! }, privates }; @@ -156,7 +160,7 @@ export class MetadataBundler { const exportSymbol = (exportedSymbol: Symbol, exportAs: string) => { const symbol = this.symbolOf(moduleName, exportAs); - result !.push(symbol); + result!.push(symbol); exportedSymbol.reexportedAs = symbol; symbol.exports = exportedSymbol; }; @@ -276,18 +280,18 @@ export class MetadataBundler { Array.from(this.symbolMap.values()).forEach(symbol => { if (symbol.referenced && !symbol.reexport) { let name = symbol.name; - const identifier = `${symbol.declaration!.module}:${symbol.declaration !.name}`; + const identifier = `${symbol.declaration!.module}:${symbol.declaration!.name}`; if (symbol.isPrivate && !symbol.privateName) { name = newPrivateName(this.privateSymbolPrefix); symbol.privateName = name; } if (symbolsMap.has(identifier)) { const names = symbolsMap.get(identifier); - names !.push(name); + names!.push(name); } else { symbolsMap.set(identifier, [name]); } - result[name] = symbol.value !; + result[name] = symbol.value!; } }); @@ -320,9 +324,9 @@ export class MetadataBundler { for (const symbol of exportedSymbols) { if (symbol.reexport) { // symbol.declaration is guaranteed to be defined during the phase this method is called. - const declaration = symbol.declaration !; + const declaration = symbol.declaration!; const module = declaration.module; - if (declaration !.name == '*') { + if (declaration!.name == '*') { // Reexport all the symbols. exportAlls.add(declaration.module); } else { @@ -346,12 +350,12 @@ export class MetadataBundler { private convertSymbol(symbol: Symbol) { // canonicalSymbol is ensured to be defined before this is called. - const canonicalSymbol = symbol.canonicalSymbol !; + const canonicalSymbol = symbol.canonicalSymbol!; if (!canonicalSymbol.referenced) { canonicalSymbol.referenced = true; // declaration is ensured to be definded before this method is called. - const declaration = canonicalSymbol.declaration !; + const declaration = canonicalSymbol.declaration!; const module = this.getMetadata(declaration.module); if (module) { const value = module.metadata[declaration.name]; @@ -399,11 +403,11 @@ export class MetadataBundler { private convertMember(moduleName: string, member: MemberMetadata) { const result: MemberMetadata = {__symbolic: member.__symbolic}; result.decorators = - member.decorators && member.decorators.map(d => this.convertExpression(moduleName, d) !); + member.decorators && member.decorators.map(d => this.convertExpression(moduleName, d)!); if (isMethodMetadata(member)) { (result as MethodMetadata).parameterDecorators = member.parameterDecorators && member.parameterDecorators.map( - d => d && d.map(p => this.convertExpression(moduleName, p) !)); + d => d && d.map(p => this.convertExpression(moduleName, p)!)); if (isConstructorMetadata(member)) { if (member.parameters) { (result as ConstructorMetadata).parameters = @@ -450,7 +454,7 @@ export class MetadataBundler { return this.convertError(moduleName, value); } if (isMetadataSymbolicExpression(value)) { - return this.convertExpression(moduleName, value) !; + return this.convertExpression(moduleName, value)!; } if (Array.isArray(value)) { return value.map(v => this.convertValue(moduleName, v)); @@ -466,8 +470,8 @@ export class MetadataBundler { } private convertExpression( - moduleName: string, value: MetadataSymbolicExpression|MetadataError|null| - undefined): MetadataSymbolicExpression|MetadataError|undefined|null { + moduleName: string, value: MetadataSymbolicExpression|MetadataError|null|undefined): + MetadataSymbolicExpression|MetadataError|undefined|null { if (value) { switch (value.__symbolic) { case 'error': @@ -487,14 +491,15 @@ export class MetadataBundler { message: value.message, line: value.line, character: value.character, - context: value.context, module + context: value.context, + module }; } private convertReference(moduleName: string, value: MetadataSymbolicReferenceExpression): MetadataSymbolicReferenceExpression|MetadataError|undefined { const createReference = (symbol: Symbol): MetadataSymbolicReferenceExpression => { - const declaration = symbol.declaration !; + const declaration = symbol.declaration!; if (declaration.module.startsWith('.')) { // Reference to a symbol defined in the module. Ensure it is converted then return a // references to the final symbol. @@ -503,11 +508,11 @@ export class MetadataBundler { __symbolic: 'reference', get name() { // Resolved lazily because private names are assigned late. - const canonicalSymbol = symbol.canonicalSymbol !; + const canonicalSymbol = symbol.canonicalSymbol!; if (canonicalSymbol.isPrivate == null) { throw Error('Invalid state: isPrivate was not initialized'); } - return canonicalSymbol.isPrivate ? canonicalSymbol.privateName ! : canonicalSymbol.name; + return canonicalSymbol.isPrivate ? canonicalSymbol.privateName! : canonicalSymbol.name; } }; } else { @@ -584,7 +589,7 @@ export class MetadataBundler { private convertExpressionNode(moduleName: string, value: MetadataSymbolicExpression): MetadataSymbolicExpression { - const result: MetadataSymbolicExpression = { __symbolic: value.__symbolic } as any; + const result: MetadataSymbolicExpression = {__symbolic: value.__symbolic} as any; for (const key in value) { (result as any)[key] = this.convertValue(moduleName, (value as any)[key]); } diff --git a/packages/compiler-cli/src/metadata/collector.ts b/packages/compiler-cli/src/metadata/collector.ts index f54ba3def7223..577347181e45e 100644 --- a/packages/compiler-cli/src/metadata/collector.ts +++ b/packages/compiler-cli/src/metadata/collector.ts @@ -8,8 +8,8 @@ import * as ts from 'typescript'; -import {Evaluator, errorSymbol, recordMapEntry} from './evaluator'; -import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, METADATA_VERSION, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema'; +import {errorSymbol, Evaluator, recordMapEntry} from './evaluator'; +import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata, MemberMetadata, METADATA_VERSION, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata} from './schema'; import {Symbols} from './symbols'; const isStatic = (node: ts.Declaration) => @@ -60,12 +60,12 @@ export class MetadataCollector { new Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>(); const composedSubstituter = substituteExpression && this.options.substituteExpression ? (value: MetadataValue, node: ts.Node) => - this.options.substituteExpression !(substituteExpression(value, node), node) : + this.options.substituteExpression!(substituteExpression(value, node), node) : substituteExpression; const evaluatorOptions = substituteExpression ? {...this.options, substituteExpression: composedSubstituter} : this.options; - let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined; + let metadata: {[name: string]: MetadataValue|ClassMetadata|FunctionMetadata}|undefined; const evaluator = new Evaluator(locals, nodeMap, evaluatorOptions, (name, value) => { if (!metadata) metadata = {}; metadata[name] = value; @@ -88,9 +88,9 @@ export class MetadataCollector { return errorSymbol(message, node, context, sourceFile); } - function maybeGetSimpleFunction( - functionDeclaration: ts.FunctionDeclaration | - ts.MethodDeclaration): {func: FunctionMetadata, name: string}|undefined { + function maybeGetSimpleFunction(functionDeclaration: ts.FunctionDeclaration| + ts.MethodDeclaration): {func: FunctionMetadata, name: string}| + undefined { if (functionDeclaration.name && functionDeclaration.name.kind == ts.SyntaxKind.Identifier) { const nameNode = <ts.Identifier>functionDeclaration.name; const functionName = nameNode.text; @@ -119,8 +119,8 @@ export class MetadataCollector { function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata { const result: ClassMetadata = {__symbolic: 'class'}; - function getDecorators(decorators: ReadonlyArray<ts.Decorator>| undefined): - MetadataSymbolicExpression[]|undefined { + function getDecorators(decorators: ReadonlyArray<ts.Decorator>| + undefined): MetadataSymbolicExpression[]|undefined { if (decorators && decorators.length) return decorators.map(decorator => objFromDecorator(decorator)); return undefined; @@ -167,8 +167,8 @@ export class MetadataCollector { } // static member - let statics: {[name: string]: MetadataValue | FunctionMetadata}|null = null; - function recordStaticMember(name: string, value: MetadataValue | FunctionMetadata) { + let statics: {[name: string]: MetadataValue|FunctionMetadata}|null = null; + function recordStaticMember(name: string, value: MetadataValue|FunctionMetadata) { if (!statics) statics = {}; statics[name] = value; } @@ -189,11 +189,10 @@ export class MetadataCollector { } const methodDecorators = getDecorators(method.decorators); const parameters = method.parameters; - const parameterDecoratorData: - ((MetadataSymbolicExpression | MetadataError)[] | undefined)[] = []; - const parametersData: - (MetadataSymbolicReferenceExpression | MetadataError | - MetadataSymbolicSelectExpression | null)[] = []; + const parameterDecoratorData: ((MetadataSymbolicExpression | MetadataError)[]| + undefined)[] = []; + const parametersData: (MetadataSymbolicReferenceExpression|MetadataError| + MetadataSymbolicSelectExpression|null)[] = []; let hasDecoratorData: boolean = false; let hasParameterData: boolean = false; for (const parameter of parameters) { @@ -282,15 +281,14 @@ export class MetadataCollector { ts.getCombinedModifierFlags(node as ts.Declaration) & ts.ModifierFlags.Export; const isExportedIdentifier = (identifier?: ts.Identifier) => identifier && exportMap.has(identifier.text); - const isExported = - (node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration | - ts.InterfaceDeclaration | ts.EnumDeclaration) => - isExport(node) || isExportedIdentifier(node.name); + const isExported = (node: ts.FunctionDeclaration|ts.ClassDeclaration|ts.TypeAliasDeclaration| + ts.InterfaceDeclaration|ts.EnumDeclaration) => + isExport(node) || isExportedIdentifier(node.name); const exportedIdentifierName = (identifier?: ts.Identifier) => identifier && (exportMap.get(identifier.text) || identifier.text); - const exportedName = - (node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration | - ts.TypeAliasDeclaration | ts.EnumDeclaration) => exportedIdentifierName(node.name); + const exportedName = (node: ts.FunctionDeclaration|ts.ClassDeclaration| + ts.InterfaceDeclaration|ts.TypeAliasDeclaration|ts.EnumDeclaration) => + exportedIdentifierName(node.name); // Pre-declare classes and functions @@ -419,8 +417,8 @@ export class MetadataCollector { if (name) { if (!metadata) metadata = {}; // TODO(alxhub): The literal here is not valid FunctionMetadata. - metadata[name] = maybeFunc ? recordEntry(maybeFunc.func, node) : - ({ __symbolic: 'function' } as any); + metadata[name] = + maybeFunc ? recordEntry(maybeFunc.func, node) : ({__symbolic: 'function'} as any); } } break; @@ -456,7 +454,8 @@ export class MetadataCollector { operator: '+', left: { __symbolic: 'select', - expression: recordEntry({__symbolic: 'reference', name: enumName}, node), name + expression: recordEntry({__symbolic: 'reference', name: enumName}, node), + name }, } as any; } else { @@ -555,7 +554,8 @@ export class MetadataCollector { } const result: ModuleMetadata = { __symbolic: 'module', - version: this.options.version || METADATA_VERSION, metadata + version: this.options.version || METADATA_VERSION, + metadata }; if (sourceFile.moduleName) result.importAs = sourceFile.moduleName; if (exports) result.exports = exports; @@ -570,8 +570,7 @@ function validateMetadata( metadata: {[name: string]: MetadataEntry}) { let locals: Set<string> = new Set(['Array', 'Object', 'Set', 'Map', 'string', 'number', 'any']); - function validateExpression( - expression: MetadataValue | MetadataSymbolicExpression | MetadataError) { + function validateExpression(expression: MetadataValue|MetadataSymbolicExpression|MetadataError) { if (!expression) { return; } else if (Array.isArray(expression)) { @@ -648,11 +647,11 @@ function validateMetadata( } if (classData.members) { Object.getOwnPropertyNames(classData.members) - .forEach(name => classData.members ![name].forEach((m) => validateMember(classData, m))); + .forEach(name => classData.members![name].forEach((m) => validateMember(classData, m))); } if (classData.statics) { Object.getOwnPropertyNames(classData.statics).forEach(name => { - const staticMember = classData.statics ![name]; + const staticMember = classData.statics![name]; if (isFunctionMetadata(staticMember)) { validateExpression(staticMember.value); } else { @@ -675,7 +674,7 @@ function validateMetadata( } } - function shouldReportNode(node: ts.Node | undefined) { + function shouldReportNode(node: ts.Node|undefined) { if (node) { const nodeStart = node.getStart(); return !( @@ -688,12 +687,13 @@ function validateMetadata( function reportError(error: MetadataError) { const node = nodeMap.get(error); if (shouldReportNode(node)) { - const lineInfo = error.line != undefined ? - error.character != undefined ? `:${error.line + 1}:${error.character + 1}` : - `:${error.line + 1}` : - ''; - throw new Error( - `${sourceFile.fileName}${lineInfo}: Metadata collected contains an error that will be reported at runtime: ${expandedMessage(error)}.\n ${JSON.stringify(error)}`); + const lineInfo = error.line != undefined ? error.character != undefined ? + `:${error.line + 1}:${error.character + 1}` : + `:${error.line + 1}` : + ''; + throw new Error(`${sourceFile.fileName}${ + lineInfo}: Metadata collected contains an error that will be reported at runtime: ${ + expandedMessage(error)}.\n ${JSON.stringify(error)}`); } } @@ -708,8 +708,9 @@ function validateMetadata( if (shouldReportNode(node)) { if (node) { const {line, character} = sourceFile.getLineAndCharacterOfPosition(node.getStart()); - throw new Error( - `${sourceFile.fileName}:${line + 1}:${character + 1}: Error encountered in metadata generated for exported symbol '${name}': \n ${e.message}`); + throw new Error(`${sourceFile.fileName}:${line + 1}:${ + character + 1}: Error encountered in metadata generated for exported symbol '${ + name}': \n ${e.message}`); } throw new Error( `Error encountered in metadata generated for exported symbol ${name}: \n ${e.message}`); @@ -722,7 +723,7 @@ function validateMetadata( function namesOf(parameters: ts.NodeArray<ts.ParameterDeclaration>): string[] { const result: string[] = []; - function addNamesOf(name: ts.Identifier | ts.BindingPattern) { + function addNamesOf(name: ts.Identifier|ts.BindingPattern) { if (name.kind == ts.SyntaxKind.Identifier) { const identifier = <ts.Identifier>name; result.push(identifier.text); @@ -752,7 +753,8 @@ function expandedMessage(error: any): string { switch (error.message) { case 'Reference to non-exported class': if (error.context && error.context.className) { - return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`; + return `Reference to a non-exported class ${ + error.context.className}. Consider exporting the class`; } break; case 'Variable not initialized': @@ -771,7 +773,8 @@ function expandedMessage(error: any): string { 'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function'; case 'Reference to a local symbol': if (error.context && error.context.name) { - return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`; + return `Reference to a local (non-exported) symbol '${ + error.context.name}'. Consider exporting the symbol`; } } return error.message; diff --git a/packages/compiler-cli/src/metadata/evaluator.ts b/packages/compiler-cli/src/metadata/evaluator.ts index 9cf440cc71f08..4752d98211f04 100644 --- a/packages/compiler-cli/src/metadata/evaluator.ts +++ b/packages/compiler-cli/src/metadata/evaluator.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {CollectorOptions} from './collector'; -import {ClassMetadata, FunctionMetadata, InterfaceMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSourceLocationInfo, MetadataSymbolicCallExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema'; +import {ClassMetadata, FunctionMetadata, InterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSourceLocationInfo, MetadataSymbolicCallExpression, MetadataValue} from './schema'; import {Symbols} from './symbols'; @@ -46,8 +46,9 @@ export function recordMapEntry<T extends MetadataEntry>( sourceFile?: ts.SourceFile) { if (!nodeMap.has(entry)) { nodeMap.set(entry, node); - if (node && (isMetadataImportedSymbolReferenceExpression(entry) || - isMetadataImportDefaultReference(entry)) && + if (node && + (isMetadataImportedSymbolReferenceExpression(entry) || + isMetadataImportDefaultReference(entry)) && entry.line == null) { const info = sourceInfo(node, sourceFile); if (info.line != null) entry.line = info.line; @@ -88,7 +89,7 @@ export interface ImportMetadata { } -function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile { +function getSourceFileOfNode(node: ts.Node|undefined): ts.SourceFile { while (node && node.kind != ts.SyntaxKind.SourceFile) { node = node.parent; } @@ -97,7 +98,7 @@ function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile { /* @internal */ export function sourceInfo( - node: ts.Node | undefined, sourceFile: ts.SourceFile | undefined): MetadataSourceLocationInfo { + node: ts.Node|undefined, sourceFile: ts.SourceFile|undefined): MetadataSourceLocationInfo { if (node) { sourceFile = sourceFile || getSourceFileOfNode(node); if (sourceFile) { @@ -435,7 +436,7 @@ export class Evaluator { case ts.SyntaxKind.TypeReference: const typeReferenceNode = <ts.TypeReferenceNode>node; const typeNameNode = typeReferenceNode.typeName; - const getReference: (typeNameNode: ts.Identifier | ts.QualifiedName) => MetadataValue = + const getReference: (typeNameNode: ts.Identifier|ts.QualifiedName) => MetadataValue = node => { if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) { const qualifiedName = <ts.QualifiedName>node; @@ -691,6 +692,6 @@ function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment { const empty = ts.createNodeArray<any>(); -function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>| undefined): ts.NodeArray<T> { +function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>|undefined): ts.NodeArray<T> { return v || empty; } \ No newline at end of file diff --git a/packages/compiler-cli/src/metadata/schema.ts b/packages/compiler-cli/src/metadata/schema.ts index ec2ed8ad1cf68..519a8c1fd6627 100644 --- a/packages/compiler-cli/src/metadata/schema.ts +++ b/packages/compiler-cli/src/metadata/schema.ts @@ -18,7 +18,7 @@ export const METADATA_VERSION = 4; -export type MetadataEntry = ClassMetadata | InterfaceMetadata | FunctionMetadata | MetadataValue; +export type MetadataEntry = ClassMetadata|InterfaceMetadata|FunctionMetadata|MetadataValue; export interface ModuleMetadata { __symbolic: 'module'; @@ -43,18 +43,22 @@ export interface ClassMetadata { arity?: number; decorators?: (MetadataSymbolicExpression|MetadataError)[]; members?: MetadataMap; - statics?: {[name: string]: MetadataValue | FunctionMetadata}; + statics?: {[name: string]: MetadataValue|FunctionMetadata}; } export function isClassMetadata(value: any): value is ClassMetadata { return value && value.__symbolic === 'class'; } -export interface InterfaceMetadata { __symbolic: 'interface'; } +export interface InterfaceMetadata { + __symbolic: 'interface'; +} export function isInterfaceMetadata(value: any): value is InterfaceMetadata { return value && value.__symbolic === 'interface'; } -export interface MetadataMap { [name: string]: MemberMetadata[]; } +export interface MetadataMap { + [name: string]: MemberMetadata[]; +} export interface MemberMetadata { __symbolic: 'constructor'|'method'|'property'; @@ -99,24 +103,26 @@ export function isFunctionMetadata(value: any): value is FunctionMetadata { return value && value.__symbolic === 'function'; } -export type MetadataValue = string | number | boolean | undefined | null | MetadataObject | - MetadataArray | MetadataSymbolicExpression | MetadataSymbolicReferenceExpression | - MetadataSymbolicBinaryExpression | MetadataSymbolicIndexExpression | - MetadataSymbolicCallExpression | MetadataSymbolicPrefixExpression | - MetadataSymbolicIfExpression | MetadataSymbolicSpreadExpression | - MetadataSymbolicSelectExpression | MetadataError; +export type MetadataValue = string|number|boolean|undefined|null|MetadataObject|MetadataArray| + MetadataSymbolicExpression|MetadataSymbolicReferenceExpression|MetadataSymbolicBinaryExpression| + MetadataSymbolicIndexExpression|MetadataSymbolicCallExpression|MetadataSymbolicPrefixExpression| + MetadataSymbolicIfExpression|MetadataSymbolicSpreadExpression|MetadataSymbolicSelectExpression| + MetadataError; -export interface MetadataObject { [name: string]: MetadataValue; } +export interface MetadataObject { + [name: string]: MetadataValue; +} -export interface MetadataArray { [name: number]: MetadataValue; } +export interface MetadataArray { + [name: number]: MetadataValue; +} -export type MetadataSymbolicExpression = MetadataSymbolicBinaryExpression | - MetadataSymbolicIndexExpression | MetadataSymbolicIndexExpression | - MetadataSymbolicCallExpression | MetadataSymbolicCallExpression | - MetadataSymbolicPrefixExpression | MetadataSymbolicIfExpression | - MetadataGlobalReferenceExpression | MetadataModuleReferenceExpression | - MetadataImportedSymbolReferenceExpression | MetadataImportedDefaultReferenceExpression | - MetadataSymbolicSelectExpression | MetadataSymbolicSpreadExpression; +export type MetadataSymbolicExpression = MetadataSymbolicBinaryExpression| + MetadataSymbolicIndexExpression|MetadataSymbolicIndexExpression|MetadataSymbolicCallExpression| + MetadataSymbolicCallExpression|MetadataSymbolicPrefixExpression|MetadataSymbolicIfExpression| + MetadataGlobalReferenceExpression|MetadataModuleReferenceExpression| + MetadataImportedSymbolReferenceExpression|MetadataImportedDefaultReferenceExpression| + MetadataSymbolicSelectExpression|MetadataSymbolicSpreadExpression; export function isMetadataSymbolicExpression(value: any): value is MetadataSymbolicExpression { if (value) { @@ -234,18 +240,17 @@ export function isMetadataImportedSymbolReferenceExpression(value: any): export interface MetadataImportedDefaultReferenceExpression extends MetadataSourceLocationInfo { __symbolic: 'reference'; module: string; - default: - boolean; - arguments?: MetadataValue[]; + default: boolean; + arguments?: MetadataValue[]; } export function isMetadataImportDefaultReference(value: any): value is MetadataImportedDefaultReferenceExpression { return value && value.module && value.default && isMetadataSymbolicReferenceExpression(value); } -export type MetadataSymbolicReferenceExpression = MetadataGlobalReferenceExpression | - MetadataModuleReferenceExpression | MetadataImportedSymbolReferenceExpression | - MetadataImportedDefaultReferenceExpression; +export type MetadataSymbolicReferenceExpression = + MetadataGlobalReferenceExpression|MetadataModuleReferenceExpression| + MetadataImportedSymbolReferenceExpression|MetadataImportedDefaultReferenceExpression; export function isMetadataSymbolicReferenceExpression(value: any): value is MetadataSymbolicReferenceExpression { return value && value.__symbolic === 'reference'; diff --git a/packages/compiler-cli/src/metadata/symbols.ts b/packages/compiler-cli/src/metadata/symbols.ts index afd7aff8271e3..e1f6563c76b42 100644 --- a/packages/compiler-cli/src/metadata/symbols.ts +++ b/packages/compiler-cli/src/metadata/symbols.ts @@ -12,7 +12,7 @@ import {MetadataSymbolicReferenceExpression, MetadataValue} from './schema'; export class Symbols { // TODO(issue/24571): remove '!'. - private _symbols !: Map<string, MetadataValue>; + private _symbols!: Map<string, MetadataValue>; private references = new Map<string, MetadataSymbolicReferenceExpression>(); constructor(private sourceFile: ts.SourceFile) {} @@ -21,12 +21,16 @@ export class Symbols { return (preferReference && this.references.get(name)) || this.symbols.get(name); } - define(name: string, value: MetadataValue) { this.symbols.set(name, value); } + define(name: string, value: MetadataValue) { + this.symbols.set(name, value); + } defineReference(name: string, value: MetadataSymbolicReferenceExpression) { this.references.set(name, value); } - has(name: string): boolean { return this.symbols.has(name); } + has(name: string): boolean { + return this.symbols.has(name); + } private get symbols(): Map<string, MetadataValue> { let result = this._symbols; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 246b1ab0311c0..1aa775a1ebed7 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, ParseError, ParseSourceFile, ParseTemplateOptions, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler'; +import {compileComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, makeBindingParser, ParseError, ParseSourceFile, parseTemplate, ParseTemplateOptions, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {CycleAnalyzer} from '../../cycles'; @@ -15,7 +15,7 @@ import {absoluteFrom, relative} from '../../file_system'; import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; import {DependencyTracker} from '../../incremental/api'; import {IndexingContext} from '../../indexer'; -import {DirectiveMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, extractDirectiveGuards} from '../../metadata'; +import {DirectiveMeta, extractDirectiveGuards, InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; import {flattenInheritedDirectiveMetadata} from '../../metadata/src/inheritance'; import {EnumValue, PartialEvaluator} from '../../partial_evaluator'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; @@ -200,7 +200,7 @@ export class ComponentDecoratorHandler implements } else { return previous; } - }, undefined) !; + }, undefined)!; // Note that we could technically combine the `viewProvidersRequiringFactory` and @@ -211,7 +211,7 @@ export class ComponentDecoratorHandler implements let wrappedViewProviders: Expression|null = null; if (component.has('viewProviders')) { - const viewProviders = component.get('viewProviders') !; + const viewProviders = component.get('viewProviders')!; viewProvidersRequiringFactory = resolveProvidersRequiringFactory(viewProviders, this.reflector, this.evaluator); wrappedViewProviders = new WrappedNodeExpr( @@ -221,7 +221,7 @@ export class ComponentDecoratorHandler implements if (component.has('providers')) { providersRequiringFactory = resolveProvidersRequiringFactory( - component.get('providers') !, this.reflector, this.evaluator); + component.get('providers')!, this.reflector, this.evaluator); } // Parse the template. @@ -232,14 +232,14 @@ export class ComponentDecoratorHandler implements let template: ParsedTemplateWithSource; if (this.preanalyzeTemplateCache.has(node)) { // The template was parsed in preanalyze. Use it and delete it to save memory. - const preanalyzed = this.preanalyzeTemplateCache.get(node) !; + const preanalyzed = this.preanalyzeTemplateCache.get(node)!; this.preanalyzeTemplateCache.delete(node); template = preanalyzed; } else { // The template was not already parsed. Either there's a templateUrl, or an inline template. if (component.has('templateUrl')) { - const templateUrlExpr = component.get('templateUrl') !; + const templateUrlExpr = component.get('templateUrl')!; const templateUrl = this.evaluator.evaluate(templateUrlExpr); if (typeof templateUrl !== 'string') { throw new FatalDiagnosticError( @@ -303,7 +303,7 @@ export class ComponentDecoratorHandler implements let animations: Expression|null = null; if (component.has('animations')) { - animations = new WrappedNodeExpr(component.get('animations') !); + animations = new WrappedNodeExpr(component.get('animations')!); } const output: AnalysisOutput<ComponentAnalysisData> = { @@ -313,6 +313,7 @@ export class ComponentDecoratorHandler implements ...metadata, template: { nodes: template.emitNodes, + ngContentSelectors: template.ngContentSelectors, }, encapsulation, interpolation: template.interpolation, @@ -322,7 +323,8 @@ export class ComponentDecoratorHandler implements // analyzed and the full compilation scope for the component can be realized. animations, viewProviders: wrappedViewProviders, - i18nUseExternalIds: this.i18nUseExternalIds, relativeContextFilePath, + i18nUseExternalIds: this.i18nUseExternalIds, + relativeContextFilePath, }, guards: extractDirectiveGuards(node, this.reflector), metadataStmt: generateSetClassMetadataCall( @@ -334,7 +336,7 @@ export class ComponentDecoratorHandler implements }, }; if (changeDetection !== null) { - output.analysis !.meta.changeDetection = changeDetection; + output.analysis!.meta.changeDetection = changeDetection; } return output; } @@ -352,7 +354,8 @@ export class ComponentDecoratorHandler implements outputs: analysis.meta.outputs, queries: analysis.meta.queries.map(query => query.propertyName), isComponent: true, - baseClass: analysis.baseClass, ...analysis.guards, + baseClass: analysis.baseClass, + ...analysis.guards, }); this.injectableRegistry.registerInjectable(node); @@ -414,8 +417,8 @@ export class ComponentDecoratorHandler implements } for (const {name, ref} of scope.compilation.pipes) { if (!ts.isClassDeclaration(ref.node)) { - throw new Error( - `Unexpected non-class declaration ${ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`); + throw new Error(`Unexpected non-class declaration ${ + ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`); } pipes.set(name, ref as Reference<ClassDeclaration<ts.ClassDeclaration>>); } @@ -490,7 +493,7 @@ export class ComponentDecoratorHandler implements // The BoundTarget knows which directives and pipes matched the template. const usedDirectives = bound.getUsedDirectives(); - const usedPipes = bound.getUsedPipes().map(name => pipes.get(name) !); + const usedPipes = bound.getUsedPipes().map(name => pipes.get(name)!); // Scan through the directives/pipes actually used in the template and check whether any // import which needs to be generated would create a cycle. @@ -538,7 +541,7 @@ export class ComponentDecoratorHandler implements if (analysis.providersRequiringFactory !== null && analysis.meta.providers instanceof WrappedNodeExpr) { const providerDiagnostics = getProviderDiagnostics( - analysis.providersRequiringFactory, analysis.meta.providers !.node, + analysis.providersRequiringFactory, analysis.meta.providers!.node, this.injectableRegistry); diagnostics.push(...providerDiagnostics); } @@ -546,7 +549,7 @@ export class ComponentDecoratorHandler implements if (analysis.viewProvidersRequiringFactory !== null && analysis.meta.viewProviders instanceof WrappedNodeExpr) { const viewProviderDiagnostics = getProviderDiagnostics( - analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders !.node, + analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders!.node, this.injectableRegistry); diagnostics.push(...viewProviderDiagnostics); } @@ -586,7 +589,7 @@ export class ComponentDecoratorHandler implements private _resolveLiteral(decorator: Decorator): ts.ObjectLiteralExpression { if (this.literalCache.has(decorator)) { - return this.literalCache.get(decorator) !; + return this.literalCache.get(decorator)!; } if (decorator.args === null || decorator.args.length !== 1) { throw new FatalDiagnosticError( @@ -608,7 +611,7 @@ export class ComponentDecoratorHandler implements component: Map<string, ts.Expression>, field: string, enumSymbolName: string): number|null { let resolved: number|null = null; if (component.has(field)) { - const expr = component.get(field) !; + const expr = component.get(field)!; const value = this.evaluator.evaluate(expr) as any; if (value instanceof EnumValue && isAngularCoreReference(value.enumRef, enumSymbolName)) { resolved = value.resolved as number; @@ -627,7 +630,7 @@ export class ComponentDecoratorHandler implements return extraUrls.length > 0 ? extraUrls : null; } - const styleUrlsExpr = component.get('styleUrls') !; + const styleUrlsExpr = component.get('styleUrls')!; const styleUrls = this.evaluator.evaluate(styleUrlsExpr); if (!Array.isArray(styleUrls) || !styleUrls.every(url => typeof url === 'string')) { throw new FatalDiagnosticError( @@ -642,7 +645,7 @@ export class ComponentDecoratorHandler implements containingFile: string): Promise<ParsedTemplate|null> { if (component.has('templateUrl')) { // Extract the templateUrl and preload it. - const templateUrlExpr = component.get('templateUrl') !; + const templateUrlExpr = component.get('templateUrl')!; const templateUrl = this.evaluator.evaluate(templateUrlExpr); if (typeof templateUrl !== 'string') { throw new FatalDiagnosticError( @@ -702,7 +705,7 @@ export class ComponentDecoratorHandler implements ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template'); } - const templateExpr = component.get('template') !; + const templateExpr = component.get('template')!; let templateStr: string; let templateUrl: string = ''; @@ -720,7 +723,7 @@ export class ComponentDecoratorHandler implements escapedString = true; sourceMapping = { type: 'direct', - node: templateExpr as(ts.StringLiteral | ts.NoSubstitutionTemplateLiteral), + node: templateExpr as (ts.StringLiteral | ts.NoSubstitutionTemplateLiteral), }; } else { const resolvedTemplate = this.evaluator.evaluate(templateExpr); @@ -748,7 +751,7 @@ export class ComponentDecoratorHandler implements templateRange: LexerRange|undefined, escapedString: boolean): ParsedTemplate { let preserveWhitespaces: boolean = this.defaultPreserveWhitespaces; if (component.has('preserveWhitespaces')) { - const expr = component.get('preserveWhitespaces') !; + const expr = component.get('preserveWhitespaces')!; const value = this.evaluator.evaluate(expr); if (typeof value !== 'boolean') { throw new FatalDiagnosticError( @@ -759,7 +762,7 @@ export class ComponentDecoratorHandler implements let interpolation: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG; if (component.has('interpolation')) { - const expr = component.get('interpolation') !; + const expr = component.get('interpolation')!; const value = this.evaluator.evaluate(expr); if (!Array.isArray(value) || value.length !== 2 || !value.every(element => typeof element === 'string')) { @@ -767,15 +770,17 @@ export class ComponentDecoratorHandler implements ErrorCode.VALUE_HAS_WRONG_TYPE, expr, 'interpolation must be an array with 2 elements of string type'); } - interpolation = InterpolationConfig.fromArray(value as[string, string]); + interpolation = InterpolationConfig.fromArray(value as [string, string]); } - const {errors, nodes: emitNodes, styleUrls, styles} = parseTemplate(templateStr, templateUrl, { - preserveWhitespaces, - interpolationConfig: interpolation, - range: templateRange, escapedString, - enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, - }); + const {errors, nodes: emitNodes, styleUrls, styles, ngContentSelectors} = + parseTemplate(templateStr, templateUrl, { + preserveWhitespaces, + interpolationConfig: interpolation, + range: templateRange, + escapedString, + enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, + }); // Unfortunately, the primary parse of the template above may not contain accurate source map // information. If used directly, it would result in incorrect code locations in template @@ -793,7 +798,8 @@ export class ComponentDecoratorHandler implements const {nodes: diagNodes} = parseTemplate(templateStr, templateUrl, { preserveWhitespaces: true, interpolationConfig: interpolation, - range: templateRange, escapedString, + range: templateRange, + escapedString, enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, leadingTriviaChars: [], }); @@ -804,8 +810,10 @@ export class ComponentDecoratorHandler implements diagNodes, styleUrls, styles, + ngContentSelectors, errors, - template: templateStr, templateUrl, + template: templateStr, + templateUrl, isInline: component.has('template'), file: new ParseSourceFile(templateStr, templateUrl), }; @@ -817,7 +825,7 @@ export class ComponentDecoratorHandler implements } // Figure out what file is being imported. - return this.moduleResolver.resolveModule(expr.value.moduleName !, origin.fileName); + return this.moduleResolver.resolveModule(expr.value.moduleName!, origin.fileName); } private _isCyclicImport(expr: Expression, origin: ts.SourceFile): boolean { @@ -923,6 +931,11 @@ export interface ParsedTemplate { */ styles: string[]; + /** + * Any ng-content selectors extracted from the template. + */ + ngContentSelectors: string[]; + /** * Whether the template was inline. */ diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/diagnostics.ts b/packages/compiler-cli/src/ngtsc/annotations/src/diagnostics.ts index 8b2853eefb345..0b4c1da3c5ce7 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/diagnostics.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/diagnostics.ts @@ -36,9 +36,13 @@ export function getProviderDiagnostics( const contextNode = provider.getOriginForDiagnostics(providersDeclaration); diagnostics.push(makeDiagnostic( ErrorCode.UNDECORATED_PROVIDER, contextNode, - `The class '${provider.node.name.text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime. + `The class '${ + provider.node.name + .text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime. -Either add the @Injectable() decorator to '${provider.node.name.text}', or configure a different provider (such as a provider with 'useFactory'). +Either add the @Injectable() decorator to '${ + provider.node.name + .text}', or configure a different provider (such as a provider with 'useFactory'). `, [{node: provider.node, messageText: `'${provider.node.name.text}' is declared here.`}])); } @@ -52,7 +56,7 @@ export function getDirectiveDiagnostics( kind: string): ts.Diagnostic[]|null { let diagnostics: ts.Diagnostic[]|null = []; - const addDiagnostics = (more: ts.Diagnostic | ts.Diagnostic[] | null) => { + const addDiagnostics = (more: ts.Diagnostic|ts.Diagnostic[]|null) => { if (more === null) { return; } else if (diagnostics === null) { @@ -121,14 +125,16 @@ export function checkInheritanceOfDirective( function getInheritedUndecoratedCtorDiagnostic( node: ClassDeclaration, baseClass: Reference, reader: MetadataReader) { - const subclassMeta = reader.getDirectiveMetadata(new Reference(node)) !; + const subclassMeta = reader.getDirectiveMetadata(new Reference(node))!; const dirOrComp = subclassMeta.isComponent ? 'Component' : 'Directive'; const baseClassName = baseClass.debugName; return makeDiagnostic( ErrorCode.DIRECTIVE_INHERITS_UNDECORATED_CTOR, node.name, - `The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${baseClassName}, ` + + `The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${ + baseClassName}, ` + `but the latter does not have an Angular decorator of its own. Dependency injection will not be able to ` + - `resolve the parameters of ${baseClassName}'s constructor. Either add a @Directive decorator ` + + `resolve the parameters of ${ + baseClassName}'s constructor. Either add a @Directive decorator ` + `to ${baseClassName}, or add an explicit constructor to ${node.name.text}.`); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index a0caee7461317..1c86f3d0b3def 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ConstantPool, EMPTY_SOURCE_SPAN, Expression, Identifiers, ParseError, ParsedHostBindings, R3DependencyMetadata, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, WrappedNodeExpr, compileDirectiveFromMetadata, makeBindingParser, parseHostBindings, verifyHostBindings} from '@angular/compiler'; +import {compileDirectiveFromMetadata, ConstantPool, Expression, Identifiers, makeBindingParser, ParsedHostBindings, ParseError, parseHostBindings, R3DependencyMetadata, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, verifyHostBindings, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; @@ -14,14 +14,14 @@ import {DefaultImportRecorder, Reference} from '../../imports'; import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; import {extractDirectiveGuards} from '../../metadata/src/util'; import {DynamicValue, EnumValue, PartialEvaluator} from '../../partial_evaluator'; -import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, ReflectionHost, filterToMembersWithDecorator, reflectObjectLiteral} from '../../reflection'; +import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, filterToMembersWithDecorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {LocalModuleScopeRegistry} from '../../scope'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform'; import {getDirectiveDiagnostics, getProviderDiagnostics} from './diagnostics'; import {compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; -import {findAngularDecorator, getConstructorDependencies, isAngularDecorator, readBaseClass, resolveProvidersRequiringFactory, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies, wrapFunctionExpressionsInParens, wrapTypeReference} from './util'; +import {createSourceSpan, findAngularDecorator, getConstructorDependencies, isAngularDecorator, readBaseClass, resolveProvidersRequiringFactory, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies, wrapFunctionExpressionsInParens, wrapTypeReference} from './util'; const EMPTY_OBJECT: {[key: string]: string} = {}; const FIELD_DECORATORS = [ @@ -92,7 +92,7 @@ export class DirectiveDecoratorHandler implements let providersRequiringFactory: Set<Reference<ClassDeclaration>>|null = null; if (directiveResult !== undefined && directiveResult.decorator.has('providers')) { providersRequiringFactory = resolveProvidersRequiringFactory( - directiveResult.decorator.get('providers') !, this.reflector, this.evaluator); + directiveResult.decorator.get('providers')!, this.reflector, this.evaluator); } return { @@ -102,7 +102,8 @@ export class DirectiveDecoratorHandler implements node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler), baseClass: readBaseClass(node, this.reflector, this.evaluator), - guards: extractDirectiveGuards(node, this.reflector), providersRequiringFactory + guards: extractDirectiveGuards(node, this.reflector), + providersRequiringFactory } }; } @@ -120,7 +121,8 @@ export class DirectiveDecoratorHandler implements outputs: analysis.meta.outputs, queries: analysis.meta.queries.map(query => query.propertyName), isComponent: false, - baseClass: analysis.baseClass, ...analysis.guards, + baseClass: analysis.baseClass, + ...analysis.guards, }); this.injectableRegistry.registerInjectable(node); @@ -132,7 +134,7 @@ export class DirectiveDecoratorHandler implements if (analysis.providersRequiringFactory !== null && analysis.meta.providers instanceof WrappedNodeExpr) { const providerDiagnostics = getProviderDiagnostics( - analysis.providersRequiringFactory, analysis.meta.providers !.node, + analysis.providersRequiringFactory, analysis.meta.providers!.node, this.injectableRegistry); diagnostics.push(...providerDiagnostics); } @@ -176,9 +178,8 @@ export class DirectiveDecoratorHandler implements export function extractDirectiveMetadata( clazz: ClassDeclaration, decorator: Readonly<Decorator|null>, reflector: ReflectionHost, evaluator: PartialEvaluator, defaultImportRecorder: DefaultImportRecorder, isCore: boolean, - flags: HandlerFlags, annotateForClosureCompiler: boolean, - defaultSelector: string | null = - null): {decorator: Map<string, ts.Expression>, metadata: R3DirectiveMetadata}|undefined { + flags: HandlerFlags, annotateForClosureCompiler: boolean, defaultSelector: string|null = null): + {decorator: Map<string, ts.Expression>, metadata: R3DirectiveMetadata}|undefined { let directive: Map<string, ts.Expression>; if (decorator === null || decorator.args === null || decorator.args.length === 0) { directive = new Map<string, ts.Expression>(); @@ -220,9 +221,10 @@ export function extractDirectiveMetadata( // And outputs. const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', evaluator); - const outputsFromFields = parseDecoratedFields( - filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator, - resolveOutput) as{[field: string]: string}; + const outputsFromFields = + parseDecoratedFields( + filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator, + resolveOutput) as {[field: string]: string}; // Construct the list of queries. const contentChildFromFields = queriesFromFields( filterToMembersWithDecorator(decoratedElements, 'ContentChild', coreModule), reflector, @@ -244,7 +246,7 @@ export function extractDirectiveMetadata( if (directive.has('queries')) { const queriesFromDecorator = - extractQueriesFromDecorator(directive.get('queries') !, reflector, evaluator, isCore); + extractQueriesFromDecorator(directive.get('queries')!, reflector, evaluator, isCore); queries.push(...queriesFromDecorator.content); viewQueries.push(...queriesFromDecorator.view); } @@ -252,7 +254,7 @@ export function extractDirectiveMetadata( // Parse the selector. let selector = defaultSelector; if (directive.has('selector')) { - const expr = directive.get('selector') !; + const expr = directive.get('selector')!; const resolved = evaluator.evaluate(expr); if (typeof resolved !== 'string') { throw new FatalDiagnosticError( @@ -272,8 +274,8 @@ export function extractDirectiveMetadata( const providers: Expression|null = directive.has('providers') ? new WrappedNodeExpr( annotateForClosureCompiler ? - wrapFunctionExpressionsInParens(directive.get('providers') !) : - directive.get('providers') !) : + wrapFunctionExpressionsInParens(directive.get('providers')!) : + directive.get('providers')!) : null; // Determine if `ngOnChanges` is a lifecycle hook defined on the component. @@ -284,7 +286,7 @@ export function extractDirectiveMetadata( // Parse exportAs. let exportAs: string[]|null = null; if (directive.has('exportAs')) { - const expr = directive.get('exportAs') !; + const expr = directive.get('exportAs')!; const resolved = evaluator.evaluate(expr); if (typeof resolved !== 'string') { throw new FatalDiagnosticError( @@ -312,15 +314,24 @@ export function extractDirectiveMetadata( const metadata: R3DirectiveMetadata = { name: clazz.name.text, - deps: ctorDeps, host, + deps: ctorDeps, + host, lifecycle: { - usesOnChanges, + usesOnChanges, }, inputs: {...inputsFromMeta, ...inputsFromFields}, - outputs: {...outputsFromMeta, ...outputsFromFields}, queries, viewQueries, selector, - fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE), type, internalType, + outputs: {...outputsFromMeta, ...outputsFromFields}, + queries, + viewQueries, + selector, + fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE), + type, + internalType, typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0, - typeSourceSpan: EMPTY_SOURCE_SPAN, usesInheritance, exportAs, providers + typeSourceSpan: createSourceSpan(clazz.name), + usesInheritance, + exportAs, + providers }; return {decorator: directive, metadata}; } @@ -366,11 +377,11 @@ export function extractQueryMetadata( } const options = reflectObjectLiteral(optionsExpr); if (options.has('read')) { - read = new WrappedNodeExpr(options.get('read') !); + read = new WrappedNodeExpr(options.get('read')!); } if (options.has('descendants')) { - const descendantsExpr = options.get('descendants') !; + const descendantsExpr = options.get('descendants')!; const descendantsValue = evaluator.evaluate(descendantsExpr); if (typeof descendantsValue !== 'boolean') { throw new FatalDiagnosticError( @@ -381,7 +392,7 @@ export function extractQueryMetadata( } if (options.has('static')) { - const staticValue = evaluator.evaluate(options.get('static') !); + const staticValue = evaluator.evaluate(options.get('static')!); if (typeof staticValue !== 'boolean') { throw new FatalDiagnosticError( ErrorCode.VALUE_HAS_WRONG_TYPE, node, `@${name} options.static must be a boolean`); @@ -466,7 +477,7 @@ export function parseFieldArrayValue( } // Resolve the field of interest from the directive metadata to a string[]. - const expression = directive.get(field) !; + const expression = directive.get(field)!; const value = evaluator.evaluate(expression); if (!isStringArrayOrDie(value, field, expression)) { throw new FatalDiagnosticError( @@ -489,15 +500,13 @@ function parseFieldToPropertyMapping( return EMPTY_OBJECT; } - return metaValues.reduce( - (results, value) => { - // Either the value is 'field' or 'field: property'. In the first case, `property` will - // be undefined, in which case the field name should also be used as the property name. - const [field, property] = value.split(':', 2).map(str => str.trim()); - results[field] = property || field; - return results; - }, - {} as{[field: string]: string}); + return metaValues.reduce((results, value) => { + // Either the value is 'field' or 'field: property'. In the first case, `property` will + // be undefined, in which case the field name should also be used as the property name. + const [field, property] = value.split(':', 2).map(str => str.trim()); + results[field] = property || field; + return results; + }, {} as {[field: string]: string}); } /** @@ -507,33 +516,32 @@ function parseFieldToPropertyMapping( function parseDecoratedFields( fields: {member: ClassMember, decorators: Decorator[]}[], evaluator: PartialEvaluator, mapValueResolver: (publicName: string, internalName: string) => - string | [string, string]): {[field: string]: string | [string, string]} { - return fields.reduce( - (results, field) => { - const fieldName = field.member.name; - field.decorators.forEach(decorator => { - // The decorator either doesn't have an argument (@Input()) in which case the property - // name is used, or it has one argument (@Output('named')). - if (decorator.args == null || decorator.args.length === 0) { - results[fieldName] = fieldName; - } else if (decorator.args.length === 1) { - const property = evaluator.evaluate(decorator.args[0]); - if (typeof property !== 'string') { - throw new FatalDiagnosticError( - ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator), - `@${decorator.name} decorator argument must resolve to a string`); - } - results[fieldName] = mapValueResolver(property, fieldName); - } else { - // Too many arguments. - throw new FatalDiagnosticError( - ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), - `@${decorator.name} can have at most one argument, got ${decorator.args.length} argument(s)`); - } - }); - return results; - }, - {} as{[field: string]: string | [string, string]}); + string | [string, string]): {[field: string]: string|[string, string]} { + return fields.reduce((results, field) => { + const fieldName = field.member.name; + field.decorators.forEach(decorator => { + // The decorator either doesn't have an argument (@Input()) in which case the property + // name is used, or it has one argument (@Output('named')). + if (decorator.args == null || decorator.args.length === 0) { + results[fieldName] = fieldName; + } else if (decorator.args.length === 1) { + const property = evaluator.evaluate(decorator.args[0]); + if (typeof property !== 'string') { + throw new FatalDiagnosticError( + ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator), + `@${decorator.name} decorator argument must resolve to a string`); + } + results[fieldName] = mapValueResolver(property, fieldName); + } else { + // Too many arguments. + throw new FatalDiagnosticError( + ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), + `@${decorator.name} can have at most one argument, got ${ + decorator.args.length} argument(s)`); + } + }); + return results; + }, {} as {[field: string]: string | [string, string]}); } function resolveInput(publicName: string, internalName: string): [string, string] { @@ -552,7 +560,7 @@ export function queriesFromFields( const node = member.node || Decorator.nodeForError(decorator); // Throw in case of `@Input() @ContentChild('foo') foo: any`, which is not supported in Ivy - if (member.decorators !.some(v => v.name === 'Input')) { + if (member.decorators!.some(v => v.name === 'Input')) { throw new FatalDiagnosticError( ErrorCode.DECORATOR_COLLISION, node, 'Cannot combine @Input decorators with query decorators'); @@ -580,77 +588,86 @@ type StringMap<T> = { [key: string]: T; }; -export function extractHostBindings( - members: ClassMember[], evaluator: PartialEvaluator, coreModule: string | undefined, - metadata?: Map<string, ts.Expression>): ParsedHostBindings { - let hostMetadata: StringMap<string|Expression> = {}; - if (metadata && metadata.has('host')) { - const expr = metadata.get('host') !; - const hostMetaMap = evaluator.evaluate(expr); - if (!(hostMetaMap instanceof Map)) { - throw new FatalDiagnosticError( - ErrorCode.VALUE_HAS_WRONG_TYPE, expr, `Decorator host metadata must be an object`); +function evaluateHostExpressionBindings( + hostExpr: ts.Expression, evaluator: PartialEvaluator): ParsedHostBindings { + const hostMetaMap = evaluator.evaluate(hostExpr); + if (!(hostMetaMap instanceof Map)) { + throw new FatalDiagnosticError( + ErrorCode.VALUE_HAS_WRONG_TYPE, hostExpr, `Decorator host metadata must be an object`); + } + const hostMetadata: StringMap<string|Expression> = {}; + hostMetaMap.forEach((value, key) => { + // Resolve Enum references to their declared value. + if (value instanceof EnumValue) { + value = value.resolved; } - hostMetaMap.forEach((value, key) => { - // Resolve Enum references to their declared value. - if (value instanceof EnumValue) { - value = value.resolved; - } - if (typeof key !== 'string') { - throw new FatalDiagnosticError( - ErrorCode.VALUE_HAS_WRONG_TYPE, expr, - `Decorator host metadata must be a string -> string object, but found unparseable key`); - } + if (typeof key !== 'string') { + throw new FatalDiagnosticError( + ErrorCode.VALUE_HAS_WRONG_TYPE, hostExpr, + `Decorator host metadata must be a string -> string object, but found unparseable key`); + } - if (typeof value == 'string') { - hostMetadata[key] = value; - } else if (value instanceof DynamicValue) { - hostMetadata[key] = new WrappedNodeExpr(value.node as ts.Expression); - } else { - throw new FatalDiagnosticError( - ErrorCode.VALUE_HAS_WRONG_TYPE, expr, - `Decorator host metadata must be a string -> string object, but found unparseable value`); - } - }); - } + if (typeof value == 'string') { + hostMetadata[key] = value; + } else if (value instanceof DynamicValue) { + hostMetadata[key] = new WrappedNodeExpr(value.node as ts.Expression); + } else { + throw new FatalDiagnosticError( + ErrorCode.VALUE_HAS_WRONG_TYPE, hostExpr, + `Decorator host metadata must be a string -> string object, but found unparseable value`); + } + }); const bindings = parseHostBindings(hostMetadata); - // TODO: create and provide proper sourceSpan to make error message more descriptive (FW-995) - // For now, pass an incorrect (empty) but valid sourceSpan. - const errors = verifyHostBindings(bindings, EMPTY_SOURCE_SPAN); + const errors = verifyHostBindings(bindings, createSourceSpan(hostExpr)); if (errors.length > 0) { throw new FatalDiagnosticError( - // TODO: provide more granular diagnostic and output specific host expression that triggered - // an error instead of the whole host object - ErrorCode.HOST_BINDING_PARSE_ERROR, metadata !.get('host') !, + // TODO: provide more granular diagnostic and output specific host expression that + // triggered an error instead of the whole host object. + ErrorCode.HOST_BINDING_PARSE_ERROR, hostExpr, errors.map((error: ParseError) => error.msg).join('\n')); } - filterToMembersWithDecorator(members, 'HostBinding', coreModule).forEach(({member, decorators}) => { - decorators.forEach(decorator => { - let hostPropertyName: string = member.name; - if (decorator.args !== null && decorator.args.length > 0) { - if (decorator.args.length !== 1) { - throw new FatalDiagnosticError( - ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), - `@HostBinding can have at most one argument, got ${decorator.args.length} argument(s)`); - } + return bindings; +} - const resolved = evaluator.evaluate(decorator.args[0]); - if (typeof resolved !== 'string') { - throw new FatalDiagnosticError( - ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator), - `@HostBinding's argument must be a string`); - } +export function extractHostBindings( + members: ClassMember[], evaluator: PartialEvaluator, coreModule: string|undefined, + metadata?: Map<string, ts.Expression>): ParsedHostBindings { + let bindings: ParsedHostBindings; + if (metadata && metadata.has('host')) { + bindings = evaluateHostExpressionBindings(metadata.get('host')!, evaluator); + } else { + bindings = parseHostBindings({}); + } - hostPropertyName = resolved; - } + filterToMembersWithDecorator(members, 'HostBinding', coreModule) + .forEach(({member, decorators}) => { + decorators.forEach(decorator => { + let hostPropertyName: string = member.name; + if (decorator.args !== null && decorator.args.length > 0) { + if (decorator.args.length !== 1) { + throw new FatalDiagnosticError( + ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), + `@HostBinding can have at most one argument, got ${ + decorator.args.length} argument(s)`); + } - bindings.properties[hostPropertyName] = member.name; - }); - }); + const resolved = evaluator.evaluate(decorator.args[0]); + if (typeof resolved !== 'string') { + throw new FatalDiagnosticError( + ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator), + `@HostBinding's argument must be a string`); + } + + hostPropertyName = resolved; + } + + bindings.properties[hostPropertyName] = member.name; + }); + }); filterToMembersWithDecorator(members, 'HostListener', coreModule) .forEach(({member, decorators}) => { diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts b/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts index 0aa284ca7697b..e7a3308e99566 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {R3FactoryMetadata, compileFactoryFunction} from '@angular/compiler'; +import {compileFactoryFunction, R3FactoryMetadata} from '@angular/compiler'; import {CompileResult} from '../../transform'; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts index 3f67fafcad817..6578c7aebf709 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr, compileInjectable as compileIvyInjectable} from '@angular/compiler'; +import {compileInjectable as compileIvyInjectable, Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; @@ -83,7 +83,9 @@ export class InjectableDecoratorHandler implements }; } - register(node: ClassDeclaration): void { this.injectableRegistry.registerInjectable(node); } + register(node: ClassDeclaration): void { + this.injectableRegistry.registerInjectable(node); + } compile(node: ClassDeclaration, analysis: Readonly<InjectableHandlerData>): CompileResult[] { const res = compileIvyInjectable(analysis.meta); @@ -165,12 +167,12 @@ function extractInjectableMetadata( const meta = reflectObjectLiteral(metaNode); let providedIn: Expression = new LiteralExpr(null); if (meta.has('providedIn')) { - providedIn = new WrappedNodeExpr(meta.get('providedIn') !); + providedIn = new WrappedNodeExpr(meta.get('providedIn')!); } let userDeps: R3DependencyMetadata[]|undefined = undefined; if ((meta.has('useClass') || meta.has('useFactory')) && meta.has('deps')) { - const depsExpr = meta.get('deps') !; + const depsExpr = meta.get('deps')!; if (!ts.isArrayLiteralExpression(depsExpr)) { throw new FatalDiagnosticError( ErrorCode.VALUE_NOT_LITERAL, depsExpr, @@ -186,7 +188,7 @@ function extractInjectableMetadata( typeArgumentCount, internalType, providedIn, - useValue: new WrappedNodeExpr(unwrapForwardRef(meta.get('useValue') !, reflector)), + useValue: new WrappedNodeExpr(unwrapForwardRef(meta.get('useValue')!, reflector)), }; } else if (meta.has('useExisting')) { return { @@ -195,7 +197,7 @@ function extractInjectableMetadata( typeArgumentCount, internalType, providedIn, - useExisting: new WrappedNodeExpr(unwrapForwardRef(meta.get('useExisting') !, reflector)), + useExisting: new WrappedNodeExpr(unwrapForwardRef(meta.get('useExisting')!, reflector)), }; } else if (meta.has('useClass')) { return { @@ -204,19 +206,20 @@ function extractInjectableMetadata( typeArgumentCount, internalType, providedIn, - useClass: new WrappedNodeExpr(unwrapForwardRef(meta.get('useClass') !, reflector)), + useClass: new WrappedNodeExpr(unwrapForwardRef(meta.get('useClass')!, reflector)), userDeps, }; } else if (meta.has('useFactory')) { // useFactory is special - the 'deps' property must be analyzed. - const factory = new WrappedNodeExpr(meta.get('useFactory') !); + const factory = new WrappedNodeExpr(meta.get('useFactory')!); return { name, type, typeArgumentCount, internalType, providedIn, - useFactory: factory, userDeps, + useFactory: factory, + userDeps, }; } else { return {name, type, typeArgumentCount, internalType, providedIn}; @@ -274,6 +277,7 @@ function extractInjectableCtorDeps( function getDep(dep: ts.Expression, reflector: ReflectionHost): R3DependencyMetadata { const meta: R3DependencyMetadata = { token: new WrappedNodeExpr(dep), + attribute: null, host: false, resolved: R3ResolvedDependencyType.Token, optional: false, diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts index e779f8d8178fd..bca360ad5efa2 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NONE_TYPE, ReturnStatement, Statement, WrappedNodeExpr, literalMap} from '@angular/compiler'; +import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, literalMap, NONE_TYPE, ReturnStatement, Statement, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {DefaultImportRecorder} from '../../imports'; @@ -71,7 +71,7 @@ export function generateSetClassMetadataCall( duplicateDecoratedMemberNames.join(', ')); } const decoratedMembers = - classMembers.map(member => classMemberToMetadata(member.name, member.decorators !, isCore)); + classMembers.map(member => classMemberToMetadata(member.name, member.decorators!, isCore)); if (decoratedMembers.length > 0) { metaPropDecorators = ts.createObjectLiteral(decoratedMembers); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 940ef2458c399..ad66da3d1b476 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CUSTOM_ELEMENTS_SCHEMA, Expression, ExternalExpr, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NO_ERRORS_SCHEMA, R3Identifiers, R3InjectorMetadata, R3NgModuleMetadata, R3Reference, STRING_TYPE, SchemaMetadata, Statement, WrappedNodeExpr, compileInjector, compileNgModule} from '@angular/compiler'; +import {compileInjector, compileNgModule, CUSTOM_ELEMENTS_SCHEMA, Expression, ExternalExpr, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NO_ERRORS_SCHEMA, R3Identifiers, R3InjectorMetadata, R3NgModuleMetadata, R3Reference, SchemaMetadata, Statement, STRING_TYPE, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic} from '../../diagnostics'; @@ -40,7 +40,9 @@ export interface NgModuleAnalysis { providers: ts.Expression|null; } -export interface NgModuleResolution { injectorImports: Expression[]; } +export interface NgModuleResolution { + injectorImports: Expression[]; +} /** * Compiles @NgModule annotations to ngModuleDef fields. @@ -116,7 +118,7 @@ export class NgModuleDecoratorHandler implements let declarationRefs: Reference<ClassDeclaration>[] = []; let rawDeclarations: ts.Expression|null = null; if (ngModule.has('declarations')) { - rawDeclarations = ngModule.get('declarations') !; + rawDeclarations = ngModule.get('declarations')!; const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver); declarationRefs = this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations'); @@ -128,7 +130,9 @@ export class NgModuleDecoratorHandler implements diagnostics.push(makeDiagnostic( ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, - `Cannot declare '${ref.node.name.text}' in an NgModule as it's not a part of the current compilation.`, + `Cannot declare '${ + ref.node.name + .text}' in an NgModule as it's not a part of the current compilation.`, [{ node: ref.node.name, messageText: `'${ref.node.name.text}' is declared here.`, @@ -144,28 +148,28 @@ export class NgModuleDecoratorHandler implements let importRefs: Reference<ClassDeclaration>[] = []; let rawImports: ts.Expression|null = null; if (ngModule.has('imports')) { - rawImports = ngModule.get('imports') !; + rawImports = ngModule.get('imports')!; const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers); importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports'); } let exportRefs: Reference<ClassDeclaration>[] = []; let rawExports: ts.Expression|null = null; if (ngModule.has('exports')) { - rawExports = ngModule.get('exports') !; + rawExports = ngModule.get('exports')!; const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers); exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports'); this.referencesRegistry.add(node, ...exportRefs); } let bootstrapRefs: Reference<ClassDeclaration>[] = []; if (ngModule.has('bootstrap')) { - const expr = ngModule.get('bootstrap') !; + const expr = ngModule.get('bootstrap')!; const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver); bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap'); } const schemas: SchemaMetadata[] = []; if (ngModule.has('schemas')) { - const rawExpr = ngModule.get('schemas') !; + const rawExpr = ngModule.get('schemas')!; const result = this.evaluator.evaluate(rawExpr); if (!Array.isArray(result)) { throw new FatalDiagnosticError( @@ -203,7 +207,7 @@ export class NgModuleDecoratorHandler implements } const id: Expression|null = - ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id') !) : null; + ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id')!) : null; const valueContext = node.getSourceFile(); let typeContext = valueContext; @@ -220,7 +224,7 @@ export class NgModuleDecoratorHandler implements const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext)); const isForwardReference = (ref: R3Reference) => - isExpressionForwardReference(ref.value, node.name !, valueContext); + isExpressionForwardReference(ref.value, node.name!, valueContext); const containsForwardDecls = bootstrap.some(isForwardReference) || declarations.some(isForwardReference) || imports.some(isForwardReference) || exports.some(isForwardReference); @@ -244,7 +248,7 @@ export class NgModuleDecoratorHandler implements schemas: [], }; - const rawProviders = ngModule.has('providers') ? ngModule.get('providers') ! : null; + const rawProviders = ngModule.has('providers') ? ngModule.get('providers')! : null; const wrapperProviders = rawProviders !== null ? new WrappedNodeExpr( this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) : @@ -256,7 +260,7 @@ export class NgModuleDecoratorHandler implements // and pipes from the module exports. const injectorImports: WrappedNodeExpr<ts.Expression>[] = []; if (ngModule.has('imports')) { - injectorImports.push(new WrappedNodeExpr(ngModule.get('imports') !)); + injectorImports.push(new WrappedNodeExpr(ngModule.get('imports')!)); } if (this.routeAnalyzer !== null) { @@ -279,7 +283,8 @@ export class NgModuleDecoratorHandler implements schemas: schemas, mod: ngModuleDef, inj: ngInjectorDef, - declarations: declarationRefs, rawDeclarations, + declarations: declarationRefs, + rawDeclarations, imports: importRefs, exports: exportRefs, providers: rawProviders, @@ -326,7 +331,7 @@ export class NgModuleDecoratorHandler implements if (analysis.providersRequiringFactory !== null) { const providerDiagnostics = getProviderDiagnostics( - analysis.providersRequiringFactory, analysis.providers !, this.injectableRegistry); + analysis.providersRequiringFactory, analysis.providers!, this.injectableRegistry); diagnostics.push(...providerDiagnostics); } @@ -396,7 +401,7 @@ export class NgModuleDecoratorHandler implements const pipes = scope.compilation.pipes.map(pipe => this.refEmitter.emit(pipe.ref, context)); const directiveArray = new LiteralArrayExpr(directives); const pipesArray = new LiteralArrayExpr(pipes); - const declExpr = this.refEmitter.emit(decl, context) !; + const declExpr = this.refEmitter.emit(decl, context)!; const setComponentScope = new ExternalExpr(R3Identifiers.setComponentScope); const callExpr = new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]); @@ -472,8 +477,9 @@ export class NgModuleDecoratorHandler implements return null; } - const typeName = type && (ts.isIdentifier(type.typeName) && type.typeName || - ts.isQualifiedName(type.typeName) && type.typeName.right) || + const typeName = type && + (ts.isIdentifier(type.typeName) && type.typeName || + ts.isQualifiedName(type.typeName) && type.typeName.right) || null; if (typeName === null) { return null; @@ -559,7 +565,7 @@ export class NgModuleDecoratorHandler implements // Unwrap ModuleWithProviders for modules that are locally declared (and thus static // resolution was able to descend into the function and return an object literal, a Map). if (entry instanceof Map && entry.has('ngModule')) { - entry = entry.get('ngModule') !; + entry = entry.get('ngModule')!; } if (Array.isArray(entry)) { @@ -569,14 +575,16 @@ export class NgModuleDecoratorHandler implements if (!this.isClassDeclarationReference(entry)) { throw new FatalDiagnosticError( ErrorCode.VALUE_HAS_WRONG_TYPE, entry.node, - `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a class`); + `Value at position ${idx} in the NgModule.${arrayName} of ${ + className} is not a class`); } refList.push(entry); } else { // TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array. throw new FatalDiagnosticError( ErrorCode.VALUE_HAS_WRONG_TYPE, expr, - `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a reference: ${entry}`); + `Value at position ${idx} in the NgModule.${arrayName} of ${ + className} is not a reference: ${entry}`); } }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts index cc99211d165ba..60b0a058d6e34 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler'; +import {compilePipeFromMetadata, Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; @@ -79,7 +79,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan throw new FatalDiagnosticError( ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`); } - const pipeNameExpr = pipe.get('name') !; + const pipeNameExpr = pipe.get('name')!; const pipeName = this.evaluator.evaluate(pipeNameExpr); if (typeof pipeName !== 'string') { throw new FatalDiagnosticError( @@ -88,7 +88,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan let pure = true; if (pipe.has('pure')) { - const expr = pipe.get('pure') !; + const expr = pipe.get('pure')!; const pureValue = this.evaluator.evaluate(expr); if (typeof pureValue !== 'boolean') { throw new FatalDiagnosticError( @@ -103,7 +103,8 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan name, type, internalType, - typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0, pipeName, + typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0, + pipeName, deps: getValidConstructorDependencies( clazz, this.reflector, this.defaultImportRecorder, this.isCore), pure, diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts index 6cca49f67bba4..d160bdf2b562c 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {Expression, ExternalExpr, R3DependencyMetadata, R3Reference, R3ResolvedDependencyType, WrappedNodeExpr} from '@angular/compiler'; +import {Expression, ExternalExpr, LiteralExpr, ParseLocation, ParseSourceFile, ParseSourceSpan, R3DependencyMetadata, R3Reference, R3ResolvedDependencyType, ReadPropExpr, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic} from '../../diagnostics'; import {DefaultImportRecorder, ImportFlags, Reference, ReferenceEmitter} from '../../imports'; import {ForeignFunctionResolver, PartialEvaluator} from '../../partial_evaluator'; -import {ClassDeclaration, CtorParameter, Decorator, Import, ReflectionHost, TypeValueReference, isNamedClassDeclaration} from '../../reflection'; +import {ClassDeclaration, CtorParameter, Decorator, Import, isNamedClassDeclaration, ReflectionHost, TypeValueReference} from '../../reflection'; import {DeclarationData} from '../../scope'; export enum ConstructorDepErrorKind { @@ -21,8 +21,7 @@ export enum ConstructorDepErrorKind { export type ConstructorDeps = { deps: R3DependencyMetadata[]; -} | -{ +}|{ deps: null; errors: ConstructorDepError[]; }; @@ -48,11 +47,12 @@ export function getConstructorDependencies( } ctorParams.forEach((param, idx) => { let token = valueReferenceToExpression(param.typeValueReference, defaultImportRecorder); + let attribute: Expression|null = null; let optional = false, self = false, skipSelf = false, host = false; let resolved = R3ResolvedDependencyType.Token; (param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => { - const name = isCore || dec.import === null ? dec.name : dec.import !.name; + const name = isCore || dec.import === null ? dec.name : dec.import!.name; if (name === 'Inject') { if (dec.args === null || dec.args.length !== 1) { throw new FatalDiagnosticError( @@ -74,7 +74,13 @@ export function getConstructorDependencies( ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(dec), `Unexpected number of arguments to @Attribute().`); } - token = new WrappedNodeExpr(dec.args[0]); + const attributeName = dec.args[0]; + token = new WrappedNodeExpr(attributeName); + if (ts.isStringLiteralLike(attributeName)) { + attribute = new LiteralExpr(attributeName.text); + } else { + attribute = new WrappedNodeExpr(ts.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)); + } resolved = R3ResolvedDependencyType.Attribute; } else { throw new FatalDiagnosticError( @@ -90,10 +96,11 @@ export function getConstructorDependencies( if (token === null) { errors.push({ index: idx, - kind: ConstructorDepErrorKind.NO_SUITABLE_TOKEN, param, + kind: ConstructorDepErrorKind.NO_SUITABLE_TOKEN, + param, }); } else { - deps.push({token, optional, self, skipSelf, host, resolved}); + deps.push({token, attribute, optional, self, skipSelf, host, resolved}); } }); if (errors.length === 0) { @@ -115,10 +122,10 @@ export function valueReferenceToExpression( export function valueReferenceToExpression( valueRef: null, defaultImportRecorder: DefaultImportRecorder): null; export function valueReferenceToExpression( - valueRef: TypeValueReference | null, defaultImportRecorder: DefaultImportRecorder): Expression| + valueRef: TypeValueReference|null, defaultImportRecorder: DefaultImportRecorder): Expression| null; export function valueReferenceToExpression( - valueRef: TypeValueReference | null, defaultImportRecorder: DefaultImportRecorder): Expression| + valueRef: TypeValueReference|null, defaultImportRecorder: DefaultImportRecorder): Expression| null { if (valueRef === null) { return null; @@ -131,7 +138,19 @@ export function valueReferenceToExpression( return new WrappedNodeExpr(valueRef.expression); } else { // TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here. - return new ExternalExpr(valueRef as{moduleName: string, name: string}); + const ref = valueRef as { + moduleName: string; + importedName: string; + nestedPath: string[]|null; + }; + let importExpr: Expression = + new ExternalExpr({moduleName: ref.moduleName, name: ref.importedName}); + if (ref.nestedPath !== null) { + for (const property of ref.nestedPath) { + importExpr = new ReadPropExpr(importExpr, property); + } + } + return importExpr; } } @@ -141,7 +160,7 @@ export function valueReferenceToExpression( * * This is a companion function to `validateConstructorDependencies` which accepts invalid deps. */ -export function unwrapConstructorDependencies(deps: ConstructorDeps | null): R3DependencyMetadata[]| +export function unwrapConstructorDependencies(deps: ConstructorDeps|null): R3DependencyMetadata[]| 'invalid'|null { if (deps === null) { return null; @@ -169,18 +188,19 @@ export function getValidConstructorDependencies( * deps. */ export function validateConstructorDependencies( - clazz: ClassDeclaration, deps: ConstructorDeps | null): R3DependencyMetadata[]|null { + clazz: ClassDeclaration, deps: ConstructorDeps|null): R3DependencyMetadata[]|null { if (deps === null) { return null; } else if (deps.deps !== null) { return deps.deps; } else { // TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here. - const {param, index} = (deps as{errors: ConstructorDepError[]}).errors[0]; + const {param, index} = (deps as {errors: ConstructorDepError[]}).errors[0]; // There is at least one error. throw new FatalDiagnosticError( ErrorCode.PARAM_MISSING_TOKEN, param.nameNode, - `No suitable injection token for parameter '${param.name || index}' of class '${clazz.name.text}'.\n` + + `No suitable injection token for parameter '${param.name || index}' of class '${ + clazz.name.text}'.\n` + (param.typeNode !== null ? `Found ${param.typeNode.getText()}` : 'no type or decorator')); } @@ -312,8 +332,7 @@ export function forwardRefResolver( */ export function combineResolvers(resolvers: ForeignFunctionResolver[]): ForeignFunctionResolver { return (ref: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>, - args: ReadonlyArray<ts.Expression>): ts.Expression | - null => { + args: ReadonlyArray<ts.Expression>): ts.Expression|null => { for (const resolver of resolvers) { const resolved = resolver(ref, args); if (resolved !== null) { @@ -369,7 +388,8 @@ const parensWrapperTransformerFactory: ts.TransformerFactory<ts.Expression> = /** * Wraps all functions in a given expression in parentheses. This is needed to avoid problems * where Tsickle annotations added between analyse and transform phases in Angular may trigger - * automatic semicolon insertion, e.g. if a function is the expression in a `return` statement. More + * automatic semicolon insertion, e.g. if a function is the expression in a `return` statement. + * More * info can be found in Tsickle source code here: * https://github.com/angular/tsickle/blob/d7974262571c8a17d684e5ba07680e1b1993afdd/src/jsdoc_transformer.ts#L1021 * @@ -398,8 +418,8 @@ export function makeDuplicateDeclarationError( const contextNode = decl.ref.getOriginForDiagnostics(decl.rawDeclarations, decl.ngModule.name); context.push({ node: contextNode, - messageText: - `'${node.name.text}' is listed in the declarations of the NgModule '${decl.ngModule.name.text}'.`, + messageText: `'${node.name.text}' is listed in the declarations of the NgModule '${ + decl.ngModule.name.text}'.`, }); } @@ -433,7 +453,7 @@ export function resolveProvidersRequiringFactory( } else if (provider instanceof Reference) { tokenClass = provider; } else if (provider instanceof Map && provider.has('useClass') && !provider.has('deps')) { - const useExisting = provider.get('useClass') !; + const useExisting = provider.get('useClass')!; if (useExisting instanceof Reference) { tokenClass = useExisting; } @@ -467,3 +487,17 @@ export function wrapTypeReference(reflector: ReflectionHost, clazz: ClassDeclara value; return {value, type}; } + +/** Creates a ParseSourceSpan for a TypeScript node. */ +export function createSourceSpan(node: ts.Node): ParseSourceSpan { + const sf = node.getSourceFile(); + const [startOffset, endOffset] = [node.getStart(), node.getEnd()]; + const {line: startLine, character: startCol} = sf.getLineAndCharacterOfPosition(startOffset); + const {line: endLine, character: endCol} = sf.getLineAndCharacterOfPosition(endOffset); + const parseSf = new ParseSourceFile(sf.getFullText(), sf.fileName); + + // +1 because values are zero-indexed. + return new ParseSourceSpan( + new ParseLocation(parseSf, startOffset, startLine + 1, startCol + 1), + new ParseLocation(parseSf, endOffset, endLine + 1, endCol + 1)); +} diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts index 02772389e3ac2..fa952d1a60ffe 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts @@ -12,17 +12,23 @@ import {runInEachFileSystem} from '../../file_system/testing'; import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; import {PartialEvaluator} from '../../partial_evaluator'; -import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {getDeclaration, makeProgram} from '../../testing'; import {ResourceLoader} from '../src/api'; import {ComponentDecoratorHandler} from '../src/component'; export class NoopResourceLoader implements ResourceLoader { - resolve(): string { throw new Error('Not implemented.'); } + resolve(): string { + throw new Error('Not implemented.'); + } canPreload = false; - load(): string { throw new Error('Not implemented'); } - preload(): Promise<void>|undefined { throw new Error('Not implemented'); } + load(): string { + throw new Error('Not implemented'); + } + preload(): Promise<void>|undefined { + throw new Error('Not implemented'); + } } runInEachFileSystem(() => { describe('ComponentDecoratorHandler', () => { @@ -83,10 +89,12 @@ runInEachFileSystem(() => { const diag = err.toDiagnostic(); expect(diag.code).toEqual(ivyCode(ErrorCode.DECORATOR_ARG_NOT_LITERAL)); expect(diag.file.fileName.endsWith('entry.ts')).toBe(true); - expect(diag.start).toBe(detected.metadata.args ![0].getStart()); + expect(diag.start).toBe(detected.metadata.args![0].getStart()); } }); }); - function ivyCode(code: ErrorCode): number { return Number('-99' + code.valueOf()); } + function ivyCode(code: ErrorCode): number { + return Number('-99' + code.valueOf()); + } }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts index b15e85ca63048..0cd61336f10ad 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts @@ -5,21 +5,23 @@ * 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 * as ts from 'typescript'; + import {absoluteFrom} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; import {PartialEvaluator} from '../../partial_evaluator'; -import {ClassDeclaration, TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {getDeclaration, makeProgram} from '../../testing'; import {DirectiveDecoratorHandler} from '../src/directive'; runInEachFileSystem(() => { - describe('DirectiveDecoratorHandler', () => { - let _: typeof absoluteFrom; - beforeEach(() => _ = absoluteFrom); + let _: typeof absoluteFrom; + beforeEach(() => _ = absoluteFrom); + describe('DirectiveDecoratorHandler', () => { it('should use the `ReflectionHost` to detect class inheritance', () => { const {program} = makeProgram([ { @@ -40,53 +42,77 @@ runInEachFileSystem(() => { }, ]); - const checker = program.getTypeChecker(); - const reflectionHost = new TestReflectionHost(checker); - const evaluator = new PartialEvaluator(reflectionHost, checker, /* dependencyTracker */ null); - const metaReader = new LocalMetadataRegistry(); - const dtsReader = new DtsMetadataReader(checker, reflectionHost); - const scopeRegistry = new LocalModuleScopeRegistry( - metaReader, new MetadataDtsModuleScopeResolver(dtsReader, null), new ReferenceEmitter([]), - null); - const injectableRegistry = new InjectableClassRegistry(reflectionHost); - const handler = new DirectiveDecoratorHandler( - reflectionHost, evaluator, scopeRegistry, scopeRegistry, metaReader, - NOOP_DEFAULT_IMPORT_RECORDER, injectableRegistry, - /* isCore */ false, /* annotateForClosureCompiler */ false); - - const analyzeDirective = (dirName: string) => { - const DirNode = getDeclaration(program, _('/entry.ts'), dirName, isNamedClassDeclaration); - - const detected = - handler.detect(DirNode, reflectionHost.getDecoratorsOfDeclaration(DirNode)); - if (detected === undefined) { - throw new Error(`Failed to recognize @Directive (${dirName}).`); - } - - const {analysis} = handler.analyze(DirNode, detected.metadata); - if (analysis === undefined) { - throw new Error(`Failed to analyze @Directive (${dirName}).`); - } - - return analysis; - }; - - // By default, `TestReflectionHost#hasBaseClass()` returns `false`. - const analysis1 = analyzeDirective('TestDir1'); + const analysis1 = analyzeDirective(program, 'TestDir1', /*hasBaseClass*/ false); expect(analysis1.meta.usesInheritance).toBe(false); - // Tweak `TestReflectionHost#hasBaseClass()` to return true. - reflectionHost.hasBaseClassReturnValue = true; - - const analysis2 = analyzeDirective('TestDir2'); + const analysis2 = analyzeDirective(program, 'TestDir2', /*hasBaseClass*/ true); expect(analysis2.meta.usesInheritance).toBe(true); }); + + it('should record the source span of a Directive class type', () => { + const src = ` + import {Directive} from '@angular/core'; + + @Directive({selector: 'test-dir'}) + export class TestDir {} + `; + const {program} = makeProgram([ + { + name: _('/node_modules/@angular/core/index.d.ts'), + contents: 'export const Directive: any;', + }, + { + name: _('/entry.ts'), + contents: src, + }, + ]); + + const analysis = analyzeDirective(program, 'TestDir'); + const span = analysis.meta.typeSourceSpan; + expect(span.toString()).toBe('TestDir'); + expect(span.start.toString()).toContain('/entry.ts@5:22'); + expect(span.end.toString()).toContain('/entry.ts@5:29'); + }); }); // Helpers - class TestReflectionHost extends TypeScriptReflectionHost { - hasBaseClassReturnValue = false; + function analyzeDirective(program: ts.Program, dirName: string, hasBaseClass: boolean = false) { + class TestReflectionHost extends TypeScriptReflectionHost { + constructor(checker: ts.TypeChecker) { + super(checker); + } + + hasBaseClass(_class: ClassDeclaration): boolean { + return hasBaseClass; + } + } + + const checker = program.getTypeChecker(); + const reflectionHost = new TestReflectionHost(checker); + const evaluator = new PartialEvaluator(reflectionHost, checker, /*dependencyTracker*/ null); + const metaReader = new LocalMetadataRegistry(); + const dtsReader = new DtsMetadataReader(checker, reflectionHost); + const scopeRegistry = new LocalModuleScopeRegistry( + metaReader, new MetadataDtsModuleScopeResolver(dtsReader, null), new ReferenceEmitter([]), + null); + const injectableRegistry = new InjectableClassRegistry(reflectionHost); + const handler = new DirectiveDecoratorHandler( + reflectionHost, evaluator, scopeRegistry, scopeRegistry, metaReader, + NOOP_DEFAULT_IMPORT_RECORDER, injectableRegistry, /*isCore*/ false, + /*annotateForClosureCompiler*/ false); + + const DirNode = getDeclaration(program, _('/entry.ts'), dirName, isNamedClassDeclaration); + + const detected = handler.detect(DirNode, reflectionHost.getDecoratorsOfDeclaration(DirNode)); + if (detected === undefined) { + throw new Error(`Failed to recognize @Directive (${dirName}).`); + } + + const {analysis} = handler.analyze(DirNode, detected.metadata); + if (analysis === undefined) { + throw new Error(`Failed to analyze @Directive (${dirName}).`); + } - hasBaseClass(clazz: ClassDeclaration): boolean { return this.hasBaseClassReturnValue; } + return analysis; } }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts index fed17853d4e9e..104f9d48a0c2d 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts @@ -10,7 +10,7 @@ import {absoluteFrom} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../imports'; import {InjectableClassRegistry} from '../../metadata'; -import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {InjectableDecoratorHandler} from '../src/injectable'; @@ -31,7 +31,7 @@ runInEachFileSystem(() => { const diag = err.toDiagnostic(); expect(diag.code).toEqual(ngErrorCode(ErrorCode.INJECTABLE_DUPLICATE_PROV)); expect(diag.file.fileName.endsWith('entry.ts')).toBe(true); - expect(diag.start).toBe(ɵprov.nameNode !.getStart()); + expect(diag.start).toBe(ɵprov.nameNode!.getStart()); } }); @@ -43,7 +43,6 @@ runInEachFileSystem(() => { expect(res).not.toContain(jasmine.objectContaining({name: 'ɵprov'})); }); }); - }); }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts index 536bb02c4df8b..5bf1fc82f9ba5 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; + import {absoluteFrom, getSourceFileOrError} from '../../file_system'; -import {TestFile, runInEachFileSystem} from '../../file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../file_system/testing'; import {NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports'; import {TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/ng_module_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/ng_module_spec.ts index 2e89ddca33278..0495a5c03f86b 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/ng_module_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/ng_module_spec.ts @@ -14,7 +14,7 @@ import {runInEachFileSystem} from '../../file_system/testing'; import {LocalIdentifierStrategy, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; import {PartialEvaluator} from '../../partial_evaluator'; -import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {getDeclaration, makeProgram} from '../../testing'; import {NgModuleDecoratorHandler} from '../src/ng_module'; @@ -79,7 +79,7 @@ runInEachFileSystem(() => { if (detected === undefined) { return fail('Failed to recognize @NgModule'); } - const moduleDef = handler.analyze(TestModule, detected.metadata).analysis !.mod; + const moduleDef = handler.analyze(TestModule, detected.metadata).analysis!.mod; expect(getReferenceIdentifierTexts(moduleDef.declarations)).toEqual(['TestComp']); expect(getReferenceIdentifierTexts(moduleDef.exports)).toEqual(['TestComp']); diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/util_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/util_spec.ts index 95fc30ef1ccb9..6318ea4fc8d0f 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/util_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/util_spec.ts @@ -13,8 +13,9 @@ import {unwrapExpression} from '../src/util'; describe('ngtsc annotation utilities', () => { describe('unwrapExpression', () => { const obj = ts.createObjectLiteral(); - it('should pass through an ObjectLiteralExpression', - () => { expect(unwrapExpression(obj)).toBe(obj); }); + it('should pass through an ObjectLiteralExpression', () => { + expect(unwrapExpression(obj)).toBe(obj); + }); it('should unwrap an ObjectLiteralExpression in parentheses', () => { const wrapped = ts.createParen(obj); diff --git a/packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts b/packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts index fb67d80a60652..fa84da22a06e0 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts @@ -56,7 +56,7 @@ export interface ResourceHost { * core interface. */ export interface ExtendedTsCompilerHost extends ts.CompilerHost, Partial<ResourceHost>, - Partial<UnifiedModulesHost> {} + Partial<UnifiedModulesHost> {} export interface LazyRoute { route: string; diff --git a/packages/compiler-cli/src/ngtsc/core/api/src/options.ts b/packages/compiler-cli/src/ngtsc/core/api/src/options.ts index 5bcbcc67b0dba..a27ceb3b04bf4 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/options.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/options.ts @@ -36,7 +36,8 @@ export interface TestOnlyOptions { */ ivyTemplateTypeCheck?: boolean; - /** An option to enable ngtsc's internal performance tracing. + /** + * An option to enable ngtsc's internal performance tracing. * * This should be a path to a JSON file where trace information will be written. An optional 'ts:' * prefix will cause the trace to be written via the TS host instead of directly to the filesystem @@ -54,4 +55,5 @@ export interface TestOnlyOptions { * Also includes a few miscellaneous options. */ export interface NgCompilerOptions extends ts.CompilerOptions, LegacyNgcOptions, BazelAndG3Options, - NgcCompatibilityOptions, StrictTemplateOptions, TestOnlyOptions, I18nOptions, MiscOptions {} \ No newline at end of file + NgcCompatibilityOptions, StrictTemplateOptions, + TestOnlyOptions, I18nOptions, MiscOptions {} \ No newline at end of file diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 9f315059b9275..f932927066175 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -12,23 +12,23 @@ import * as ts from 'typescript'; import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from '../../annotations'; import {CycleAnalyzer, ImportGraph} from '../../cycles'; import {ErrorCode, ngErrorCode} from '../../diagnostics'; -import {ReferenceGraph, checkForPrivateExports} from '../../entry_point'; -import {LogicalFileSystem, getSourceFileOrError} from '../../file_system'; -import {AbsoluteModuleStrategy, AliasStrategy, AliasingHost, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports'; +import {checkForPrivateExports, ReferenceGraph} from '../../entry_point'; +import {getSourceFileOrError, LogicalFileSystem} from '../../file_system'; +import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports'; import {IncrementalDriver} from '../../incremental'; -import {IndexedComponent, IndexingContext, generateAnalysis} from '../../indexer'; +import {generateAnalysis, IndexedComponent, IndexingContext} from '../../indexer'; import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, MetadataReader} from '../../metadata'; import {ModuleWithProvidersScanner} from '../../modulewithproviders'; import {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf'; import {TypeScriptReflectionHost} from '../../reflection'; import {HostResourceLoader} from '../../resource'; -import {NgModuleRouteAnalyzer, entryPointKeyFor} from '../../routing'; +import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing'; import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {generatedFactoryTransform} from '../../shims'; import {ivySwitchTransform} from '../../switch'; -import {DecoratorHandler, DtsTransformRegistry, TraitCompiler, aliasTransformFactory, declarationTransformFactory, ivyTransformFactory} from '../../transform'; -import {TypeCheckContext, TypeCheckingConfig, isTemplateDiagnostic} from '../../typecheck'; +import {aliasTransformFactory, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform'; +import {isTemplateDiagnostic, TypeCheckContext, TypeCheckingConfig} from '../../typecheck'; import {getSourceFileOrNull, isDtsPath, resolveModuleName} from '../../util/src/typescript'; import {LazyRoute, NgCompilerOptions} from '../api'; @@ -191,7 +191,9 @@ export class NgCompiler { /** * Get all setup-related diagnostics for this compilation. */ - getOptionDiagnostics(): ts.Diagnostic[] { return this.constructionDiagnostics; } + getOptionDiagnostics(): ts.Diagnostic[] { + return this.constructionDiagnostics; + } /** * Get the `ts.Program` to use as a starting point when spawning a subsequent incremental @@ -202,7 +204,9 @@ export class NgCompiler { * operation, the consumer's `ts.Program` is no longer usable for starting a new incremental * compilation. `getNextProgram` retrieves the `ts.Program` which can be used instead. */ - getNextProgram(): ts.Program { return this.nextProgram; } + getNextProgram(): ts.Program { + return this.nextProgram; + } /** * Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`) @@ -262,8 +266,8 @@ export class NgCompiler { // Relative entry paths are disallowed. if (entryRoute.startsWith('.')) { - throw new Error( - `Failed to list lazy routes: Resolution of relative paths (${entryRoute}) is not supported.`); + throw new Error(`Failed to list lazy routes: Resolution of relative paths (${ + entryRoute}) is not supported.`); } // Non-relative entry paths fall into one of the following categories: @@ -349,7 +353,7 @@ export class NgCompiler { if (this.compilation === null) { this.analyzeSync(); } - return this.compilation !; + return this.compilation!; } private analyzeSync(): void { @@ -482,7 +486,7 @@ export class NgCompiler { // Execute the typeCheck phase of each decorator in the program. const prepSpan = this.perfRecorder.start('typeCheckPrep'); const ctx = new TypeCheckContext( - typeCheckingConfig, compilation.refEmitter !, compilation.reflector, host.typeCheckFile); + typeCheckingConfig, compilation.refEmitter!, compilation.reflector, host.typeCheckFile); compilation.traitCompiler.typeCheck(ctx); this.perfRecorder.stop(prepSpan); @@ -505,7 +509,7 @@ export class NgCompiler { const recordSpan = this.perfRecorder.start('recordDependencies'); const depGraph = this.incrementalDriver.depGraph; - for (const scope of this.compilation !.scopeRegistry !.getCompilationScopes()) { + for (const scope of this.compilation!.scopeRegistry!.getCompilationScopes()) { const file = scope.declaration.getSourceFile(); const ngModuleFile = scope.ngModule.getSourceFile(); @@ -517,7 +521,7 @@ export class NgCompiler { depGraph.addDependency(file, ngModuleFile); const meta = - this.compilation !.metaReader.getDirectiveMetadata(new Reference(scope.declaration)); + this.compilation!.metaReader.getDirectiveMetadata(new Reference(scope.declaration)); if (meta !== null && meta.isComponent) { // If a component's template changes, it might have affected the import graph, and thus the // remote scoping feature which is activated in the event of potential import cycles. Thus, @@ -543,12 +547,11 @@ export class NgCompiler { } private scanForMwp(sf: ts.SourceFile): void { - this.compilation !.mwpScanner.scan(sf, { + this.compilation!.mwpScanner.scan(sf, { addTypeReplacement: (node: ts.Declaration, type: Type): void => { // Only obtain the return type transform for the source file once there's a type to replace, // so that no transform is allocated when there's nothing to do. - this.compilation !.dtsTransforms !.getReturnTypeTransform(sf).addTypeReplacement( - node, type); + this.compilation!.dtsTransforms!.getReturnTypeTransform(sf).addTypeReplacement(node, type); } }); } @@ -686,9 +689,18 @@ export class NgCompiler { this.options.compileNonExportedClasses !== false, dtsTransforms); return { - isCore, traitCompiler, reflector, scopeRegistry, - dtsTransforms, exportReferenceGraph, routeAnalyzer, mwpScanner, - metaReader, defaultImportTracker, aliasingHost, refEmitter, + isCore, + traitCompiler, + reflector, + scopeRegistry, + dtsTransforms, + exportReferenceGraph, + routeAnalyzer, + mwpScanner, + metaReader, + defaultImportTracker, + aliasingHost, + refEmitter, }; } } diff --git a/packages/compiler-cli/src/ngtsc/core/src/host.ts b/packages/compiler-cli/src/ngtsc/core/src/host.ts index 90c684394c434..474e08c05fb6b 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/host.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {ErrorCode, ngErrorCode} from '../../diagnostics'; -import {FlatIndexGenerator, findFlatIndexEntryPoint} from '../../entry_point'; +import {findFlatIndexEntryPoint, FlatIndexGenerator} from '../../entry_point'; import {AbsoluteFsPath, resolve} from '../../file_system'; import {FactoryGenerator, FactoryTracker, ShimGenerator, SummaryGenerator, TypeCheckShimGenerator} from '../../shims'; import {typeCheckFilePath} from '../../typecheck'; @@ -88,8 +88,7 @@ export class DelegatingCompilerHost implements * `ExtendedTsCompilerHost` methods whenever present. */ export class NgCompilerHost extends DelegatingCompilerHost implements - RequiredCompilerHostDelegations, - ExtendedTsCompilerHost { + RequiredCompilerHostDelegations, ExtendedTsCompilerHost { readonly factoryTracker: FactoryTracker|null = null; readonly entryPoint: AbsoluteFsPath|null = null; readonly diagnostics: ts.Diagnostic[]; diff --git a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts index 493f0a8860e9b..eb8db2ed16fa2 100644 --- a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts +++ b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {FileSystem, NgtscCompilerHost, absoluteFrom as _, getFileSystem, getSourceFileOrError, setFileSystem} from '../../file_system'; +import {absoluteFrom as _, FileSystem, getFileSystem, getSourceFileOrError, NgtscCompilerHost, setFileSystem} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {NgCompilerOptions} from '../api'; import {NgCompiler} from '../src/compiler'; @@ -16,7 +16,6 @@ import {NgCompilerHost} from '../src/host'; runInEachFileSystem(() => { - describe('NgCompiler', () => { let fs: FileSystem; diff --git a/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts b/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts index 37d876c1baa48..c14642c1f8834 100644 --- a/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts +++ b/packages/compiler-cli/src/ngtsc/cycles/src/imports.ts @@ -30,7 +30,7 @@ export class ImportGraph { if (!this.map.has(sf)) { this.map.set(sf, this.scanImports(sf)); } - return this.map.get(sf) !; + return this.map.get(sf)!; } /** @@ -47,7 +47,9 @@ export class ImportGraph { return; } results.add(sf); - this.importsOf(sf).forEach(imported => { this.transitiveImportsOfHelper(imported, results); }); + this.importsOf(sf).forEach(imported => { + this.transitiveImportsOfHelper(imported, results); + }); } /** diff --git a/packages/compiler-cli/src/ngtsc/diagnostics/src/error.ts b/packages/compiler-cli/src/ngtsc/diagnostics/src/error.ts index 2454c8641828a..54b03dd5d9963 100644 --- a/packages/compiler-cli/src/ngtsc/diagnostics/src/error.ts +++ b/packages/compiler-cli/src/ngtsc/diagnostics/src/error.ts @@ -33,7 +33,8 @@ export function makeDiagnostic(code: ErrorCode, node: ts.Node, messageText: stri code: Number('-99' + code.valueOf()), file: ts.getOriginalNode(node).getSourceFile(), start: node.getStart(undefined, false), - length: node.getWidth(), messageText, + length: node.getWidth(), + messageText, }; if (relatedInfo !== undefined) { diag.relatedInformation = relatedInfo.map(info => { diff --git a/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts b/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts index fcf3b760d23a1..96d95677d3894 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts @@ -24,7 +24,9 @@ export class FlatIndexGenerator implements ShimGenerator { join(dirname(entryPoint), relativeFlatIndexPath).replace(/\.js$/, '') + '.ts'; } - recognize(fileName: string): boolean { return fileName === this.flatIndexPath; } + recognize(fileName: string): boolean { + return fileName === this.flatIndexPath; + } generate(): ts.SourceFile { const relativeEntryPoint = relativePathBetween(this.flatIndexPath, this.entryPoint); diff --git a/packages/compiler-cli/src/ngtsc/entry_point/src/private_export_checker.ts b/packages/compiler-cli/src/ngtsc/entry_point/src/private_export_checker.ts index 06e04d4cd5baf..57ea95bcec6d6 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/src/private_export_checker.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/src/private_export_checker.ts @@ -95,9 +95,11 @@ export function checkForPrivateExports( const diagnostic: ts.Diagnostic = { category: ts.DiagnosticCategory.Error, code: ngErrorCode(ErrorCode.SYMBOL_NOT_EXPORTED), - file: transitiveReference.getSourceFile(), ...getPosOfDeclaration(transitiveReference), - messageText: - `Unsupported private ${descriptor} ${name}. This ${descriptor} is visible to consumers via ${visibleVia}, but is not exported from the top-level library entrypoint.`, + file: transitiveReference.getSourceFile(), + ...getPosOfDeclaration(transitiveReference), + messageText: `Unsupported private ${descriptor} ${name}. This ${ + descriptor} is visible to consumers via ${ + visibleVia}, but is not exported from the top-level library entrypoint.`, }; diagnostics.push(diagnostic); diff --git a/packages/compiler-cli/src/ngtsc/entry_point/src/reference_graph.ts b/packages/compiler-cli/src/ngtsc/entry_point/src/reference_graph.ts index 396e90c5e9593..7691df63c3f8a 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/src/reference_graph.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/src/reference_graph.ts @@ -15,7 +15,7 @@ export class ReferenceGraph<T = ts.Declaration> { if (!this.references.has(from)) { this.references.set(from, new Set()); } - this.references.get(from) !.add(to); + this.references.get(from)!.add(to); } transitiveReferencesOf(target: T): Set<T> { @@ -47,7 +47,7 @@ export class ReferenceGraph<T = ts.Declaration> { // Look through the outgoing edges of `source`. // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762) let candidatePath: T[]|null = null; - this.references.get(source) !.forEach(edge => { + this.references.get(source)!.forEach(edge => { // Early exit if a path has already been found. if (candidatePath !== null) { return; @@ -67,7 +67,7 @@ export class ReferenceGraph<T = ts.Declaration> { private collectTransitiveReferences(set: Set<T>, decl: T): void { if (this.references.has(decl)) { // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762) - this.references.get(decl) !.forEach(ref => { + this.references.get(decl)!.forEach(ref => { if (!set.has(ref)) { set.add(ref); this.collectTransitiveReferences(set, ref); diff --git a/packages/compiler-cli/src/ngtsc/entry_point/test/entry_point_spec.ts b/packages/compiler-cli/src/ngtsc/entry_point/test/entry_point_spec.ts index 7099cb8cb64e0..ed77965149b54 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/test/entry_point_spec.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/test/entry_point_spec.ts @@ -16,9 +16,9 @@ runInEachFileSystem(() => { beforeEach(() => _ = absoluteFrom); describe('findFlatIndexEntryPoint', () => { - - it('should use the only source file if only a single one is specified', - () => { expect(findFlatIndexEntryPoint([_('/src/index.ts')])).toBe(_('/src/index.ts')); }); + it('should use the only source file if only a single one is specified', () => { + expect(findFlatIndexEntryPoint([_('/src/index.ts')])).toBe(_('/src/index.ts')); + }); it('should use the shortest source file ending with "index.ts" for multiple files', () => { expect(findFlatIndexEntryPoint([ diff --git a/packages/compiler-cli/src/ngtsc/entry_point/test/reference_graph_spec.ts b/packages/compiler-cli/src/ngtsc/entry_point/test/reference_graph_spec.ts index 78be9e7e6b620..6ed312b9425eb 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/test/reference_graph_spec.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/test/reference_graph_spec.ts @@ -12,8 +12,9 @@ import {ReferenceGraph} from '../src/reference_graph'; describe('entry_point reference graph', () => { let graph: ReferenceGraph<string>; - const refs = - (target: string) => { return Array.from(graph.transitiveReferencesOf(target)).sort(); }; + const refs = (target: string) => { + return Array.from(graph.transitiveReferencesOf(target)).sort(); + }; beforeEach(() => { graph = new ReferenceGraph(); @@ -45,6 +46,7 @@ describe('entry_point reference graph', () => { expect(graph.pathFrom('beta', 'alpha')).toEqual(['beta', 'delta', 'alpha']); }); - it('should not report a path that doesn\'t exist', - () => { expect(graph.pathFrom('gamma', 'beta')).toBeNull(); }); + it('should not report a path that doesn\'t exist', () => { + expect(graph.pathFrom('gamma', 'beta')).toBeNull(); + }); }); diff --git a/packages/compiler-cli/src/ngtsc/file_system/index.ts b/packages/compiler-cli/src/ngtsc/file_system/index.ts index 594af21923e6c..f706e913aef6f 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/index.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/index.ts @@ -5,9 +5,8 @@ * 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 */ -export {CachedFileSystem} from './src/cached_file_system'; export {NgtscCompilerHost} from './src/compiler_host'; -export {absoluteFrom, absoluteFromSourceFile, basename, dirname, getFileSystem, isRoot, join, relative, relativeFrom, resolve, setFileSystem} from './src/helpers'; +export {absoluteFrom, absoluteFromSourceFile, basename, dirname, getFileSystem, isRoot, isRooted, join, relative, relativeFrom, resolve, setFileSystem} from './src/helpers'; export {LogicalFileSystem, LogicalProjectPath} from './src/logical'; export {NodeJSFileSystem} from './src/node_js_file_system'; export {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './src/types'; diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts deleted file mode 100644 index 25e9ff81de18b..0000000000000 --- a/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts +++ /dev/null @@ -1,155 +0,0 @@ -/** - * @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 {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './types'; - - -/** - * A wrapper around `FileSystem` that caches hits to `exists()` and - * `readFile()` to improve performance. - * - * Be aware that any changes to the file system from outside of this - * class could break the cache, leaving it with stale values. - */ -export class CachedFileSystem implements FileSystem { - private existsCache = new Map<AbsoluteFsPath, boolean>(); - private readFileCache = new Map<AbsoluteFsPath, any>(); - - constructor(private delegate: FileSystem) {} - - exists(path: AbsoluteFsPath): boolean { - if (!this.existsCache.has(path)) { - this.existsCache.set(path, this.delegate.exists(path)); - } - return this.existsCache.get(path) !; - } - - invalidateCaches(path: AbsoluteFsPath) { - this.readFileCache.delete(path); - this.existsCache.delete(path); - } - - readFile(path: AbsoluteFsPath): string { - if (!this.readFileCache.has(path)) { - try { - if (this.lstat(path).isSymbolicLink()) { - // don't cache the value of a symbolic link - return this.delegate.readFile(path); - } - this.readFileCache.set(path, this.delegate.readFile(path)); - } catch (e) { - this.readFileCache.set(path, e); - } - } - const result = this.readFileCache.get(path); - if (typeof result === 'string') { - return result; - } else { - throw result; - } - } - - writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void { - this.delegate.writeFile(path, data, exclusive); - this.readFileCache.set(path, data); - this.existsCache.set(path, true); - } - - removeFile(path: AbsoluteFsPath): void { - this.delegate.removeFile(path); - this.readFileCache.delete(path); - this.existsCache.set(path, false); - } - - symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { - this.delegate.symlink(target, path); - this.existsCache.set(path, true); - } - - copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { - this.delegate.copyFile(from, to); - this.existsCache.set(to, true); - } - - moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { - this.delegate.moveFile(from, to); - - this.existsCache.set(from, false); - this.existsCache.set(to, true); - - if (this.readFileCache.has(from)) { - this.readFileCache.set(to, this.readFileCache.get(from)); - this.readFileCache.delete(from); - } else { - this.readFileCache.delete(to); - } - } - - ensureDir(path: AbsoluteFsPath): void { - this.delegate.ensureDir(path); - while (!this.isRoot(path)) { - this.existsCache.set(path, true); - path = this.dirname(path); - } - } - - removeDeep(path: AbsoluteFsPath): void { - this.delegate.removeDeep(path); - - // Clear out this directory and all its children from the `exists` cache. - for (const p of this.existsCache.keys()) { - if (p.startsWith(path)) { - this.existsCache.set(p, false); - } - } - - // Clear out this directory and all its children from the `readFile` cache. - for (const p of this.readFileCache.keys()) { - if (p.startsWith(path)) { - this.readFileCache.delete(p); - } - } - } - - - lstat(path: AbsoluteFsPath): FileStats { - const stat = this.delegate.lstat(path); - // if the `path` does not exist then `lstat` will thrown an error. - this.existsCache.set(path, true); - return stat; - } - - stat(path: AbsoluteFsPath): FileStats { - const stat = this.delegate.stat(path); - // if the `path` does not exist then `stat` will thrown an error. - this.existsCache.set(path, true); - return stat; - } - - // The following methods simply call through to the delegate. - readdir(path: AbsoluteFsPath): PathSegment[] { return this.delegate.readdir(path); } - pwd(): AbsoluteFsPath { return this.delegate.pwd(); } - chdir(path: AbsoluteFsPath): void { this.delegate.chdir(path); } - extname(path: AbsoluteFsPath|PathSegment): string { return this.delegate.extname(path); } - isCaseSensitive(): boolean { return this.delegate.isCaseSensitive(); } - isRoot(path: AbsoluteFsPath): boolean { return this.delegate.isRoot(path); } - isRooted(path: string): boolean { return this.delegate.isRooted(path); } - resolve(...paths: string[]): AbsoluteFsPath { return this.delegate.resolve(...paths); } - dirname<T extends PathString>(file: T): T { return this.delegate.dirname(file); } - join<T extends PathString>(basePath: T, ...paths: string[]): T { - return this.delegate.join(basePath, ...paths); - } - relative<T extends PathString>(from: T, to: T): PathSegment { - return this.delegate.relative(from, to); - } - basename(filePath: string, extension?: string|undefined): PathSegment { - return this.delegate.basename(filePath, extension); - } - realpath(filePath: AbsoluteFsPath): AbsoluteFsPath { return this.delegate.realpath(filePath); } - getDefaultLibLocation(): AbsoluteFsPath { return this.delegate.getDefaultLibLocation(); } - normalize<T extends PathString>(path: T): T { return this.delegate.normalize(path); } -} diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/compiler_host.ts b/packages/compiler-cli/src/ngtsc/file_system/src/compiler_host.ts index 3c7b62b26de90..40717c44f5388 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/compiler_host.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/compiler_host.ts @@ -26,7 +26,9 @@ export class NgtscCompilerHost implements ts.CompilerHost { return this.fs.join(this.getDefaultLibLocation(), ts.getDefaultLibFileName(options)); } - getDefaultLibLocation(): string { return this.fs.getDefaultLibLocation(); } + getDefaultLibLocation(): string { + return this.fs.getDefaultLibLocation(); + } writeFile( fileName: string, data: string, writeByteOrderMark: boolean, @@ -37,13 +39,17 @@ export class NgtscCompilerHost implements ts.CompilerHost { this.fs.writeFile(path, data); } - getCurrentDirectory(): string { return this.fs.pwd(); } + getCurrentDirectory(): string { + return this.fs.pwd(); + } getCanonicalFileName(fileName: string): string { return this.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(); } - useCaseSensitiveFileNames(): boolean { return this.fs.isCaseSensitive(); } + useCaseSensitiveFileNames(): boolean { + return this.fs.isCaseSensitive(); + } getNewLine(): string { switch (this.options.newLine) { diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/helpers.ts b/packages/compiler-cli/src/ngtsc/file_system/src/helpers.ts index 9cf87fedb67ab..61d676e0586f3 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/helpers.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/helpers.ts @@ -37,8 +37,8 @@ export function absoluteFromSourceFile(sf: ts.SourceFile): AbsoluteFsPath { } /** -* Convert the path `path` to a `PathSegment`, throwing an error if it's not a relative path. -*/ + * Convert the path `path` to a `PathSegment`, throwing an error if it's not a relative path. + */ export function relativeFrom(path: string): PathSegment { const normalized = normalizeSeparators(path); if (fs.isRooted(normalized)) { @@ -73,6 +73,13 @@ export function isRoot(path: AbsoluteFsPath): boolean { return fs.isRoot(path); } +/** + * Static access to `isRooted`. + */ +export function isRooted(path: string): boolean { + return fs.isRooted(path); +} + /** * Static access to `relative`. */ diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/invalid_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/src/invalid_file_system.ts index 2fcf235afe9e0..b9a9c4af215ab 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/invalid_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/invalid_file_system.ts @@ -16,32 +16,84 @@ import {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './ * the `FileSystem` under the hood. */ export class InvalidFileSystem implements FileSystem { - exists(path: AbsoluteFsPath): boolean { throw makeError(); } - readFile(path: AbsoluteFsPath): string { throw makeError(); } - writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void { throw makeError(); } - removeFile(path: AbsoluteFsPath): void { throw makeError(); } - symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { throw makeError(); } - readdir(path: AbsoluteFsPath): PathSegment[] { throw makeError(); } - lstat(path: AbsoluteFsPath): FileStats { throw makeError(); } - stat(path: AbsoluteFsPath): FileStats { throw makeError(); } - pwd(): AbsoluteFsPath { throw makeError(); } - chdir(path: AbsoluteFsPath): void { throw makeError(); } - extname(path: AbsoluteFsPath|PathSegment): string { throw makeError(); } - copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); } - moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); } - ensureDir(path: AbsoluteFsPath): void { throw makeError(); } - removeDeep(path: AbsoluteFsPath): void { throw makeError(); } - isCaseSensitive(): boolean { throw makeError(); } - resolve(...paths: string[]): AbsoluteFsPath { throw makeError(); } - dirname<T extends PathString>(file: T): T { throw makeError(); } - join<T extends PathString>(basePath: T, ...paths: string[]): T { throw makeError(); } - isRoot(path: AbsoluteFsPath): boolean { throw makeError(); } - isRooted(path: string): boolean { throw makeError(); } - relative<T extends PathString>(from: T, to: T): PathSegment { throw makeError(); } - basename(filePath: string, extension?: string): PathSegment { throw makeError(); } - realpath(filePath: AbsoluteFsPath): AbsoluteFsPath { throw makeError(); } - getDefaultLibLocation(): AbsoluteFsPath { throw makeError(); } - normalize<T extends PathString>(path: T): T { throw makeError(); } + exists(path: AbsoluteFsPath): boolean { + throw makeError(); + } + readFile(path: AbsoluteFsPath): string { + throw makeError(); + } + writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void { + throw makeError(); + } + removeFile(path: AbsoluteFsPath): void { + throw makeError(); + } + symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { + throw makeError(); + } + readdir(path: AbsoluteFsPath): PathSegment[] { + throw makeError(); + } + lstat(path: AbsoluteFsPath): FileStats { + throw makeError(); + } + stat(path: AbsoluteFsPath): FileStats { + throw makeError(); + } + pwd(): AbsoluteFsPath { + throw makeError(); + } + chdir(path: AbsoluteFsPath): void { + throw makeError(); + } + extname(path: AbsoluteFsPath|PathSegment): string { + throw makeError(); + } + copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { + throw makeError(); + } + moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { + throw makeError(); + } + ensureDir(path: AbsoluteFsPath): void { + throw makeError(); + } + removeDeep(path: AbsoluteFsPath): void { + throw makeError(); + } + isCaseSensitive(): boolean { + throw makeError(); + } + resolve(...paths: string[]): AbsoluteFsPath { + throw makeError(); + } + dirname<T extends PathString>(file: T): T { + throw makeError(); + } + join<T extends PathString>(basePath: T, ...paths: string[]): T { + throw makeError(); + } + isRoot(path: AbsoluteFsPath): boolean { + throw makeError(); + } + isRooted(path: string): boolean { + throw makeError(); + } + relative<T extends PathString>(from: T, to: T): PathSegment { + throw makeError(); + } + basename(filePath: string, extension?: string): PathSegment { + throw makeError(); + } + realpath(filePath: AbsoluteFsPath): AbsoluteFsPath { + throw makeError(); + } + getDefaultLibLocation(): AbsoluteFsPath { + throw makeError(); + } + normalize<T extends PathString>(path: T): T { + throw makeError(); + } } function makeError() { diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts b/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts index 312cd0d6d4415..15b5c24ae7df1 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts @@ -91,7 +91,7 @@ export class LogicalFileSystem { } this.cache.set(physicalFile, logicalFile); } - return this.cache.get(physicalFile) !; + return this.cache.get(physicalFile)!; } private createLogicalProjectPath(file: AbsoluteFsPath, rootDir: AbsoluteFsPath): diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/node_js_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/src/node_js_file_system.ts index b373e8e567cd9..2a1a41a7b5d24 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/node_js_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/node_js_file_system.ts @@ -17,20 +17,42 @@ import {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './ */ export class NodeJSFileSystem implements FileSystem { private _caseSensitive: boolean|undefined = undefined; - exists(path: AbsoluteFsPath): boolean { return fs.existsSync(path); } - readFile(path: AbsoluteFsPath): string { return fs.readFileSync(path, 'utf8'); } + exists(path: AbsoluteFsPath): boolean { + return fs.existsSync(path); + } + readFile(path: AbsoluteFsPath): string { + return fs.readFileSync(path, 'utf8'); + } writeFile(path: AbsoluteFsPath, data: string, exclusive: boolean = false): void { fs.writeFileSync(path, data, exclusive ? {flag: 'wx'} : undefined); } - removeFile(path: AbsoluteFsPath): void { fs.unlinkSync(path); } - symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { fs.symlinkSync(target, path); } - readdir(path: AbsoluteFsPath): PathSegment[] { return fs.readdirSync(path) as PathSegment[]; } - lstat(path: AbsoluteFsPath): FileStats { return fs.lstatSync(path); } - stat(path: AbsoluteFsPath): FileStats { return fs.statSync(path); } - pwd(): AbsoluteFsPath { return this.normalize(process.cwd()) as AbsoluteFsPath; } - chdir(dir: AbsoluteFsPath): void { process.chdir(dir); } - copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.copyFileSync(from, to); } - moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.renameSync(from, to); } + removeFile(path: AbsoluteFsPath): void { + fs.unlinkSync(path); + } + symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { + fs.symlinkSync(target, path); + } + readdir(path: AbsoluteFsPath): PathSegment[] { + return fs.readdirSync(path) as PathSegment[]; + } + lstat(path: AbsoluteFsPath): FileStats { + return fs.lstatSync(path); + } + stat(path: AbsoluteFsPath): FileStats { + return fs.statSync(path); + } + pwd(): AbsoluteFsPath { + return this.normalize(process.cwd()) as AbsoluteFsPath; + } + chdir(dir: AbsoluteFsPath): void { + process.chdir(dir); + } + copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { + fs.copyFileSync(from, to); + } + moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { + fs.renameSync(from, to); + } ensureDir(path: AbsoluteFsPath): void { const parents: AbsoluteFsPath[] = []; while (!this.isRoot(path) && !this.exists(path)) { @@ -38,10 +60,12 @@ export class NodeJSFileSystem implements FileSystem { path = this.dirname(path); } while (parents.length) { - this.safeMkdir(parents.pop() !); + this.safeMkdir(parents.pop()!); } } - removeDeep(path: AbsoluteFsPath): void { fsExtra.removeSync(path); } + removeDeep(path: AbsoluteFsPath): void { + fsExtra.removeSync(path); + } isCaseSensitive(): boolean { if (this._caseSensitive === undefined) { this._caseSensitive = this.exists(togglePathCase(__filename)); @@ -52,20 +76,30 @@ export class NodeJSFileSystem implements FileSystem { return this.normalize(p.resolve(...paths)) as AbsoluteFsPath; } - dirname<T extends string>(file: T): T { return this.normalize(p.dirname(file)) as T; } + dirname<T extends string>(file: T): T { + return this.normalize(p.dirname(file)) as T; + } join<T extends string>(basePath: T, ...paths: string[]): T { return this.normalize(p.join(basePath, ...paths)) as T; } - isRoot(path: AbsoluteFsPath): boolean { return this.dirname(path) === this.normalize(path); } - isRooted(path: string): boolean { return p.isAbsolute(path); } + isRoot(path: AbsoluteFsPath): boolean { + return this.dirname(path) === this.normalize(path); + } + isRooted(path: string): boolean { + return p.isAbsolute(path); + } relative<T extends PathString>(from: T, to: T): PathSegment { return relativeFrom(this.normalize(p.relative(from, to))); } basename(filePath: string, extension?: string): PathSegment { return p.basename(filePath, extension) as PathSegment; } - extname(path: AbsoluteFsPath|PathSegment): string { return p.extname(path); } - realpath(path: AbsoluteFsPath): AbsoluteFsPath { return this.resolve(fs.realpathSync(path)); } + extname(path: AbsoluteFsPath|PathSegment): string { + return p.extname(path); + } + realpath(path: AbsoluteFsPath): AbsoluteFsPath { + return this.resolve(fs.realpathSync(path)); + } getDefaultLibLocation(): AbsoluteFsPath { return this.resolve(require.resolve('typescript'), '..'); } diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/types.ts b/packages/compiler-cli/src/ngtsc/file_system/src/types.ts index 520b41bead75d..365de3b851795 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/types.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/types.ts @@ -12,7 +12,7 @@ * A `string` is not assignable to a `BrandedPath`, but a `BrandedPath` is assignable to a `string`. * Two `BrandedPath`s with different brands are not mutually assignable. */ -export type BrandedPath<B extends string> = string & { +export type BrandedPath<B extends string> = string&{ _brand: B; }; @@ -63,7 +63,7 @@ export interface FileSystem { normalize<T extends PathString>(path: T): T; } -export type PathString = string | AbsoluteFsPath | PathSegment; +export type PathString = string|AbsoluteFsPath|PathSegment; /** * Information about an object in the FileSystem. diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/util.ts b/packages/compiler-cli/src/ngtsc/file_system/src/util.ts index be2863e9ac146..41f6c2b391427 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/util.ts @@ -28,8 +28,8 @@ export function stripExtension(path: string): string { export function getSourceFileOrError(program: ts.Program, fileName: AbsoluteFsPath): ts.SourceFile { const sf = program.getSourceFile(fileName); if (sf === undefined) { - throw new Error( - `Program does not contain "${fileName}" - available files are ${program.getSourceFiles().map(sf => sf.fileName).join(', ')}`); + throw new Error(`Program does not contain "${fileName}" - available files are ${ + program.getSourceFiles().map(sf => sf.fileName).join(', ')}`); } return sf; } diff --git a/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts b/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts deleted file mode 100644 index cea871856e9f6..0000000000000 --- a/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts +++ /dev/null @@ -1,372 +0,0 @@ -/** - * @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 {CachedFileSystem} from '../src/cached_file_system'; -import {absoluteFrom, join, setFileSystem} from '../src/helpers'; -import {NodeJSFileSystem} from '../src/node_js_file_system'; -import {AbsoluteFsPath, FileSystem} from '../src/types'; - -describe('CachedFileSystem', () => { - let delegate: FileSystem; - let fs: CachedFileSystem; - let abcPath: AbsoluteFsPath; - let xyzPath: AbsoluteFsPath; - - beforeEach(() => { - delegate = new NodeJSFileSystem(); - fs = new CachedFileSystem(delegate); - // Set the file-system so that calls like `absoluteFrom()` - // and `PathSegment.fromFsPath()` work correctly. - setFileSystem(fs); - abcPath = absoluteFrom('/a/b/c'); - xyzPath = absoluteFrom('/x/y/z'); - }); - - describe('exists()', () => { - it('should call delegate if not in cache', () => { - const spy = spyOn(delegate, 'exists').and.returnValue(true); - expect(fs.exists(abcPath)).toBe(true); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - - it('should take from the cache if available', () => { - const spy = spyOn(delegate, 'exists').and.returnValue(true); - fs.exists(abcPath); // Call once to fill the cache - spy.calls.reset(); - - expect(fs.exists(abcPath)).toBe(true); - expect(spy).not.toHaveBeenCalled(); - }); - }); - - describe('readFile()', () => { - let lstatSpy: jasmine.Spy; - beforeEach(() => { - // For most of the tests the files are not symbolic links. - lstatSpy = spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false}); - }); - - it('should call delegate if not in cache', () => { - const spy = spyOn(delegate, 'readFile').and.returnValue('Some contents'); - expect(fs.readFile(abcPath)).toBe('Some contents'); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - - it('should take from the cache if available', () => { - const spy = spyOn(delegate, 'readFile').and.returnValue('Some contents'); - fs.readFile(abcPath); // Call once to fill the cache - spy.calls.reset(); - - expect(fs.readFile(abcPath)).toBe('Some contents'); - expect(spy).not.toHaveBeenCalled(); - }); - - it('should cache the exception if it originally threw', () => { - const spy = spyOn(delegate, 'readFile').and.throwError('Some error'); - expect(() => fs.readFile(abcPath)).toThrowError('Some error'); - spy.calls.reset(); - expect(() => fs.readFile(abcPath)).toThrowError('Some error'); - expect(spy).not.toHaveBeenCalled(); - }); - - it('should always call delegate (and not cache) if the path is a symbolic link', () => { - const readFileSpy = spyOn(delegate, 'readFile').and.returnValue('Some contents'); - // Simulate a symlink by overriding `lstat` - lstatSpy.and.returnValue({isSymbolicLink: () => true}); - - // Read the symlink target file contents - expect(fs.readFile(abcPath)).toEqual('Some contents'); - expect(lstatSpy).toHaveBeenCalledWith(abcPath); - - // Now read it again and check that the cache was not hit - lstatSpy.calls.reset(); - readFileSpy.calls.reset(); - expect(fs.readFile(abcPath)).toEqual('Some contents'); - expect(lstatSpy).toHaveBeenCalledWith(abcPath); - }); - }); - - describe('invalidateCaches()', () => { - it('should call the delegate `readFile()` if the path for the cached file has been invalidated', - () => { - spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false}); - const spy = spyOn(delegate, 'readFile').and.returnValue('Some contents'); - fs.readFile(abcPath); // Call once to fill the cache - spy.calls.reset(); - - expect(fs.readFile(abcPath)).toBe('Some contents'); - expect(spy).not.toHaveBeenCalled(); - - fs.invalidateCaches(abcPath); - - expect(fs.readFile(abcPath)).toBe('Some contents'); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - - it('should call the delegate `exists()` if the path for the cached file has been invalidated', - () => { - const spy = spyOn(delegate, 'exists').and.returnValue(true); - fs.exists(abcPath); // Call once to fill the cache - spy.calls.reset(); - - expect(fs.exists(abcPath)).toBe(true); - expect(spy).not.toHaveBeenCalled(); - - fs.invalidateCaches(abcPath); - - expect(fs.exists(abcPath)).toBe(true); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - }); - - describe('writeFile()', () => { - it('should call delegate', () => { - const spy = spyOn(delegate, 'writeFile'); - fs.writeFile(abcPath, 'Some contents'); - expect(spy).toHaveBeenCalledWith(abcPath, 'Some contents', undefined); - spy.calls.reset(); - fs.writeFile(abcPath, 'Some contents', /* exclusive */ true); - expect(spy).toHaveBeenCalledWith(abcPath, 'Some contents', true); - }); - - it('should update the exists and "readFile" caches', () => { - spyOn(delegate, 'writeFile'); - const existsSpy = spyOn(delegate, 'exists'); - const readFileSpy = spyOn(delegate, 'readFile'); - - fs.writeFile(abcPath, 'Some contents'); - expect(fs.readFile(abcPath)).toEqual('Some contents'); - expect(fs.exists(abcPath)).toBe(true); - expect(existsSpy).not.toHaveBeenCalled(); - expect(readFileSpy).not.toHaveBeenCalled(); - }); - }); - - describe('removeFile()', () => { - it('should call delegate', () => { - const spy = spyOn(delegate, 'removeFile'); - fs.removeFile(abcPath); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - - it('should update the exists cache', () => { - spyOn(delegate, 'removeFile'); - const existsSpy = spyOn(delegate, 'exists'); - - fs.removeFile(abcPath); - expect(fs.exists(abcPath)).toBe(false); - expect(existsSpy).not.toHaveBeenCalled(); - }); - }); - - describe('readdir()', () => { - it('should call delegate', () => { - const spy = spyOn(delegate, 'readdir'); - fs.readdir(abcPath); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - }); - - describe('lstat()', () => { - it('should call delegate', () => { - const spy = spyOn(delegate, 'lstat'); - fs.lstat(abcPath); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - - it('should update the "exists" cache', () => { - spyOn(delegate, 'lstat'); - const existsSpy = spyOn(delegate, 'exists'); - fs.lstat(abcPath); - expect(fs.exists(abcPath)).toBe(true); - expect(existsSpy).not.toHaveBeenCalled(); - }); - }); - - describe('stat()', () => { - it('should call delegate', () => { - const spy = spyOn(delegate, 'stat'); - fs.stat(abcPath); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - - it('should update the "exists" cache', () => { - spyOn(delegate, 'stat'); - const existsSpy = spyOn(delegate, 'exists'); - fs.stat(abcPath); - expect(fs.exists(abcPath)).toBe(true); - expect(existsSpy).not.toHaveBeenCalled(); - }); - }); - - describe('pwd()', () => { - it('should call delegate', () => { - const spy = spyOn(delegate, 'pwd'); - fs.pwd(); - expect(spy).toHaveBeenCalledWith(); - }); - }); - - describe('copyFile()', () => { - it('should call delegate', () => { - const spy = spyOn(delegate, 'copyFile'); - fs.copyFile(abcPath, xyzPath); - expect(spy).toHaveBeenCalledWith(abcPath, xyzPath); - }); - - it('should update the "exists" cache', () => { - spyOn(delegate, 'copyFile'); - const existsSpy = spyOn(delegate, 'exists').and.returnValue(false); - fs.copyFile(abcPath, xyzPath); - expect(fs.exists(xyzPath)).toEqual(true); - expect(existsSpy).not.toHaveBeenCalled(); - }); - }); - - describe('moveFile()', () => { - beforeEach(() => { - // `moveFile()` relies upon `readFile` which calls through to `lstat()`, so stub it out. - spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false}); - }); - - it('should call delegate', () => { - const spy = spyOn(delegate, 'moveFile'); - fs.moveFile(abcPath, xyzPath); - expect(spy).toHaveBeenCalledWith(abcPath, xyzPath); - }); - - it('should update the "exists" cache', () => { - spyOn(delegate, 'moveFile'); - const existsSpy = spyOn(delegate, 'exists'); - - fs.moveFile(abcPath, xyzPath); - - expect(fs.exists(abcPath)).toEqual(false); - expect(fs.exists(xyzPath)).toEqual(true); - expect(existsSpy).not.toHaveBeenCalled(); - }); - - it('should delete the `from` "readFile" cache', () => { - spyOn(delegate, 'moveFile'); - const readFileSpy = spyOn(delegate, 'readFile'); - - // Fill the abc "readFile" cache - readFileSpy.and.returnValue('abc content'); - fs.readFile(abcPath); - - // Move the file - fs.moveFile(abcPath, xyzPath); - - // Emulate an error now that the file has been moved. - readFileSpy.and.throwError('no file'); - - // Show that asking for the abc file does not read from the cache - expect(() => fs.readFile(abcPath)).toThrowError('no file'); - expect(readFileSpy).toHaveBeenCalledWith(abcPath); - }); - - it('should update the `to` "readFile" cache (if `from` was cached)', () => { - spyOn(delegate, 'moveFile'); - const readFileSpy = spyOn(delegate, 'readFile'); - - // Fill the abc "readFile" cache - readFileSpy.and.returnValue('abc content'); - fs.readFile(abcPath); - readFileSpy.calls.reset(); - - // Move the file - fs.moveFile(abcPath, xyzPath); - - // Show that the cache was hit for the xyz file - expect(fs.readFile(xyzPath)).toEqual('abc content'); - expect(readFileSpy).not.toHaveBeenCalled(); - }); - - it('should delete the `to` "readFile" cache (if `from` was not cached)', () => { - spyOn(delegate, 'moveFile'); - const readFileSpy = spyOn(delegate, 'readFile'); - - // Fill the xyz "readFile" cache - readFileSpy.and.returnValue('xyz content'); - fs.readFile(xyzPath); - readFileSpy.calls.reset(); - - // Move the file - fs.moveFile(abcPath, xyzPath); - - // Show that the cache was not hit for the xyz file - readFileSpy.and.returnValue('abc content'); - expect(fs.readFile(xyzPath)).toBe('abc content'); - expect(readFileSpy).toHaveBeenCalledWith(xyzPath); - }); - }); - - describe('ensureDir()', () => { - it('should call delegate', () => { - const ensureDirSpy = spyOn(delegate, 'ensureDir'); - fs.ensureDir(abcPath); - expect(ensureDirSpy).toHaveBeenCalledWith(abcPath); - }); - - it('should update the "exists" cache', () => { - spyOn(delegate, 'ensureDir'); - const existsSpy = spyOn(delegate, 'exists'); - fs.ensureDir(abcPath); - existsSpy.calls.reset(); - expect(fs.exists(abcPath)).toEqual(true); - expect(fs.exists(absoluteFrom('/a/b'))).toEqual(true); - expect(fs.exists(absoluteFrom('/a'))).toEqual(true); - expect(existsSpy).not.toHaveBeenCalled(); - }); - }); - - describe('removeDeep()', () => { - it('should call delegate', () => { - const spy = spyOn(delegate, 'removeDeep'); - fs.removeDeep(abcPath); - expect(spy).toHaveBeenCalledWith(abcPath); - }); - - it('should update the exists cache', () => { - spyOn(delegate, 'writeFile'); - spyOn(delegate, 'removeDeep'); - const existsSpy = spyOn(delegate, 'exists').and.returnValue(true); - expect(fs.exists(abcPath)).toBe(true); - existsSpy.calls.reset(); - - // Create a file inside `/a/b/c`. - const abcdPath = join(abcPath, 'd'); - fs.writeFile(abcdPath, 'content'); - expect(fs.exists(abcdPath)).toBe(true); - expect(existsSpy).not.toHaveBeenCalled(); - - // Remove the `/a/b/c` directory and ensure it is removed from cache (along with its content). - fs.removeDeep(abcPath); - expect(fs.exists(abcPath)).toBeFalsy(); - expect(fs.exists(abcdPath)).toBeFalsy(); - expect(existsSpy).not.toHaveBeenCalled(); - }); - - it('should update the readFile cache', () => { - spyOn(delegate, 'writeFile'); - spyOn(delegate, 'removeDeep'); - spyOn(delegate, 'lstat').and.throwError('ENOENT: no such file or directory'); - const readFileSpy = spyOn(delegate, 'readFile'); - - // Create a file inside `/a/b/c`. - const abcdPath = join(abcPath, 'd'); - fs.writeFile(abcdPath, 'content from cache'); - expect(fs.readFile(abcdPath)).toBe('content from cache'); - expect(readFileSpy).not.toHaveBeenCalled(); - - // Remove the `/a/b/c` directory and ensure it is removed from cache (along with its content). - fs.removeDeep(abcPath); - expect(() => fs.readFile(abcdPath)).toThrowError('ENOENT: no such file or directory'); - expect(() => fs.readFile(abcPath)).toThrowError('ENOENT: no such file or directory'); - }); - }); -}); diff --git a/packages/compiler-cli/src/ngtsc/file_system/test/helpers_spec.ts b/packages/compiler-cli/src/ngtsc/file_system/test/helpers_spec.ts index b14d44cc70485..6cc36eee824d1 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/test/helpers_spec.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/test/helpers_spec.ts @@ -11,37 +11,49 @@ import {absoluteFrom, relativeFrom, setFileSystem} from '../src/helpers'; import {NodeJSFileSystem} from '../src/node_js_file_system'; describe('path types', () => { - beforeEach(() => { setFileSystem(new NodeJSFileSystem()); }); + beforeEach(() => { + setFileSystem(new NodeJSFileSystem()); + }); describe('absoluteFrom', () => { - it('should not throw when creating one from an absolute path', - () => { expect(() => absoluteFrom('/test.txt')).not.toThrow(); }); + it('should not throw when creating one from an absolute path', () => { + expect(() => absoluteFrom('/test.txt')).not.toThrow(); + }); if (os.platform() === 'win32') { - it('should not throw when creating one from a windows absolute path', - () => { expect(absoluteFrom('C:\\test.txt')).toEqual('C:/test.txt'); }); + it('should not throw when creating one from a windows absolute path', () => { + expect(absoluteFrom('C:\\test.txt')).toEqual('C:/test.txt'); + }); it('should not throw when creating one from a windows absolute path with POSIX separators', - () => { expect(absoluteFrom('C:/test.txt')).toEqual('C:/test.txt'); }); - it('should support windows drive letters', - () => { expect(absoluteFrom('D:\\foo\\test.txt')).toEqual('D:/foo/test.txt'); }); - it('should convert Windows path separators to POSIX separators', - () => { expect(absoluteFrom('C:\\foo\\test.txt')).toEqual('C:/foo/test.txt'); }); + () => { + expect(absoluteFrom('C:/test.txt')).toEqual('C:/test.txt'); + }); + it('should support windows drive letters', () => { + expect(absoluteFrom('D:\\foo\\test.txt')).toEqual('D:/foo/test.txt'); + }); + it('should convert Windows path separators to POSIX separators', () => { + expect(absoluteFrom('C:\\foo\\test.txt')).toEqual('C:/foo/test.txt'); + }); } - it('should throw when creating one from a non-absolute path', - () => { expect(() => absoluteFrom('test.txt')).toThrow(); }); + it('should throw when creating one from a non-absolute path', () => { + expect(() => absoluteFrom('test.txt')).toThrow(); + }); }); describe('relativeFrom', () => { - it('should not throw when creating one from a relative path', - () => { expect(() => relativeFrom('a/b/c.txt')).not.toThrow(); }); + it('should not throw when creating one from a relative path', () => { + expect(() => relativeFrom('a/b/c.txt')).not.toThrow(); + }); - it('should throw when creating one from an absolute path', - () => { expect(() => relativeFrom('/a/b/c.txt')).toThrow(); }); + it('should throw when creating one from an absolute path', () => { + expect(() => relativeFrom('/a/b/c.txt')).toThrow(); + }); if (os.platform() === 'win32') { - it('should throw when creating one from a Windows absolute path', - () => { expect(() => relativeFrom('C:/a/b/c.txt')).toThrow(); }); + it('should throw when creating one from a Windows absolute path', () => { + expect(() => relativeFrom('C:/a/b/c.txt')).toThrow(); + }); } }); }); diff --git a/packages/compiler-cli/src/ngtsc/file_system/test/node_js_file_system_spec.ts b/packages/compiler-cli/src/ngtsc/file_system/test/node_js_file_system_spec.ts index a2c0adc236a1f..eca3261239d1c 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/test/node_js_file_system_spec.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/test/node_js_file_system_spec.ts @@ -65,10 +65,12 @@ describe('NodeJSFileSystem', () => { describe('readdir()', () => { it('should delegate to fs.readdirSync()', () => { - const spy = spyOn(realFs, 'readdirSync').and.returnValue(['x', 'y/z']); + const spy = spyOn(realFs, 'readdirSync').and.returnValue(['x', 'y/z'] as any); const result = fs.readdir(abcPath); expect(result).toEqual([relativeFrom('x'), relativeFrom('y/z')]); - expect(spy).toHaveBeenCalledWith(abcPath); + // TODO: @JiaLiPassion need to wait for @types/jasmine update to handle optional parameters. + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486 + expect(spy as any).toHaveBeenCalledWith(abcPath); }); }); @@ -88,7 +90,9 @@ describe('NodeJSFileSystem', () => { const spy = spyOn(realFs, 'statSync').and.returnValue(stats); const result = fs.stat(abcPath); expect(result).toBe(stats); - expect(spy).toHaveBeenCalledWith(abcPath); + // TODO: @JiaLiPassion need to wait for @types/jasmine update to handle optional parameters. + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486 + expect(spy as any).toHaveBeenCalledWith(abcPath); }); }); @@ -125,7 +129,7 @@ describe('NodeJSFileSystem', () => { const xyPath = absoluteFrom('/x/y'); const mkdirCalls: string[] = []; const existsCalls: string[] = []; - spyOn(realFs, 'mkdirSync').and.callFake((path: string) => mkdirCalls.push(path)); + spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => mkdirCalls.push(path)) as any); spyOn(fs, 'exists').and.callFake((path: AbsoluteFsPath) => { existsCalls.push(path); switch (path) { @@ -171,12 +175,14 @@ describe('NodeJSFileSystem', () => { } return false; }); - spyOn(fs, 'stat').and.returnValue({isDirectory: () => true}); - const mkdirSyncSpy = spyOn(realFs, 'mkdirSync').and.callFake((path: string) => { - if (path === abcPath) { - throw new Error('It exists already. Supposedly.'); - } - }); + spyOn(fs, 'stat').and.returnValue({isDirectory: () => true} as any); + const mkdirSyncSpy = + spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => { + if (path === abcPath) { + throw new Error( + 'It exists already. Supposedly.'); + } + }) as any); fs.ensureDir(abcPath); expect(mkdirSyncSpy).toHaveBeenCalledTimes(3); @@ -186,11 +192,12 @@ describe('NodeJSFileSystem', () => { it('should fail if creating the directory throws and the directory does not exist', () => { spyOn(fs, 'exists').and.returnValue(false); - spyOn(realFs, 'mkdirSync').and.callFake((path: string) => { - if (path === abcPath) { - throw new Error('Unable to create directory (for whatever reason).'); - } - }); + spyOn(realFs, 'mkdirSync') + .and.callFake(((path: string) => { + if (path === abcPath) { + throw new Error('Unable to create directory (for whatever reason).'); + } + }) as any); expect(() => fs.ensureDir(abcPath)) .toThrowError('Unable to create directory (for whatever reason).'); @@ -210,12 +217,12 @@ describe('NodeJSFileSystem', () => { } return false; }); - spyOn(fs, 'stat').and.returnValue({isDirectory: isDirectorySpy}); - spyOn(realFs, 'mkdirSync').and.callFake((path: string) => { - if (path === abcPath) { - throw new Error('It exists already. Supposedly.'); - } - }); + spyOn(fs, 'stat').and.returnValue({isDirectory: isDirectorySpy} as any); + spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => { + if (path === abcPath) { + throw new Error('It exists already. Supposedly.'); + } + }) as any); expect(() => fs.ensureDir(abcPath)).toThrowError('It exists already. Supposedly.'); expect(isDirectorySpy).toHaveBeenCalledTimes(1); diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/index.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/index.ts index c41ab26f7b929..b5fb9f1084865 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/index.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/index.ts @@ -10,4 +10,4 @@ export {Folder, MockFileSystem} from './src/mock_file_system'; export {MockFileSystemNative} from './src/mock_file_system_native'; export {MockFileSystemPosix} from './src/mock_file_system_posix'; export {MockFileSystemWindows} from './src/mock_file_system_windows'; -export {TestFile, initMockFileSystem, runInEachFileSystem} from './src/test_helper'; +export {initMockFileSystem, runInEachFileSystem, TestFile} from './src/test_helper'; diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts index cf3c6258782dd..7731f5fea6c4d 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts @@ -21,9 +21,13 @@ export abstract class MockFileSystem implements FileSystem { this._cwd = this.normalize(cwd); } - isCaseSensitive() { return this._isCaseSensitive; } + isCaseSensitive() { + return this._isCaseSensitive; + } - exists(path: AbsoluteFsPath): boolean { return this.findFromPath(path).entity !== null; } + exists(path: AbsoluteFsPath): boolean { + return this.findFromPath(path).entity !== null; + } readFile(path: AbsoluteFsPath): string { const {entity} = this.findFromPath(path); @@ -142,7 +146,9 @@ export abstract class MockFileSystem implements FileSystem { delete entity[basename]; } - isRoot(path: AbsoluteFsPath): boolean { return this.dirname(path) === path; } + isRoot(path: AbsoluteFsPath): boolean { + return this.dirname(path) === path; + } extname(path: AbsoluteFsPath|PathSegment): string { const match = /.+(\.[^.]*)$/.exec(path); @@ -159,9 +165,13 @@ export abstract class MockFileSystem implements FileSystem { } } - pwd(): AbsoluteFsPath { return this._cwd; } + pwd(): AbsoluteFsPath { + return this._cwd; + } - chdir(path: AbsoluteFsPath): void { this._cwd = this.normalize(path); } + chdir(path: AbsoluteFsPath): void { + this._cwd = this.normalize(path); + } getDefaultLibLocation(): AbsoluteFsPath { // Mimic the node module resolution algorithm and start in the current directory, then look @@ -201,8 +211,12 @@ export abstract class MockFileSystem implements FileSystem { abstract normalize<T extends PathString>(path: T): T; protected abstract splitPath<T extends PathString>(path: T): string[]; - dump(): Folder { return cloneFolder(this._fileTree); } - init(folder: Folder): void { this._fileTree = cloneFolder(folder); } + dump(): Folder { + return cloneFolder(this._fileTree); + } + init(folder: Folder): void { + this._fileTree = cloneFolder(folder); + } protected findFromPath(path: AbsoluteFsPath, options?: {followSymLinks: boolean}): FindResult { const followSymLinks = !!options && options.followSymLinks; @@ -215,7 +229,7 @@ export abstract class MockFileSystem implements FileSystem { segments[0] = ''; let current: Entity|null = this._fileTree; while (segments.length) { - current = current[segments.shift() !]; + current = current[segments.shift()!]; if (current === undefined) { return {path, entity: null}; } @@ -239,7 +253,7 @@ export abstract class MockFileSystem implements FileSystem { protected splitIntoFolderAndFile(path: AbsoluteFsPath): [AbsoluteFsPath, string] { const segments = this.splitPath(path); - const file = segments.pop() !; + const file = segments.pop()!; return [path.substring(0, path.length - file.length - 1) as AbsoluteFsPath, file]; } } @@ -247,8 +261,10 @@ export interface FindResult { path: AbsoluteFsPath; entity: Entity|null; } -export type Entity = Folder | File | SymLink; -export interface Folder { [pathSegments: string]: Entity; } +export type Entity = Folder|File|SymLink; +export interface Folder { + [pathSegments: string]: Entity; +} export type File = string; export class SymLink { constructor(public path: AbsoluteFsPath) {} @@ -256,24 +272,32 @@ export class SymLink { class MockFileStats implements FileStats { constructor(private entity: Entity) {} - isFile(): boolean { return isFile(this.entity); } - isDirectory(): boolean { return isFolder(this.entity); } - isSymbolicLink(): boolean { return isSymLink(this.entity); } + isFile(): boolean { + return isFile(this.entity); + } + isDirectory(): boolean { + return isFolder(this.entity); + } + isSymbolicLink(): boolean { + return isSymLink(this.entity); + } } class MockFileSystemError extends Error { - constructor(public code: string, public path: string, message: string) { super(message); } + constructor(public code: string, public path: string, message: string) { + super(message); + } } -export function isFile(item: Entity | null): item is File { +export function isFile(item: Entity|null): item is File { return typeof item === 'string'; } -export function isSymLink(item: Entity | null): item is SymLink { +export function isSymLink(item: Entity|null): item is SymLink { return item instanceof SymLink; } -export function isFolder(item: Entity | null): item is Folder { +export function isFolder(item: Entity|null): item is Folder { return item !== null && !isFile(item) && !isSymLink(item); } diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_native.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_native.ts index f4539fb048fb8..da97516e24f63 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_native.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_native.ts @@ -15,7 +15,9 @@ import {MockFileSystem} from './mock_file_system'; const isWindows = os.platform() === 'win32'; export class MockFileSystemNative extends MockFileSystem { - constructor(cwd: AbsoluteFsPath = '/' as AbsoluteFsPath) { super(undefined, cwd); } + constructor(cwd: AbsoluteFsPath = '/' as AbsoluteFsPath) { + super(undefined, cwd); + } // Delegate to the real NodeJSFileSystem for these path related methods @@ -36,9 +38,13 @@ export class MockFileSystemNative extends MockFileSystem { return NodeJSFileSystem.prototype.basename.call(this, filePath, extension); } - isCaseSensitive() { return NodeJSFileSystem.prototype.isCaseSensitive.call(this); } + isCaseSensitive() { + return NodeJSFileSystem.prototype.isCaseSensitive.call(this); + } - isRooted(path: string): boolean { return NodeJSFileSystem.prototype.isRooted.call(this, path); } + isRooted(path: string): boolean { + return NodeJSFileSystem.prototype.isRooted.call(this, path); + } isRoot(path: AbsoluteFsPath): boolean { return NodeJSFileSystem.prototype.isRoot.call(this, path); @@ -57,5 +63,7 @@ export class MockFileSystemNative extends MockFileSystem { return NodeJSFileSystem.prototype.normalize.call(this, path) as T; } - protected splitPath<T>(path: string): string[] { return path.split(/[\\\/]/); } + protected splitPath<T>(path: string): string[] { + return path.split(/[\\\/]/); + } } diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_posix.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_posix.ts index 2778997240758..74c528cad2cf1 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_posix.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_posix.ts @@ -17,7 +17,9 @@ export class MockFileSystemPosix extends MockFileSystem { return this.normalize(resolved) as AbsoluteFsPath; } - dirname<T extends string>(file: T): T { return this.normalize(p.posix.dirname(file)) as T; } + dirname<T extends string>(file: T): T { + return this.normalize(p.posix.dirname(file)) as T; + } join<T extends string>(basePath: T, ...paths: string[]): T { return this.normalize(p.posix.join(basePath, ...paths)) as T; @@ -31,9 +33,13 @@ export class MockFileSystemPosix extends MockFileSystem { return p.posix.basename(filePath, extension) as PathSegment; } - isRooted(path: string): boolean { return path.startsWith('/'); } + isRooted(path: string): boolean { + return path.startsWith('/'); + } - protected splitPath<T extends PathString>(path: T): string[] { return path.split('/'); } + protected splitPath<T extends PathString>(path: T): string[] { + return path.split('/'); + } normalize<T extends PathString>(path: T): T { return path.replace(/^[a-z]:\//i, '/').replace(/\\/g, '/') as T; diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_windows.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_windows.ts index 3ec01f4ed82f3..9cabd0a2b9a43 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_windows.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system_windows.ts @@ -17,7 +17,9 @@ export class MockFileSystemWindows extends MockFileSystem { return this.normalize(resolved as AbsoluteFsPath); } - dirname<T extends string>(path: T): T { return this.normalize(p.win32.dirname(path) as T); } + dirname<T extends string>(path: T): T { + return this.normalize(p.win32.dirname(path) as T); + } join<T extends string>(basePath: T, ...paths: string[]): T { return this.normalize(p.win32.join(basePath, ...paths)) as T; @@ -31,9 +33,13 @@ export class MockFileSystemWindows extends MockFileSystem { return p.win32.basename(filePath, extension) as PathSegment; } - isRooted(path: string): boolean { return /^([A-Z]:)?([\\\/]|$)/i.test(path); } + isRooted(path: string): boolean { + return /^([A-Z]:)?([\\\/]|$)/i.test(path); + } - protected splitPath<T extends PathString>(path: T): string[] { return path.split(/[\\\/]/); } + protected splitPath<T extends PathString>(path: T): string[] { + return path.split(/[\\\/]/); + } normalize<T extends PathString>(path: T): T { return path.replace(/^[\/\\]/i, 'C:/').replace(/\\/g, '/') as T; diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/test_helper.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/test_helper.ts index f9ff163cc9678..2272d55b3b4f6 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/test_helper.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/test_helper.ts @@ -47,7 +47,9 @@ function runInFileSystem(os: string, callback: (os: string) => void, error: bool afterEach(() => setFileSystem(new InvalidFileSystem())); callback(os); if (error) { - afterAll(() => { throw new Error(`runInFileSystem limited to ${os}, cannot pass`); }); + afterAll(() => { + throw new Error(`runInFileSystem limited to ${os}, cannot pass`); + }); } }); } @@ -125,14 +127,16 @@ function monkeyPatchTypeScript(os: string, fs: MockFileSystem) { return {files, directories}; } - function realPath(path: string): string { return fs.realpath(fs.resolve(path)); } + function realPath(path: string): string { + return fs.realpath(fs.resolve(path)); + } // Rather than completely re-implementing we are using the `ts.matchFiles` function, // which is internal to the `ts` namespace. const tsMatchFiles: ( - path: string, extensions: ReadonlyArray<string>| undefined, - excludes: ReadonlyArray<string>| undefined, includes: ReadonlyArray<string>| undefined, - useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, + path: string, extensions: ReadonlyArray<string>|undefined, + excludes: ReadonlyArray<string>|undefined, includes: ReadonlyArray<string>|undefined, + useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number|undefined, getFileSystemEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string) => string[] = (ts as any).matchFiles; diff --git a/packages/compiler-cli/src/ngtsc/imports/index.ts b/packages/compiler-cli/src/ngtsc/imports/index.ts index d43beaaa30803..858f77c7e3e89 100644 --- a/packages/compiler-cli/src/ngtsc/imports/index.ts +++ b/packages/compiler-cli/src/ngtsc/imports/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -export {AliasStrategy, AliasingHost, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias'; +export {AliasingHost, AliasStrategy, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias'; export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core'; export {DefaultImportRecorder, DefaultImportTracker, NOOP_DEFAULT_IMPORT_RECORDER} from './src/default'; export {AbsoluteModuleStrategy, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter'; diff --git a/packages/compiler-cli/src/ngtsc/imports/src/alias.ts b/packages/compiler-cli/src/ngtsc/imports/src/alias.ts index 6795705e2f3a5..94b4a1144446c 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/alias.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/alias.ts @@ -10,7 +10,7 @@ import {Expression, ExternalExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {UnifiedModulesHost} from '../../core/api'; -import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost} from '../../reflection'; import {ImportFlags, ReferenceEmitStrategy} from './emitter'; import {Reference} from './references'; @@ -203,7 +203,9 @@ export class PrivateExportAliasingHost implements AliasingHost { * * Thus, `getAliasIn` always returns `null`. */ - getAliasIn(): null { return null; } + getAliasIn(): null { + return null; + } } /** diff --git a/packages/compiler-cli/src/ngtsc/imports/src/core.ts b/packages/compiler-cli/src/ngtsc/imports/src/core.ts index a20b0c3097b90..027f814978c53 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/core.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/core.ts @@ -36,11 +36,17 @@ export interface ImportRewriter { * `ImportRewriter` that does no rewriting. */ export class NoopImportRewriter implements ImportRewriter { - shouldImportSymbol(symbol: string, specifier: string): boolean { return true; } + shouldImportSymbol(symbol: string, specifier: string): boolean { + return true; + } - rewriteSymbol(symbol: string, specifier: string): string { return symbol; } + rewriteSymbol(symbol: string, specifier: string): string { + return symbol; + } - rewriteSpecifier(specifier: string, inContextOfFile: string): string { return specifier; } + rewriteSpecifier(specifier: string, inContextOfFile: string): string { + return specifier; + } } /** @@ -70,7 +76,9 @@ const CORE_MODULE = '@angular/core'; export class R3SymbolsImportRewriter implements ImportRewriter { constructor(private r3SymbolsPath: string) {} - shouldImportSymbol(symbol: string, specifier: string): boolean { return true; } + shouldImportSymbol(symbol: string, specifier: string): boolean { + return true; + } rewriteSymbol(symbol: string, specifier: string): string { if (specifier !== CORE_MODULE) { @@ -89,8 +97,8 @@ export class R3SymbolsImportRewriter implements ImportRewriter { const relativePathToR3Symbols = relativePathBetween(inContextOfFile, this.r3SymbolsPath); if (relativePathToR3Symbols === null) { - throw new Error( - `Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${this.r3SymbolsPath}`); + throw new Error(`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${ + this.r3SymbolsPath}`); } return relativePathToR3Symbols; @@ -101,5 +109,5 @@ export function validateAndRewriteCoreSymbol(name: string): string { if (!CORE_SUPPORTED_SYMBOLS.has(name)) { throw new Error(`Importing unexpected symbol ${name} while compiling ${CORE_MODULE}`); } - return CORE_SUPPORTED_SYMBOLS.get(name) !; + return CORE_SUPPORTED_SYMBOLS.get(name)!; } diff --git a/packages/compiler-cli/src/ngtsc/imports/src/default.ts b/packages/compiler-cli/src/ngtsc/imports/src/default.ts index aa36af2eb78c8..ae92d05eb6fd2 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/default.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/default.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 * as ts from 'typescript'; @@ -96,7 +96,7 @@ export class DefaultImportTracker implements DefaultImportRecorder { if (!this.sourceFileToImportMap.has(sf)) { this.sourceFileToImportMap.set(sf, new Map<ts.Identifier, ts.ImportDeclaration>()); } - this.sourceFileToImportMap.get(sf) !.set(id, decl); + this.sourceFileToImportMap.get(sf)!.set(id, decl); } recordUsedIdentifier(id: ts.Identifier): void { @@ -105,18 +105,18 @@ export class DefaultImportTracker implements DefaultImportRecorder { // The identifier's source file has no registered default imports at all. return; } - const identiferToDeclaration = this.sourceFileToImportMap.get(sf) !; + const identiferToDeclaration = this.sourceFileToImportMap.get(sf)!; if (!identiferToDeclaration.has(id)) { // The identifier isn't from a registered default import. return; } - const decl = identiferToDeclaration.get(id) !; + const decl = identiferToDeclaration.get(id)!; // Add the default import declaration to the set of used import declarations for the file. if (!this.sourceFileToUsedImports.has(sf)) { this.sourceFileToUsedImports.set(sf, new Set<ts.ImportDeclaration>()); } - this.sourceFileToUsedImports.get(sf) !.add(decl); + this.sourceFileToUsedImports.get(sf)!.add(decl); } /** @@ -127,7 +127,9 @@ export class DefaultImportTracker implements DefaultImportRecorder { */ importPreservingTransformer(): ts.TransformerFactory<ts.SourceFile> { return (context: ts.TransformationContext) => { - return (sf: ts.SourceFile) => { return this.transformSourceFile(sf); }; + return (sf: ts.SourceFile) => { + return this.transformSourceFile(sf); + }; }; } @@ -142,7 +144,7 @@ export class DefaultImportTracker implements DefaultImportRecorder { } // There are declarations that need to be preserved. - const importsToPreserve = this.sourceFileToUsedImports.get(originalSf) !; + const importsToPreserve = this.sourceFileToUsedImports.get(originalSf)!; // Generate a new statement list which preserves any imports present in `importsToPreserve`. const statements = sf.statements.map(stmt => { diff --git a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts index 6515cf49aa428..23206dcc261fb 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/emitter.ts @@ -9,7 +9,7 @@ import {Expression, ExternalExpr, ExternalReference, WrappedNodeExpr} from '@ang import * as ts from 'typescript'; import {UnifiedModulesHost} from '../../core/api'; -import {LogicalFileSystem, LogicalProjectPath, PathSegment, absoluteFromSourceFile, dirname, relative} from '../../file_system'; +import {absoluteFromSourceFile, dirname, LogicalFileSystem, LogicalProjectPath, PathSegment, relative} from '../../file_system'; import {stripExtension} from '../../file_system/src/util'; import {ReflectionHost} from '../../reflection'; import {getSourceFile, isDeclaration, isTypeDeclaration, nodeNameForError} from '../../util/src/typescript'; @@ -93,8 +93,8 @@ export class ReferenceEmitter { return emitted; } } - throw new Error( - `Unable to write a reference to ${nodeNameForError(ref.node)} in ${ref.node.getSourceFile().fileName} from ${context.fileName}`); + throw new Error(`Unable to write a reference to ${nodeNameForError(ref.node)} in ${ + ref.node.getSourceFile().fileName} from ${context.fileName}`); } } @@ -149,11 +149,11 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { return null; } else if (!isDeclaration(ref.node)) { // It's not possible to import something which isn't a declaration. - throw new Error( - `Debug assert: unable to import a Reference to non-declaration of type ${ts.SyntaxKind[ref.node.kind]}.`); + throw new Error(`Debug assert: unable to import a Reference to non-declaration of type ${ + ts.SyntaxKind[ref.node.kind]}.`); } else if ((importFlags & ImportFlags.AllowTypeImports) === 0 && isTypeDeclaration(ref.node)) { - throw new Error( - `Importing a type-only declaration of type ${ts.SyntaxKind[ref.node.kind]} in a value position is not allowed.`); + throw new Error(`Importing a type-only declaration of type ${ + ts.SyntaxKind[ref.node.kind]} in a value position is not allowed.`); } // Try to find the exported name of the declaration, if one is available. @@ -162,8 +162,9 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { if (symbolName === null) { // TODO(alxhub): make this error a ts.Diagnostic pointing at whatever caused this import to be // triggered. - throw new Error( - `Symbol ${ref.debugName} declared in ${getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${context.fileName})`); + throw new Error(`Symbol ${ref.debugName} declared in ${ + getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${ + context.fileName})`); } return new ExternalExpr(new ExternalReference(specifier, symbolName)); @@ -173,7 +174,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { |null { const exports = this.getExportsOfModule(moduleName, fromFile); if (exports !== null && exports.has(target)) { - return exports.get(target) !; + return exports.get(target)!; } else { return null; } @@ -184,7 +185,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy { if (!this.moduleExportsCache.has(moduleName)) { this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile)); } - return this.moduleExportsCache.get(moduleName) !; + return this.moduleExportsCache.get(moduleName)!; } protected enumerateExportsOfModule(specifier: string, fromFile: string): diff --git a/packages/compiler-cli/src/ngtsc/imports/src/references.ts b/packages/compiler-cli/src/ngtsc/imports/src/references.ts index 8b1df442ccd24..f6150394b3dd3 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/references.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/references.ts @@ -80,7 +80,9 @@ export class Reference<T extends ts.Node = ts.Node> { * * See `bestGuessOwningModule`. */ - get hasOwningModuleGuess(): boolean { return this.bestGuessOwningModule !== null; } + get hasOwningModuleGuess(): boolean { + return this.bestGuessOwningModule !== null; + } /** * A name for the node, if one is available. @@ -93,14 +95,18 @@ export class Reference<T extends ts.Node = ts.Node> { return id !== null ? id.text : null; } - get alias(): Expression|null { return this._alias; } + get alias(): Expression|null { + return this._alias; + } /** * Record a `ts.Identifier` by which it's valid to refer to this node, within the context of this * `Reference`. */ - addIdentifier(identifier: ts.Identifier): void { this.identifiers.push(identifier); } + addIdentifier(identifier: ts.Identifier): void { + this.identifiers.push(identifier); + } /** * Get a `ts.Identifier` within this `Reference` that can be used to refer within the context of a diff --git a/packages/compiler-cli/src/ngtsc/imports/test/default_spec.ts b/packages/compiler-cli/src/ngtsc/imports/test/default_spec.ts index defa93505a1a1..8cfc4bd1c7667 100644 --- a/packages/compiler-cli/src/ngtsc/imports/test/default_spec.ts +++ b/packages/compiler-cli/src/ngtsc/imports/test/default_spec.ts @@ -39,7 +39,7 @@ runInEachFileSystem(() => { module: ts.ModuleKind.ES2015, }); const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause); - const fooId = fooClause.name !; + const fooId = fooClause.name!; const fooDecl = fooClause.parent; const tracker = new DefaultImportTracker(); @@ -48,7 +48,7 @@ runInEachFileSystem(() => { program.emit(undefined, undefined, undefined, undefined, { before: [tracker.importPreservingTransformer()], }); - const testContents = host.readFile('/test.js') !; + const testContents = host.readFile('/test.js')!; expect(testContents).toContain(`import Foo from './dep';`); // The control should have the import elided. @@ -69,7 +69,7 @@ runInEachFileSystem(() => { module: ts.ModuleKind.CommonJS, }); const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause); - const fooId = ts.updateIdentifier(fooClause.name !); + const fooId = ts.updateIdentifier(fooClause.name!); const fooDecl = fooClause.parent; const tracker = new DefaultImportTracker(); @@ -81,7 +81,7 @@ runInEachFileSystem(() => { tracker.importPreservingTransformer(), ], }); - const testContents = host.readFile('/test.js') !; + const testContents = host.readFile('/test.js')!; expect(testContents).toContain(`var dep_1 = require("./dep");`); expect(testContents).toContain(`var ref = dep_1["default"];`); }); diff --git a/packages/compiler-cli/src/ngtsc/imports/test/emitter_spec.ts b/packages/compiler-cli/src/ngtsc/imports/test/emitter_spec.ts index 567c6143fad95..eb6bc7ac53b1a 100644 --- a/packages/compiler-cli/src/ngtsc/imports/test/emitter_spec.ts +++ b/packages/compiler-cli/src/ngtsc/imports/test/emitter_spec.ts @@ -8,8 +8,8 @@ import {ExternalExpr} from '@angular/compiler'; import * as ts from 'typescript'; -import {LogicalFileSystem, absoluteFrom as _} from '../../file_system'; -import {TestFile, runInEachFileSystem} from '../../file_system/testing'; +import {absoluteFrom as _, LogicalFileSystem} from '../../file_system'; +import {runInEachFileSystem, TestFile} from '../../file_system/testing'; import {Declaration, TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {AbsoluteModuleStrategy, ImportFlags, LogicalProjectStrategy} from '../src/emitter'; @@ -42,7 +42,7 @@ runInEachFileSystem(() => { ]); const decl = getDeclaration(program, _('/node_modules/external.d.ts'), 'Foo', ts.isClassDeclaration); - const context = program.getSourceFile(_('/context.ts')) !; + const context = program.getSourceFile(_('/context.ts'))!; const reference = new Reference(decl); const emitted = strategy.emit(reference, context, ImportFlags.None); @@ -65,7 +65,7 @@ runInEachFileSystem(() => { ]); const decl = getDeclaration(program, _('/node_modules/external.d.ts'), 'Foo', ts.isClassDeclaration); - const context = program.getSourceFile(_('/context.ts')) !; + const context = program.getSourceFile(_('/context.ts'))!; const reference = new Reference(decl, { specifier: 'external', @@ -92,7 +92,7 @@ runInEachFileSystem(() => { ]); const decl = getDeclaration( program, _('/node_modules/external.d.ts'), 'Foo', ts.isInterfaceDeclaration); - const context = program.getSourceFile(_('/context.ts')) !; + const context = program.getSourceFile(_('/context.ts'))!; const reference = new Reference(decl, { specifier: 'external', @@ -116,7 +116,7 @@ runInEachFileSystem(() => { ]); const decl = getDeclaration( program, _('/node_modules/external.d.ts'), 'Foo', ts.isInterfaceDeclaration); - const context = program.getSourceFile(_('/context.ts')) !; + const context = program.getSourceFile(_('/context.ts'))!; const reference = new Reference(decl, {specifier: 'external', resolutionContext: context.fileName}); @@ -139,7 +139,9 @@ runInEachFileSystem(() => { return null; } const fakeExports = new Map<string, Declaration>(); - realExports.forEach((decl, name) => { fakeExports.set(`test${name}`, decl); }); + realExports.forEach((decl, name) => { + fakeExports.set(`test${name}`, decl); + }); return fakeExports; } } @@ -158,12 +160,12 @@ runInEachFileSystem(() => { const logicalFs = new LogicalFileSystem([_('/')]); const strategy = new LogicalProjectStrategy(new TestHost(checker), logicalFs); const decl = getDeclaration(program, _('/index.ts'), 'Foo', ts.isClassDeclaration); - const context = program.getSourceFile(_('/context.ts')) !; + const context = program.getSourceFile(_('/context.ts'))!; const ref = strategy.emit(new Reference(decl), context); expect(ref).not.toBeNull(); // Expect the prefixed name from the TestHost. - expect((ref !as ExternalExpr).value.name).toEqual('testFoo'); + expect((ref! as ExternalExpr).value.name).toEqual('testFoo'); }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/incremental/api.ts b/packages/compiler-cli/src/ngtsc/incremental/api.ts index e987cd59ec0c6..41c3f61bbf67c 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/api.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/api.ts @@ -25,7 +25,7 @@ export interface IncrementalBuild<W> { /** * Tracks dependencies between source files or resources in the application. */ -export interface DependencyTracker<T extends{fileName: string} = ts.SourceFile> { +export interface DependencyTracker<T extends {fileName: string} = ts.SourceFile> { /** * Record that the file `from` depends on the file `on`. */ diff --git a/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts b/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts index fdd2f8ce1ca77..0c42a13824fe2 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/src/dependency_tracking.ts @@ -23,11 +23,13 @@ import {DependencyTracker} from '../api'; * 2. One of its dependencies has physically changed. * 3. One of its resource dependencies has physically changed. */ -export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> implements +export class FileDependencyGraph<T extends {fileName: string} = ts.SourceFile> implements DependencyTracker<T> { private nodes = new Map<T, FileNode>(); - addDependency(from: T, on: T): void { this.nodeFor(from).dependsOn.add(on.fileName); } + addDependency(from: T, on: T): void { + this.nodeFor(from).dependsOn.add(on.fileName); + } addResourceDependency(from: T, resource: AbsoluteFsPath): void { this.nodeFor(from).usesResources.add(resource); @@ -103,7 +105,7 @@ export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> im usesResources: new Set<AbsoluteFsPath>(), }); } - return this.nodes.get(sf) !; + return this.nodes.get(sf)!; } } @@ -111,7 +113,7 @@ export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> im * Determine whether `sf` has logically changed, given its dependencies and the set of physically * changed files and resources. */ -function isLogicallyChanged<T extends{fileName: string}>( +function isLogicallyChanged<T extends {fileName: string}>( sf: T, node: FileNode, changedTsPaths: ReadonlySet<string>, deletedTsPaths: ReadonlySet<string>, changedResources: ReadonlySet<AbsoluteFsPath>): boolean { // A file is logically changed if it has physically changed itself (including being deleted). diff --git a/packages/compiler-cli/src/ngtsc/incremental/src/state.ts b/packages/compiler-cli/src/ngtsc/incremental/src/state.ts index 21f3ef680b366..f2497cbb87dab 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/src/state.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/src/state.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom} from '../../file_system'; +import {absoluteFrom, AbsoluteFsPath} from '../../file_system'; import {ClassRecord, TraitCompiler} from '../../transform'; import {IncrementalBuild} from '../api'; @@ -194,9 +194,13 @@ export class IncrementalDriver implements IncrementalBuild<ClassRecord> { }; } - recordSuccessfulEmit(sf: ts.SourceFile): void { this.state.pendingEmit.delete(sf.fileName); } + recordSuccessfulEmit(sf: ts.SourceFile): void { + this.state.pendingEmit.delete(sf.fileName); + } - safeToSkipEmit(sf: ts.SourceFile): boolean { return !this.state.pendingEmit.has(sf.fileName); } + safeToSkipEmit(sf: ts.SourceFile): boolean { + return !this.state.pendingEmit.has(sf.fileName); + } priorWorkFor(sf: ts.SourceFile): ClassRecord[]|null { if (this.state.lastGood === null || this.logicalChanges === null) { @@ -212,7 +216,7 @@ export class IncrementalDriver implements IncrementalBuild<ClassRecord> { } } -type BuildState = PendingBuildState | AnalyzedBuildState; +type BuildState = PendingBuildState|AnalyzedBuildState; enum BuildStateKind { Pending, diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/api.ts b/packages/compiler-cli/src/ngtsc/indexer/src/api.ts index 2f6095d499216..0227717709c31 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/api.ts @@ -43,13 +43,19 @@ interface ExpressionIdentifier extends TemplateIdentifier { } /** Describes a property accessed in a template. */ -export interface PropertyIdentifier extends ExpressionIdentifier { kind: IdentifierKind.Property; } +export interface PropertyIdentifier extends ExpressionIdentifier { + kind: IdentifierKind.Property; +} /** Describes a method accessed in a template. */ -export interface MethodIdentifier extends ExpressionIdentifier { kind: IdentifierKind.Method; } +export interface MethodIdentifier extends ExpressionIdentifier { + kind: IdentifierKind.Method; +} /** Describes an element attribute in a template. */ -export interface AttributeIdentifier extends TemplateIdentifier { kind: IdentifierKind.Attribute; } +export interface AttributeIdentifier extends TemplateIdentifier { + kind: IdentifierKind.Attribute; +} /** A reference to a directive node and its selector. */ interface DirectiveReference { @@ -85,7 +91,7 @@ export interface ReferenceIdentifier extends TemplateIdentifier { /** The target of this reference. If the target is not known, this is `null`. */ target: { /** The template AST node that the reference targets. */ - node: ElementIdentifier | TemplateIdentifier; + node: ElementIdentifier|TemplateIdentifier; /** * The directive on `node` that the reference targets. If no directive is targeted, this is @@ -96,14 +102,16 @@ export interface ReferenceIdentifier extends TemplateIdentifier { } /** Describes a template variable like "foo" in `<div *ngFor="let foo of foos"></div>`. */ -export interface VariableIdentifier extends TemplateIdentifier { kind: IdentifierKind.Variable; } +export interface VariableIdentifier extends TemplateIdentifier { + kind: IdentifierKind.Variable; +} /** * Identifiers recorded at the top level of the template, without any context about the HTML nodes * they were discovered in. */ -export type TopLevelIdentifier = PropertyIdentifier | MethodIdentifier | ElementIdentifier | - TemplateNodeIdentifier | ReferenceIdentifier | VariableIdentifier; +export type TopLevelIdentifier = PropertyIdentifier|MethodIdentifier|ElementIdentifier| + TemplateNodeIdentifier|ReferenceIdentifier|VariableIdentifier; /** * Describes the absolute byte offsets of a text anchor in a source code. diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/context.ts b/packages/compiler-cli/src/ngtsc/indexer/src/context.ts index 1881ec01f0ce4..c4c5801c65fc4 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/context.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/context.ts @@ -56,5 +56,7 @@ export class IndexingContext { /** * Adds a component to the context. */ - addComponent(info: ComponentInfo) { this.components.add(info); } + addComponent(info: ComponentInfo) { + this.components.add(info); + } } diff --git a/packages/compiler-cli/src/ngtsc/indexer/src/template.ts b/packages/compiler-cli/src/ngtsc/indexer/src/template.ts index 84d32411e983b..f0b2eacf187f5 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/src/template.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/src/template.ts @@ -18,9 +18,9 @@ interface HTMLNode extends TmplAstNode { name?: string; } -type ExpressionIdentifier = PropertyIdentifier | MethodIdentifier; -type TmplTarget = TmplAstReference | TmplAstVariable; -type TargetIdentifier = ReferenceIdentifier | VariableIdentifier; +type ExpressionIdentifier = PropertyIdentifier|MethodIdentifier; +type TmplTarget = TmplAstReference|TmplAstVariable; +type TargetIdentifier = ReferenceIdentifier|VariableIdentifier; type TargetIdentifierMap = Map<TmplTarget, TargetIdentifier>; /** @@ -62,7 +62,9 @@ class ExpressionVisitor extends RecursiveAstVisitor { return visitor.identifiers; } - visit(ast: AST) { ast.visit(this); } + visit(ast: AST) { + ast.visit(this); + } visitMethodCall(ast: MethodCall, context: {}) { this.visitIdentifier(ast, IdentifierKind.Method); @@ -144,16 +146,22 @@ class TemplateVisitor extends TmplAstRecursiveVisitor { * * @param boundTemplate bound template target */ - constructor(private boundTemplate: BoundTarget<ComponentMeta>) { super(); } + constructor(private boundTemplate: BoundTarget<ComponentMeta>) { + super(); + } /** * Visits a node in the template. * * @param node node to visit */ - visit(node: HTMLNode) { node.visit(this); } + visit(node: HTMLNode) { + node.visit(this); + } - visitAll(nodes: TmplAstNode[]) { nodes.forEach(node => this.visit(node)); } + visitAll(nodes: TmplAstNode[]) { + nodes.forEach(node => this.visit(node)); + } /** * Add an identifier for an HTML element and visit its children recursively. @@ -204,8 +212,12 @@ class TemplateVisitor extends TmplAstRecursiveVisitor { this.targetToIdentifier.bind(this)); identifiers.forEach(id => this.identifiers.add(id)); } - visitBoundEvent(attribute: TmplAstBoundEvent) { this.visitExpression(attribute.handler); } - visitBoundText(text: TmplAstBoundText) { this.visitExpression(text.value); } + visitBoundEvent(attribute: TmplAstBoundEvent) { + this.visitExpression(attribute.handler); + } + visitBoundText(text: TmplAstBoundText) { + this.visitExpression(text.value); + } visitReference(reference: TmplAstReference) { const referenceIdentifer = this.targetToIdentifier(reference); @@ -222,7 +234,7 @@ class TemplateVisitor extends TmplAstRecursiveVisitor { |TemplateNodeIdentifier { // If this node has already been seen, return the cached result. if (this.elementAndTemplateIdentifierCache.has(node)) { - return this.elementAndTemplateIdentifierCache.get(node) !; + return this.elementAndTemplateIdentifierCache.get(node)!; } let name: string; @@ -254,7 +266,8 @@ class TemplateVisitor extends TmplAstRecursiveVisitor { const identifier = { name, - span: absoluteSpan, kind, + span: absoluteSpan, + kind, attributes: new Set(attributes), usedDirectives: new Set(usedDirectives.map(dir => { return { @@ -274,7 +287,7 @@ class TemplateVisitor extends TmplAstRecursiveVisitor { private targetToIdentifier(node: TmplAstReference|TmplAstVariable): TargetIdentifier { // If this node has already been seen, return the cached result. if (this.targetIdentifierCache.has(node)) { - return this.targetIdentifierCache.get(node) !; + return this.targetIdentifierCache.get(node)!; } const {name, sourceSpan} = node; @@ -304,7 +317,8 @@ class TemplateVisitor extends TmplAstRecursiveVisitor { identifier = { name, span, - kind: IdentifierKind.Reference, target, + kind: IdentifierKind.Reference, + target, }; } else { identifier = { diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/context_spec.ts b/packages/compiler-cli/src/ngtsc/indexer/test/context_spec.ts index 070234844b88c..b95fb7502ce59 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/context_spec.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/context_spec.ts @@ -19,7 +19,8 @@ runInEachFileSystem(() => { context.addComponent({ declaration, - selector: 'c-selector', boundTemplate, + selector: 'c-selector', + boundTemplate, templateMeta: { isInline: false, file: new ParseSourceFile('<div></div>', util.getTestFilePath()), @@ -29,7 +30,8 @@ runInEachFileSystem(() => { expect(context.components).toEqual(new Set([ { declaration, - selector: 'c-selector', boundTemplate, + selector: 'c-selector', + boundTemplate, templateMeta: { isInline: false, file: new ParseSourceFile('<div></div>', util.getTestFilePath()), diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts b/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts index 831de6640fbe2..0f7b7e7aeb5cd 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/template_spec.ts @@ -219,11 +219,11 @@ runInEachFileSystem(() => { const refArr = Array.from(refs); expect(refArr).toEqual(jasmine.arrayContaining([{ - name: 'foo', - kind: IdentifierKind.Property, - span: new AbsoluteSourceSpan(20, 23), - target: null, - }] as TopLevelIdentifier[])); + name: 'foo', + kind: IdentifierKind.Property, + span: new AbsoluteSourceSpan(20, 23), + target: null, + }] as TopLevelIdentifier[])); }); it('should ignore property writes that are not implicitly received by the template', () => { @@ -314,12 +314,13 @@ runInEachFileSystem(() => { }; const refArray = Array.from(refs); - expect(refArray).toEqual(jasmine.arrayContaining([{ - name: 'foo', - kind: IdentifierKind.Reference, - span: new AbsoluteSourceSpan(6, 9), - target: {node: elementReference, directive: null}, - }] as TopLevelIdentifier[])); + expect(refArray).toEqual( + jasmine.arrayContaining([{ + name: 'foo', + kind: IdentifierKind.Reference, + span: new AbsoluteSourceSpan(6, 9), + target: {node: elementReference, directive: null}, + }] as TopLevelIdentifier[])); }); it('should discover nested references', () => { @@ -334,12 +335,13 @@ runInEachFileSystem(() => { }; const refArray = Array.from(refs); - expect(refArray).toEqual(jasmine.arrayContaining([{ - name: 'foo', - kind: IdentifierKind.Reference, - span: new AbsoluteSourceSpan(12, 15), - target: {node: elementReference, directive: null}, - }] as TopLevelIdentifier[])); + expect(refArray).toEqual( + jasmine.arrayContaining([{ + name: 'foo', + kind: IdentifierKind.Reference, + span: new AbsoluteSourceSpan(12, 15), + target: {node: elementReference, directive: null}, + }] as TopLevelIdentifier[])); }); it('should discover references to references', () => { @@ -409,14 +411,14 @@ runInEachFileSystem(() => { const refArr = Array.from(refs); let fooRef = refArr.find(id => id.name === 'foo'); expect(fooRef).toBeDefined(); - expect(fooRef !.kind).toBe(IdentifierKind.Reference); + expect(fooRef!.kind).toBe(IdentifierKind.Reference); fooRef = fooRef as ReferenceIdentifier; expect(fooRef.target).toBeDefined(); - expect(fooRef.target !.node.kind).toBe(IdentifierKind.Element); - expect(fooRef.target !.node.name).toBe('div'); - expect(fooRef.target !.node.span).toEqual(new AbsoluteSourceSpan(1, 4)); - expect(fooRef.target !.directive).toEqual(declB); + expect(fooRef.target!.node.kind).toBe(IdentifierKind.Element); + expect(fooRef.target!.node.name).toBe('div'); + expect(fooRef.target!.node.span).toEqual(new AbsoluteSourceSpan(1, 4)); + expect(fooRef.target!.directive).toEqual(declB); }); it('should discover references to references', () => { @@ -455,10 +457,10 @@ runInEachFileSystem(() => { const refArray = Array.from(refs); expect(refArray).toEqual(jasmine.arrayContaining([{ - name: 'foo', - kind: IdentifierKind.Variable, - span: new AbsoluteSourceSpan(17, 20), - }] as TopLevelIdentifier[])); + name: 'foo', + kind: IdentifierKind.Variable, + span: new AbsoluteSourceSpan(17, 20), + }] as TopLevelIdentifier[])); }); it('should discover variables with let- syntax', () => { @@ -467,10 +469,10 @@ runInEachFileSystem(() => { const refArray = Array.from(refs); expect(refArray).toEqual(jasmine.arrayContaining([{ - name: 'var', - kind: IdentifierKind.Variable, - span: new AbsoluteSourceSpan(17, 20), - }] as TopLevelIdentifier[])); + name: 'var', + kind: IdentifierKind.Variable, + span: new AbsoluteSourceSpan(17, 20), + }] as TopLevelIdentifier[])); }); it('should discover nested variables', () => { @@ -479,10 +481,10 @@ runInEachFileSystem(() => { const refArray = Array.from(refs); expect(refArray).toEqual(jasmine.arrayContaining([{ - name: 'foo', - kind: IdentifierKind.Variable, - span: new AbsoluteSourceSpan(23, 26), - }] as TopLevelIdentifier[])); + name: 'foo', + kind: IdentifierKind.Variable, + span: new AbsoluteSourceSpan(23, 26), + }] as TopLevelIdentifier[])); }); it('should discover references to variables', () => { diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/transform_spec.ts b/packages/compiler-cli/src/ngtsc/indexer/test/transform_spec.ts index e77763f7c7d71..8dac5ec4b5b3e 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/transform_spec.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/transform_spec.ts @@ -68,7 +68,7 @@ runInEachFileSystem(() => { const info = analysis.get(decl); expect(info).toBeDefined(); - expect(info !.template.file) + expect(info!.template.file) .toEqual(new ParseSourceFile('class C {}', util.getTestFilePath())); }); @@ -83,7 +83,7 @@ runInEachFileSystem(() => { const info = analysis.get(decl); expect(info).toBeDefined(); - expect(info !.template.file) + expect(info!.template.file) .toEqual(new ParseSourceFile('<div>{{foo}}</div>', util.getTestFilePath())); }); @@ -110,11 +110,11 @@ runInEachFileSystem(() => { const infoA = analysis.get(declA); expect(infoA).toBeDefined(); - expect(infoA !.template.usedComponents).toEqual(new Set([declB])); + expect(infoA!.template.usedComponents).toEqual(new Set([declB])); const infoB = analysis.get(declB); expect(infoB).toBeDefined(); - expect(infoB !.template.usedComponents).toEqual(new Set([declA])); + expect(infoB!.template.usedComponents).toEqual(new Set([declA])); }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/indexer/test/util.ts b/packages/compiler-cli/src/ngtsc/indexer/test/util.ts index 55bf78fb19488..67b200f39fe33 100644 --- a/packages/compiler-cli/src/ngtsc/indexer/test/util.ts +++ b/packages/compiler-cli/src/ngtsc/indexer/test/util.ts @@ -6,9 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {BoundTarget, CssSelector, ParseTemplateOptions, R3TargetBinder, SelectorMatcher, parseTemplate} from '@angular/compiler'; +import {BoundTarget, CssSelector, parseTemplate, ParseTemplateOptions, R3TargetBinder, SelectorMatcher} from '@angular/compiler'; import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom} from '../../file_system'; + +import {absoluteFrom, AbsoluteFsPath} from '../../file_system'; import {Reference} from '../../imports'; import {ClassDeclaration} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/dts.ts b/packages/compiler-cli/src/ngtsc/metadata/src/dts.ts index 9e85145fd9d7c..1f72300077d44 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/dts.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/dts.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {Reference} from '../../imports'; -import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost} from '../../reflection'; import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta} from './api'; import {extractDirectiveGuards, extractReferencesFromType, readStringArrayType, readStringMapType, readStringType} from './util'; diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts index c6265188d153b..86d5f9588b445 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/inheritance.ts @@ -25,7 +25,7 @@ export function flattenInheritedDirectiveMetadata( throw new Error(`Metadata not found for directive: ${dir.debugName}`); } - let inputs: {[key: string]: string | [string, string]} = {}; + let inputs: {[key: string]: string|[string, string]} = {}; let outputs: {[key: string]: string} = {}; let coercedInputFields = new Set<string>(); let isDynamic = false; diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/registry.ts b/packages/compiler-cli/src/ngtsc/metadata/src/registry.ts index b1bd7a5ffb533..585af36c7bbf7 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/registry.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/registry.ts @@ -22,18 +22,24 @@ export class LocalMetadataRegistry implements MetadataRegistry, MetadataReader { private pipes = new Map<ClassDeclaration, PipeMeta>(); getDirectiveMetadata(ref: Reference<ClassDeclaration>): DirectiveMeta|null { - return this.directives.has(ref.node) ? this.directives.get(ref.node) ! : null; + return this.directives.has(ref.node) ? this.directives.get(ref.node)! : null; } getNgModuleMetadata(ref: Reference<ClassDeclaration>): NgModuleMeta|null { - return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node) ! : null; + return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node)! : null; } getPipeMetadata(ref: Reference<ClassDeclaration>): PipeMeta|null { - return this.pipes.has(ref.node) ? this.pipes.get(ref.node) ! : null; + return this.pipes.has(ref.node) ? this.pipes.get(ref.node)! : null; } - registerDirectiveMetadata(meta: DirectiveMeta): void { this.directives.set(meta.ref.node, meta); } - registerNgModuleMetadata(meta: NgModuleMeta): void { this.ngModules.set(meta.ref.node, meta); } - registerPipeMetadata(meta: PipeMeta): void { this.pipes.set(meta.ref.node, meta); } + registerDirectiveMetadata(meta: DirectiveMeta): void { + this.directives.set(meta.ref.node, meta); + } + registerNgModuleMetadata(meta: NgModuleMeta): void { + this.ngModules.set(meta.ref.node, meta); + } + registerPipeMetadata(meta: PipeMeta): void { + this.pipes.set(meta.ref.node, meta); + } } /** @@ -70,7 +76,9 @@ export class InjectableClassRegistry { constructor(private host: ReflectionHost) {} - registerInjectable(declaration: ClassDeclaration): void { this.classes.add(declaration); } + registerInjectable(declaration: ClassDeclaration): void { + this.classes.add(declaration); + } isInjectable(declaration: ClassDeclaration): boolean { // Figure out whether the class is injectable based on the registered classes, otherwise diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/util.ts b/packages/compiler-cli/src/ngtsc/metadata/src/util.ts index 48b6a3a4a9423..c3fc520dd27d3 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/util.ts @@ -9,13 +9,13 @@ import * as ts from 'typescript'; import {Reference} from '../../imports'; -import {ClassDeclaration, ClassMember, ClassMemberKind, ReflectionHost, isNamedClassDeclaration, reflectTypeEntityToDeclaration} from '../../reflection'; +import {ClassDeclaration, ClassMember, ClassMemberKind, isNamedClassDeclaration, ReflectionHost, reflectTypeEntityToDeclaration} from '../../reflection'; import {nodeDebugInfo} from '../../util/src/typescript'; import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta, TemplateGuardMeta} from './api'; export function extractReferencesFromType( - checker: ts.TypeChecker, def: ts.TypeNode, ngModuleImportedFrom: string | null, + checker: ts.TypeChecker, def: ts.TypeNode, ngModuleImportedFrom: string|null, resolutionContext: string): Reference<ClassDeclaration>[] { if (!ts.isTupleTypeNode(def)) { return []; @@ -122,7 +122,7 @@ function extractTemplateGuard(member: ClassMember): TemplateGuardMeta|null { function extractCoercedInput(member: ClassMember): string|null { if (member.kind !== ClassMemberKind.Property || !member.name.startsWith('ngAcceptInputType_')) { - return null !; + return null!; } return afterUnderscore(member.name); } diff --git a/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts b/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts index 137151ed5bccc..52320a87932ca 100644 --- a/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts +++ b/packages/compiler-cli/src/ngtsc/modulewithproviders/src/scanner.ts @@ -13,7 +13,9 @@ import {ImportFlags, Reference, ReferenceEmitter} from '../../imports'; import {PartialEvaluator, ResolvedValueMap} from '../../partial_evaluator'; import {ReflectionHost} from '../../reflection'; -export interface DtsHandler { addTypeReplacement(node: ts.Declaration, type: Type): void; } +export interface DtsHandler { + addTypeReplacement(node: ts.Declaration, type: Type): void; +} export class ModuleWithProvidersScanner { constructor( diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/builtin.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/builtin.ts index b8f4e6b8acabf..c04aad5b50fad 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/builtin.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/builtin.ts @@ -12,7 +12,9 @@ import {DynamicValue} from './dynamic'; import {KnownFn, ResolvedValue, ResolvedValueArray} from './result'; export class ArraySliceBuiltinFn extends KnownFn { - constructor(private lhs: ResolvedValueArray) { super(); } + constructor(private lhs: ResolvedValueArray) { + super(); + } evaluate(node: ts.CallExpression, args: ResolvedValueArray): ResolvedValue { if (args.length === 0) { @@ -24,7 +26,9 @@ export class ArraySliceBuiltinFn extends KnownFn { } export class ArrayConcatBuiltinFn extends KnownFn { - constructor(private lhs: ResolvedValueArray) { super(); } + constructor(private lhs: ResolvedValueArray) { + super(); + } evaluate(node: ts.CallExpression, args: ResolvedValueArray): ResolvedValue { const result: ResolvedValueArray = [...this.lhs]; diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interface.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interface.ts index 76b3e5698960f..f9a380a66c0d3 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interface.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interface.ts @@ -17,7 +17,7 @@ import {ResolvedValue} from './result'; export type ForeignFunctionResolver = (node: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>, - args: ReadonlyArray<ts.Expression>) => ts.Expression | null; + args: ReadonlyArray<ts.Expression>) => ts.Expression|null; export class PartialEvaluator { constructor( @@ -31,7 +31,8 @@ export class PartialEvaluator { originatingFile: sourceFile, absoluteModuleName: null, resolutionContext: sourceFile.fileName, - scope: new Map<ts.ParameterDeclaration, ResolvedValue>(), foreignFunctionResolver, + scope: new Map<ts.ParameterDeclaration, ResolvedValue>(), + foreignFunctionResolver, }); } } diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts index e47a59d58cba6..ab12a398fbd08 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/interpreter.ts @@ -203,19 +203,13 @@ export class StaticInterpreter { const pieces: string[] = [node.head.text]; for (let i = 0; i < node.templateSpans.length; i++) { const span = node.templateSpans[i]; - let value = this.visit(span.expression, context); - if (value instanceof EnumValue) { - value = value.resolved; - } - if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || - value == null) { - pieces.push(`${value}`); - } else if (value instanceof DynamicValue) { + const value = literal( + this.visit(span.expression, context), + () => DynamicValue.fromDynamicString(span.expression)); + if (value instanceof DynamicValue) { return DynamicValue.fromDynamicInput(node, value); - } else { - return DynamicValue.fromDynamicInput(node, DynamicValue.fromDynamicString(span.expression)); } - pieces.push(span.literal.text); + pieces.push(`${value}`, span.literal.text); } return pieces.join(''); } @@ -261,7 +255,7 @@ export class StaticInterpreter { } else if (ts.isVariableDeclaration(node)) { return this.visitVariableDeclaration(node, context); } else if (ts.isParameter(node) && context.scope.has(node)) { - return context.scope.get(node) !; + return context.scope.get(node)!; } else if (ts.isExportAssignment(node)) { return this.visitExpression(node.expression, context); } else if (ts.isEnumDeclaration(node)) { @@ -337,7 +331,8 @@ export class StaticInterpreter { } const declContext = { - ...context, ...joinModuleContext(context, node, decl), + ...context, + ...joinModuleContext(context, node, decl), }; // Visit both concrete and inline declarations. @@ -354,7 +349,7 @@ export class StaticInterpreter { const strIndex = `${rhs}`; if (lhs instanceof Map) { if (lhs.has(strIndex)) { - return lhs.get(strIndex) !; + return lhs.get(strIndex)!; } else { return undefined; } @@ -501,7 +496,7 @@ export class StaticInterpreter { return DynamicValue.fromUnsupportedSyntax(node); } - const op = UNARY_OPERATORS.get(operatorKind) !; + const op = UNARY_OPERATORS.get(operatorKind)!; const value = this.visitExpression(node.operand, context); if (value instanceof DynamicValue) { return DynamicValue.fromDynamicInput(node, value); @@ -516,11 +511,15 @@ export class StaticInterpreter { return DynamicValue.fromUnsupportedSyntax(node); } - const opRecord = BINARY_OPERATORS.get(tokenKind) !; + const opRecord = BINARY_OPERATORS.get(tokenKind)!; let lhs: ResolvedValue, rhs: ResolvedValue; if (opRecord.literal) { - lhs = literal(this.visitExpression(node.left, context), node.left); - rhs = literal(this.visitExpression(node.right, context), node.right); + lhs = literal( + this.visitExpression(node.left, context), + value => DynamicValue.fromInvalidExpressionType(node.left, value)); + rhs = literal( + this.visitExpression(node.right, context), + value => DynamicValue.fromInvalidExpressionType(node.right, value)); } else { lhs = this.visitExpression(node.left, context); rhs = this.visitExpression(node.right, context); @@ -584,12 +583,16 @@ function isFunctionOrMethodReference(ref: Reference<ts.Node>): ts.isFunctionExpression(ref.node); } -function literal(value: ResolvedValue, node: ts.Node): any { +function literal( + value: ResolvedValue, reject: (value: ResolvedValue) => ResolvedValue): ResolvedValue { + if (value instanceof EnumValue) { + value = value.resolved; + } if (value instanceof DynamicValue || value === null || value === undefined || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { return value; } - return DynamicValue.fromInvalidExpressionType(node, value); + return reject(value); } function isVariableDeclarationDeclared(node: ts.VariableDeclaration): boolean { @@ -621,7 +624,7 @@ function joinModuleContext(existing: Context, node: ts.Node, decl: Declaration): } } -function owningModule(context: Context, override: OwningModule | null = null): OwningModule|null { +function owningModule(context: Context, override: OwningModule|null = null): OwningModule|null { let specifier = context.absoluteModuleName; if (override !== null) { specifier = override.specifier; diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/result.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/result.ts index 96a0c49863dd2..c4f817d043254 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/src/result.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/src/result.ts @@ -21,8 +21,8 @@ import {DynamicValue} from './dynamic'; * non-primitive value, or a special `DynamicValue` type which indicates the value was not * available statically. */ -export type ResolvedValue = number | boolean | string | null | undefined | Reference | EnumValue | - ResolvedValueArray | ResolvedValueMap | ResolvedModule | KnownFn | DynamicValue<unknown>; +export type ResolvedValue = number|boolean|string|null|undefined|Reference|EnumValue| + ResolvedValueArray|ResolvedValueMap|ResolvedModule|KnownFn|DynamicValue<unknown>; /** * An array of `ResolvedValue`s. @@ -54,12 +54,14 @@ export class ResolvedModule { return undefined; } - return this.evaluate(this.exports.get(name) !); + return this.evaluate(this.exports.get(name)!); } getExports(): ResolvedValueMap { const map = new Map<string, ResolvedValue>(); - this.exports.forEach((decl, name) => { map.set(name, this.evaluate(decl)); }); + this.exports.forEach((decl, name) => { + map.set(name, this.evaluate(decl)); + }); return map; } } diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts index a64fe61a51d66..33e38e91a8b7b 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/test/evaluator_spec.ts @@ -42,11 +42,13 @@ runInEachFileSystem(() => { expect(value).toEqual('test'); }); - it('map access works', - () => { expect(evaluate('const obj = {a: "test"};', 'obj.a')).toEqual('test'); }); + it('map access works', () => { + expect(evaluate('const obj = {a: "test"};', 'obj.a')).toEqual('test'); + }); - it('resolves undefined property access', - () => { expect(evaluate('const obj: any = {}', 'obj.bar')).toEqual(undefined); }); + it('resolves undefined property access', () => { + expect(evaluate('const obj: any = {}', 'obj.bar')).toEqual(undefined); + }); it('function calls work', () => { expect(evaluate(`function foo(bar) { return bar; }`, 'foo("test")')).toEqual('test'); @@ -68,7 +70,9 @@ runInEachFileSystem(() => { expect(evaluate(`const x = false; const y = x ? 'true' : 'false';`, 'y')).toEqual('false'); }); - it('addition works', () => { expect(evaluate(`const x = 1 + 2;`, 'x')).toEqual(3); }); + it('addition works', () => { + expect(evaluate(`const x = 1 + 2;`, 'x')).toEqual(3); + }); it('static property on class works', () => { expect(evaluate(`class Foo { static bar = 'test'; }`, 'Foo.bar')).toEqual('test'); @@ -148,19 +152,22 @@ runInEachFileSystem(() => { expect(evaluate('const a: any = 3, b = 3;', 'a !== b')).toEqual(false); }); - it('parentheticals work', - () => { expect(evaluate(`const a = 3, b = 4;`, 'a * (a + b)')).toEqual(21); }); + it('parentheticals work', () => { + expect(evaluate(`const a = 3, b = 4;`, 'a * (a + b)')).toEqual(21); + }); - it('array access works', - () => { expect(evaluate(`const a = [1, 2, 3];`, 'a[1] + a[0]')).toEqual(3); }); + it('array access works', () => { + expect(evaluate(`const a = [1, 2, 3];`, 'a[1] + a[0]')).toEqual(3); + }); it('array access out of bounds is `undefined`', () => { expect(evaluate(`const a = [1, 2, 3];`, 'a[-1]')).toEqual(undefined); expect(evaluate(`const a = [1, 2, 3];`, 'a[3]')).toEqual(undefined); }); - it('array `length` property access works', - () => { expect(evaluate(`const a = [1, 2, 3];`, 'a[\'length\'] + 1')).toEqual(4); }); + it('array `length` property access works', () => { + expect(evaluate(`const a = [1, 2, 3];`, 'a[\'length\'] + 1')).toEqual(4); + }); it('array `slice` function works', () => { expect(evaluate(`const a = [1, 2, 3];`, 'a[\'slice\']()')).toEqual([1, 2, 3]); @@ -185,10 +192,13 @@ runInEachFileSystem(() => { expect(evaluate('const a = false;', 'a')).toEqual(false); }); - it('supports undefined', - () => { expect(evaluate('const a = undefined;', 'a')).toEqual(undefined); }); + it('supports undefined', () => { + expect(evaluate('const a = undefined;', 'a')).toEqual(undefined); + }); - it('supports null', () => { expect(evaluate('const a = null;', 'a')).toEqual(null); }); + it('supports null', () => { + expect(evaluate('const a = null;', 'a')).toEqual(null); + }); it('resolves unknown binary operators as dynamic value', () => { const value = evaluate('declare const window: any;', '"location" in window'); @@ -308,7 +318,7 @@ runInEachFileSystem(() => { ]); const checker = program.getTypeChecker(); const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration); - const expr = result.initializer !; + const expr = result.initializer!; const evaluator = makeEvaluator(checker); const resolved = evaluator.evaluate(expr); if (!(resolved instanceof Reference)) { @@ -338,7 +348,7 @@ runInEachFileSystem(() => { ]); const checker = program.getTypeChecker(); const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration); - const expr = result.initializer !; + const expr = result.initializer!; const evaluator = makeEvaluator(checker); const resolved = evaluator.evaluate(expr); if (!(resolved instanceof Reference)) { @@ -348,7 +358,7 @@ runInEachFileSystem(() => { expect(ts.isFunctionDeclaration(resolved.node)).toBe(true); const reference = resolved.getIdentityIn(getSourceFileOrError(program, _('/entry.ts'))); expect(reference).not.toBeNull(); - expect(reference !.getSourceFile()).toEqual(getSourceFileOrError(program, _('/entry.ts'))); + expect(reference!.getSourceFile()).toEqual(getSourceFileOrError(program, _('/entry.ts'))); }); it('reads values from default exports', () => { @@ -434,8 +444,34 @@ runInEachFileSystem(() => { .toEqual('test'); }); - it('template expressions work', - () => { expect(evaluate('const a = 2, b = 4;', '`1${a}3${b}5`')).toEqual('12345'); }); + it('template expressions work', () => { + expect(evaluate('const a = 2, b = 4;', '`1${a}3${b}5`')).toEqual('12345'); + }); + + it('template expressions should resolve enums', () => { + expect(evaluate('enum Test { VALUE = "test" };', '`a.${Test.VALUE}.b`')).toBe('a.test.b'); + }); + + it('string concatenation should resolve enums', () => { + expect(evaluate('enum Test { VALUE = "test" };', '"a." + Test.VALUE + ".b"')) + .toBe('a.test.b'); + }); + + it('should resolve non-literals as dynamic string', () => { + const value = evaluate(`const a: any = [];`, '`a.${a}.b`'); + + if (!(value instanceof DynamicValue)) { + return fail(`Should have resolved to a DynamicValue`); + } + expect(value.node.getText()).toEqual('`a.${a}.b`'); + + if (!value.isFromDynamicInput()) { + return fail('Should originate from dynamic input'); + } else if (!value.reason.isFromDynamicString()) { + return fail('Should refer to a dynamic string part'); + } + expect(value.reason.node.getText()).toEqual('a'); + }); it('enum resolution works', () => { const result = evaluate( @@ -469,7 +505,7 @@ runInEachFileSystem(() => { ]); const checker = program.getTypeChecker(); const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration); - const expr = result.initializer !as ts.ObjectLiteralExpression; + const expr = result.initializer! as ts.ObjectLiteralExpression; const prop = expr.properties[0] as ts.ShorthandPropertyAssignment; const evaluator = makeEvaluator(checker); const resolved = evaluator.evaluate(prop.name); @@ -487,13 +523,13 @@ runInEachFileSystem(() => { ]); const checker = program.getTypeChecker(); const result = getDeclaration(program, _('/entry.ts'), 'target$', ts.isVariableDeclaration); - const expr = result.initializer !as ts.ObjectLiteralExpression; + const expr = result.initializer! as ts.ObjectLiteralExpression; const evaluator = makeEvaluator(checker); const resolved = evaluator.evaluate(expr); if (!(resolved instanceof Map)) { return fail('Should have resolved to a Map'); } - const value = resolved.get('value') !; + const value = resolved.get('value')!; if (!(value instanceof DynamicValue)) { return fail(`Should have resolved 'value' to a DynamicValue`); } @@ -501,12 +537,6 @@ runInEachFileSystem(() => { expect(value.node).toBe(prop.initializer); }); - it('should resolve enums in template expressions', () => { - const value = - evaluate(`enum Test { VALUE = 'test', } const value = \`a.\${Test.VALUE}.b\`;`, 'value'); - expect(value).toBe('a.test.b'); - }); - it('should not attach identifiers to FFR-resolved values', () => { const value = evaluate( ` @@ -745,18 +775,22 @@ runInEachFileSystem(() => { describe('(visited file tracking)', () => { it('should track each time a source file is visited', () => { - const addDependency = jasmine.createSpy('DependencyTracker'); + const addDependency = + jasmine.createSpy<DependencyTracker['addDependency']>('DependencyTracker'); const {expression, checker} = makeExpression( `class A { static foo = 42; } function bar() { return A.foo; }`, 'bar()'); const evaluator = makeEvaluator(checker, {...fakeDepTracker, addDependency}); evaluator.evaluate(expression); expect(addDependency).toHaveBeenCalledTimes(2); // two declaration visited - expect(addDependency.calls.allArgs().map(args => [args[0].fileName, args[1].fileName])) + expect( + addDependency.calls.allArgs().map( + (args: Parameters<typeof addDependency>) => [args[0].fileName, args[1].fileName])) .toEqual([[_('/entry.ts'), _('/entry.ts')], [_('/entry.ts'), _('/entry.ts')]]); }); it('should track imported source files', () => { - const addDependency = jasmine.createSpy('DependencyTracker'); + const addDependency = + jasmine.createSpy<DependencyTracker['addDependency']>('DependencyTracker'); const {expression, checker} = makeExpression(`import {Y} from './other'; const A = Y;`, 'A', [ {name: _('/other.ts'), contents: `export const Y = 'test';`}, @@ -765,7 +799,9 @@ runInEachFileSystem(() => { const evaluator = makeEvaluator(checker, {...fakeDepTracker, addDependency}); evaluator.evaluate(expression); expect(addDependency).toHaveBeenCalledTimes(2); - expect(addDependency.calls.allArgs().map(args => [args[0].fileName, args[1].fileName])) + expect( + addDependency.calls.allArgs().map( + (args: Parameters<typeof addDependency>) => [args[0].fileName, args[1].fileName])) .toEqual([ [_('/entry.ts'), _('/entry.ts')], [_('/entry.ts'), _('/other.ts')], @@ -773,7 +809,8 @@ runInEachFileSystem(() => { }); it('should track files passed through during re-exports', () => { - const addDependency = jasmine.createSpy('DependencyTracker'); + const addDependency = + jasmine.createSpy<DependencyTracker['addDependency']>('DependencyTracker'); const {expression, checker} = makeExpression(`import * as mod from './direct-reexport';`, 'mod.value.property', [ {name: _('/const.ts'), contents: 'export const value = {property: "test"};'}, @@ -793,7 +830,9 @@ runInEachFileSystem(() => { const evaluator = makeEvaluator(checker, {...fakeDepTracker, addDependency}); evaluator.evaluate(expression); expect(addDependency).toHaveBeenCalledTimes(2); - expect(addDependency.calls.allArgs().map(args => [args[0].fileName, args[1].fileName])) + expect( + addDependency.calls.allArgs().map( + (args: Parameters<typeof addDependency>) => [args[0].fileName, args[1].fileName])) .toEqual([ [_('/entry.ts'), _('/direct-reexport.ts')], // Not '/indirect-reexport.ts' or '/def.ts'. diff --git a/packages/compiler-cli/src/ngtsc/partial_evaluator/test/utils.ts b/packages/compiler-cli/src/ngtsc/partial_evaluator/test/utils.ts index ac19be5c6fe21..83578c36f0eb3 100644 --- a/packages/compiler-cli/src/ngtsc/partial_evaluator/test/utils.ts +++ b/packages/compiler-cli/src/ngtsc/partial_evaluator/test/utils.ts @@ -31,7 +31,7 @@ export function makeExpression(code: string, expr: string, supportingFiles: Test const decl = getDeclaration(program, absoluteFrom('/entry.ts'), 'target$', ts.isVariableDeclaration); return { - expression: decl.initializer !, + expression: decl.initializer!, host, options, checker, diff --git a/packages/compiler-cli/src/ngtsc/perf/src/noop.ts b/packages/compiler-cli/src/ngtsc/perf/src/noop.ts index 01e4c9c68063f..8cd7c799c53b3 100644 --- a/packages/compiler-cli/src/ngtsc/perf/src/noop.ts +++ b/packages/compiler-cli/src/ngtsc/perf/src/noop.ts @@ -12,9 +12,11 @@ import {PerfRecorder} from './api'; export const NOOP_PERF_RECORDER: PerfRecorder = { enabled: false, - mark: (name: string, node: ts.SourceFile | ts.Declaration, category?: string, detail?: string): - void => {}, - start: (name: string, node: ts.SourceFile | ts.Declaration, category?: string, detail?: string): - number => { return 0;}, - stop: (span: number | false): void => {}, + mark: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string): + void => {}, + start: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string): + number => { + return 0; + }, + stop: (span: number|false): void => {}, }; diff --git a/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts b/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts index 0eb6251b91077..80c4a8d32dc1c 100644 --- a/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts +++ b/packages/compiler-cli/src/ngtsc/perf/src/tracking.ts @@ -20,7 +20,9 @@ export class PerfTracker implements PerfRecorder { private constructor(private zeroTime: HrTime) {} - static zeroedToNow(): PerfTracker { return new PerfTracker(mark()); } + static zeroedToNow(): PerfTracker { + return new PerfTracker(mark()); + } mark(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string): void { @@ -73,7 +75,9 @@ export class PerfTracker implements PerfRecorder { return msg; } - asJson(): unknown { return this.log; } + asJson(): unknown { + return this.log; + } serializeToFile(target: string, host: ts.CompilerHost): void { const json = JSON.stringify(this.log, null, 2); diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index 4ed3fb4a854fe..5e250ddc7bd5e 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -10,7 +10,6 @@ import {GeneratedFile} from '@angular/compiler'; import * as ts from 'typescript'; import * as api from '../transformers/api'; -import {nocollapseHack} from '../transformers/nocollapse_hack'; import {verifySupportedTypeScriptVersion} from '../typescript_support'; import {NgCompilerHost} from './core'; @@ -78,7 +77,9 @@ export class NgtscProgram implements api.Program { new NgCompiler(this.host, options, this.tsProgram, reuseProgram, this.perfRecorder); } - getTsProgram(): ts.Program { return this.tsProgram; } + getTsProgram(): ts.Program { + return this.tsProgram; + } getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken| undefined): readonly ts.Diagnostic[] { @@ -138,8 +139,8 @@ export class NgtscProgram implements api.Program { } getNgSemanticDiagnostics( - fileName?: string|undefined, cancellationToken?: ts.CancellationToken| - undefined): readonly(ts.Diagnostic|api.Diagnostic)[] { + fileName?: string|undefined, cancellationToken?: ts.CancellationToken|undefined): + readonly(ts.Diagnostic|api.Diagnostic)[] { let sf: ts.SourceFile|undefined = undefined; if (fileName !== undefined) { sf = this.tsProgram.getSourceFile(fileName); @@ -162,14 +163,17 @@ export class NgtscProgram implements api.Program { * This is used by the Angular CLI to allow for spawning (async) child compilations for things * like SASS files used in `styleUrls`. */ - loadNgStructureAsync(): Promise<void> { return this.compiler.analyzeAsync(); } + loadNgStructureAsync(): Promise<void> { + return this.compiler.analyzeAsync(); + } listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] { return this.compiler.listLazyRoutes(entryRoute); } emit(opts?: { - emitFlags?: api.EmitFlags | undefined; cancellationToken?: ts.CancellationToken | undefined; + emitFlags?: api.EmitFlags|undefined; + cancellationToken?: ts.CancellationToken | undefined; customTransformers?: api.CustomTransformers | undefined; emitCallback?: api.TsEmitCallback | undefined; mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback | undefined; @@ -180,8 +184,8 @@ export class NgtscProgram implements api.Program { const writeFile: ts.WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean, - onError: ((message: string) => void) | undefined, - sourceFiles: ReadonlyArray<ts.SourceFile>| undefined) => { + onError: ((message: string) => void)|undefined, + sourceFiles: ReadonlyArray<ts.SourceFile>|undefined) => { if (sourceFiles !== undefined) { // Record successful writes for any `ts.SourceFile` (that's not a declaration file) // that's an input to this write. @@ -193,16 +197,6 @@ export class NgtscProgram implements api.Program { this.compiler.incrementalDriver.recordSuccessfulEmit(writtenSf); } } - - // If Closure annotations are being produced, tsickle should be adding `@nocollapse` to - // any static fields present. However, tsickle doesn't yet handle synthetic fields added - // during other transformations, so this hack is in place to ensure Ivy definitions get - // properly annotated, pending an upstream fix in tsickle. - // - // TODO(alxhub): remove when tsickle properly annotates synthetic fields. - if (this.closureCompilerEnabled && fileName.endsWith('.js')) { - data = nocollapseHack(data); - } this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles); }; @@ -232,7 +226,8 @@ export class NgtscProgram implements api.Program { program: this.tsProgram, host: this.host, options: this.options, - emitOnlyDtsFiles: false, writeFile, + emitOnlyDtsFiles: false, + writeFile, customTransformers: { before: beforeTransforms, after: customTransforms && customTransforms.afterTs, @@ -268,11 +263,16 @@ export class NgtscProgram implements api.Program { } } -const defaultEmitCallback: api.TsEmitCallback = - ({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, - customTransformers}) => - program.emit( - targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); +const defaultEmitCallback: api.TsEmitCallback = ({ + program, + targetSourceFile, + writeFile, + cancellationToken, + emitOnlyDtsFiles, + customTransformers +}) => + program.emit( + targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult { const diagnostics: ts.Diagnostic[] = []; diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/host.ts b/packages/compiler-cli/src/ngtsc/reflection/src/host.ts index 6879769e16255..55cff19104355 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/host.ts @@ -12,7 +12,7 @@ import * as ts from 'typescript'; * Metadata extracted from an instance of a decorator on another declaration, or synthesized from * other information about a class. */ -export type Decorator = ConcreteDecorator | SyntheticDecorator; +export type Decorator = ConcreteDecorator|SyntheticDecorator; export interface BaseDecorator { /** @@ -28,11 +28,11 @@ export interface BaseDecorator { */ identifier: DecoratorIdentifier|null; - /** - * `Import` by which the decorator was brought into the module in which it was invoked, or `null` - * if the decorator was declared in the same module and not imported. - */ - import : Import | null; +/** + * `Import` by which the decorator was brought into the module in which it was invoked, or `null` + * if the decorator was declared in the same module and not imported. + */ +import: Import|null; /** * TypeScript reference to the decorator itself, or `null` if the decorator is synthesized (e.g. @@ -87,8 +87,8 @@ export const Decorator = { * A decorator is identified by either a simple identifier (e.g. `Decorator`) or, in some cases, * a namespaced property access (e.g. `core.Decorator`). */ -export type DecoratorIdentifier = ts.Identifier | NamespacedIdentifier; -export type NamespacedIdentifier = ts.PropertyAccessExpression & { +export type DecoratorIdentifier = ts.Identifier|NamespacedIdentifier; +export type NamespacedIdentifier = ts.PropertyAccessExpression&{ expression: ts.Identifier; name: ts.Identifier }; @@ -113,7 +113,7 @@ export function isDecoratorIdentifier(exp: ts.Expression): exp is DecoratorIdent * For `ReflectionHost` purposes, a class declaration should always have a `name` identifier, * because we need to be able to reference it in other parts of the program. */ -export type ClassDeclaration<T extends ts.Declaration = ts.Declaration> = T & {name: ts.Identifier}; +export type ClassDeclaration<T extends ts.Declaration = ts.Declaration> = T&{name: ts.Identifier}; /** * An enumeration of possible kinds of class members. @@ -241,11 +241,26 @@ export interface ClassMember { */ export type TypeValueReference = { local: true; expression: ts.Expression; defaultImportStatement: ts.ImportDeclaration | null; -} | -{ +}|{ local: false; - name: string; + + /** + * The module specifier from which the `importedName` symbol should be imported. + */ moduleName: string; + + /** + * The name of the top-level symbol that is imported from `moduleName`. If `nestedPath` is also + * present, a nested object is being referenced from the top-level symbol. + */ + importedName: string; + + /** + * If present, represents the symbol names that are referenced from the top-level import. + * When `null` or empty, the `importedName` itself is the symbol being referenced. + */ + nestedPath: string[]|null; + valueDeclaration: ts.Declaration; }; @@ -447,7 +462,7 @@ export interface InlineDeclaration extends BaseDeclaration { * downlevelings to a `ts.Expression` instead. */ export type Declaration<T extends ts.Declaration = ts.Declaration> = - ConcreteDeclaration<T>| InlineDeclaration; + ConcreteDeclaration<T>|InlineDeclaration; /** * Abstracts reflection operations on a TypeScript AST. diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/type_to_value.ts b/packages/compiler-cli/src/ngtsc/reflection/src/type_to_value.ts index edcab882ab871..4b1ecb5f68e3f 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/type_to_value.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/type_to_value.ts @@ -18,7 +18,7 @@ import {TypeValueReference} from './host'; * declaration, or if it is not possible to statically understand. */ export function typeToValue( - typeNode: ts.TypeNode | null, checker: ts.TypeChecker): TypeValueReference|null { + typeNode: ts.TypeNode|null, checker: ts.TypeChecker): TypeValueReference|null { // It's not possible to get a value expression if the parameter doesn't even have a type. if (typeNode === null || !ts.isTypeReferenceNode(typeNode)) { return null; @@ -42,31 +42,76 @@ export function typeToValue( // Look at the local `ts.Symbol`'s declarations and see if it comes from an import // statement. If so, extract the module specifier and the name of the imported type. const firstDecl = local.declarations && local.declarations[0]; + if (firstDecl !== undefined) { + if (ts.isImportClause(firstDecl) && firstDecl.name !== undefined) { + // This is a default import. + // import Foo from 'foo'; - if (firstDecl && ts.isImportClause(firstDecl) && firstDecl.name !== undefined) { - // This is a default import. - return { - local: true, - // Copying the name here ensures the generated references will be correctly transformed along - // with the import. - expression: ts.updateIdentifier(firstDecl.name), - defaultImportStatement: firstDecl.parent, - }; - } else if (firstDecl && isImportSource(firstDecl)) { - const origin = extractModuleAndNameFromImport(firstDecl, symbols.importName); - return {local: false, valueDeclaration: decl.valueDeclaration, ...origin}; - } else { - const expression = typeNodeToValueExpr(typeNode); - if (expression !== null) { return { local: true, - expression, - defaultImportStatement: null, + // Copying the name here ensures the generated references will be correctly transformed + // along with the import. + expression: ts.updateIdentifier(firstDecl.name), + defaultImportStatement: firstDecl.parent, + }; + } else if (ts.isImportSpecifier(firstDecl)) { + // The symbol was imported by name + // import {Foo} from 'foo'; + // or + // import {Foo as Bar} from 'foo'; + + // Determine the name to import (`Foo`) from the import specifier, as the symbol names of + // the imported type could refer to a local alias (like `Bar` in the example above). + const importedName = (firstDecl.propertyName || firstDecl.name).text; + + // The first symbol name refers to the local name, which is replaced by `importedName` above. + // Any remaining symbol names make up the complete path to the value. + const [_localName, ...nestedPath] = symbols.symbolNames; + + const moduleName = extractModuleName(firstDecl.parent.parent.parent); + return { + local: false, + valueDeclaration: decl.valueDeclaration, + moduleName, + importedName, + nestedPath + }; + } else if (ts.isNamespaceImport(firstDecl)) { + // The import is a namespace import + // import * as Foo from 'foo'; + + if (symbols.symbolNames.length === 1) { + // The type refers to the namespace itself, which cannot be represented as a value. + return null; + } + + // The first symbol name refers to the local name of the namespace, which is is discarded + // as a new namespace import will be generated. This is followed by the symbol name that needs + // to be imported and any remaining names that constitute the complete path to the value. + const [_ns, importedName, ...nestedPath] = symbols.symbolNames; + + const moduleName = extractModuleName(firstDecl.parent.parent); + return { + local: false, + valueDeclaration: decl.valueDeclaration, + moduleName, + importedName, + nestedPath }; - } else { - return null; } } + + // If the type is not imported, the type reference can be converted into an expression as is. + const expression = typeNodeToValueExpr(typeNode); + if (expression !== null) { + return { + local: true, + expression, + defaultImportStatement: null, + }; + } else { + return null; + } } /** @@ -88,14 +133,14 @@ export function typeNodeToValueExpr(node: ts.TypeNode): ts.Expression|null { * * In the event that the `TypeReference` refers to a locally declared symbol, these will be the * same. If the `TypeReference` refers to an imported symbol, then `decl` will be the fully resolved - * `ts.Symbol` of the referenced symbol. `local` will be the `ts.Symbol` of the `ts.Identifer` which - * points to the import statement by which the symbol was imported. + * `ts.Symbol` of the referenced symbol. `local` will be the `ts.Symbol` of the `ts.Identifier` + * which points to the import statement by which the symbol was imported. * - * In the event `typeRef` refers to a default import, an `importName` will also be returned to - * give the identifier name within the current file by which the import is known. + * All symbol names that make up the type reference are returned left-to-right into the + * `symbolNames` array, which is guaranteed to include at least one entry. */ function resolveTypeSymbols(typeRef: ts.TypeReferenceNode, checker: ts.TypeChecker): - {local: ts.Symbol, decl: ts.Symbol, importName: string | null}|null { + {local: ts.Symbol, decl: ts.Symbol, symbolNames: string[]}|null { const typeName = typeRef.typeName; // typeRefSymbol is the ts.Symbol of the entire type reference. const typeRefSymbol: ts.Symbol|undefined = checker.getSymbolAtLocation(typeName); @@ -103,32 +148,36 @@ function resolveTypeSymbols(typeRef: ts.TypeReferenceNode, checker: ts.TypeCheck return null; } - // local is the ts.Symbol for the local ts.Identifier for the type. + // `local` is the `ts.Symbol` for the local `ts.Identifier` for the type. // If the type is actually locally declared or is imported by name, for example: // import {Foo} from './foo'; - // then it'll be the same as top. If the type is imported via a namespace import, for example: + // then it'll be the same as `typeRefSymbol`. + // + // If the type is imported via a namespace import, for example: // import * as foo from './foo'; // and then referenced as: // constructor(f: foo.Foo) - // then local will be the ts.Symbol of `foo`, whereas top will be the ts.Symbol of `foo.Foo`. - // This allows tracking of the import behind whatever type reference exists. + // then `local` will be the `ts.Symbol` of `foo`, whereas `typeRefSymbol` will be the `ts.Symbol` + // of `foo.Foo`. This allows tracking of the import behind whatever type reference exists. let local = typeRefSymbol; - let importName: string|null = null; - // TODO(alxhub): this is technically not correct. The user could have any import type with any - // amount of qualification following the imported type: - // - // import * as foo from 'foo' - // constructor(inject: foo.X.Y.Z) - // - // What we really want is the ability to express the arbitrary operation of `.X.Y.Z` on top of - // whatever import we generate for 'foo'. This logic is sufficient for now, though. - if (ts.isQualifiedName(typeName) && ts.isIdentifier(typeName.left) && - ts.isIdentifier(typeName.right)) { - const localTmp = checker.getSymbolAtLocation(typeName.left); + // Destructure a name like `foo.X.Y.Z` as follows: + // - in `leftMost`, the `ts.Identifier` of the left-most name (`foo`) in the qualified name. + // This identifier is used to resolve the `ts.Symbol` for `local`. + // - in `symbolNames`, all names involved in the qualified path, or a single symbol name if the + // type is not qualified. + let leftMost = typeName; + const symbolNames: string[] = []; + while (ts.isQualifiedName(leftMost)) { + symbolNames.unshift(leftMost.right.text); + leftMost = leftMost.left; + } + symbolNames.unshift(leftMost.text); + + if (leftMost !== typeName) { + const localTmp = checker.getSymbolAtLocation(leftMost); if (localTmp !== undefined) { local = localTmp; - importName = typeName.right.text; } } @@ -137,7 +186,7 @@ function resolveTypeSymbols(typeRef: ts.TypeReferenceNode, checker: ts.TypeCheck if (typeRefSymbol.flags & ts.SymbolFlags.Alias) { decl = checker.getAliasedSymbol(typeRefSymbol); } - return {local, decl, importName}; + return {local, decl, symbolNames}; } function entityNameToValue(node: ts.EntityName): ts.Expression|null { @@ -151,38 +200,9 @@ function entityNameToValue(node: ts.EntityName): ts.Expression|null { } } -function isImportSource(node: ts.Declaration): node is(ts.ImportSpecifier | ts.NamespaceImport) { - return ts.isImportSpecifier(node) || ts.isNamespaceImport(node); -} - -function extractModuleAndNameFromImport( - node: ts.ImportSpecifier | ts.NamespaceImport | ts.ImportClause, - localName: string | null): {name: string, moduleName: string} { - let name: string; - let moduleSpecifier: ts.Expression; - switch (node.kind) { - case ts.SyntaxKind.ImportSpecifier: - // The symbol was imported by name, in a ts.ImportSpecifier. - name = (node.propertyName || node.name).text; - moduleSpecifier = node.parent.parent.parent.moduleSpecifier; - break; - case ts.SyntaxKind.NamespaceImport: - // The symbol was imported via a namespace import. In this case, the name to use when - // importing it was extracted by resolveTypeSymbols. - if (localName === null) { - // resolveTypeSymbols() should have extracted the correct local name for the import. - throw new Error(`Debug failure: no local name provided for NamespaceImport`); - } - name = localName; - moduleSpecifier = node.parent.parent.moduleSpecifier; - break; - default: - throw new Error(`Unreachable: ${ts.SyntaxKind[(node as ts.Node).kind]}`); - } - - if (!ts.isStringLiteral(moduleSpecifier)) { +function extractModuleName(node: ts.ImportDeclaration): string { + if (!ts.isStringLiteral(node.moduleSpecifier)) { throw new Error('not a module specifier'); } - const moduleName = moduleSpecifier.text; - return {moduleName, name}; + return node.moduleSpecifier.text; } diff --git a/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts b/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts index c96ae69a09b63..ec66a14014c30 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, ReflectionHost, isDecoratorIdentifier} from './host'; +import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, isDecoratorIdentifier, ReflectionHost} from './host'; import {typeToValue} from './type_to_value'; /** @@ -76,8 +76,10 @@ export class TypeScriptReflectionHost implements ReflectionHost { return { name, - nameNode: node.name, typeValueReference, - typeNode: originalTypeNode, decorators, + nameNode: node.name, + typeValueReference, + typeNode: originalTypeNode, + decorators, }; }); } @@ -183,11 +185,17 @@ export class TypeScriptReflectionHost implements ReflectionHost { return declaration.initializer || null; } - getDtsDeclaration(_: ts.Declaration): ts.Declaration|null { return null; } + getDtsDeclaration(_: ts.Declaration): ts.Declaration|null { + return null; + } - getInternalNameOfClass(clazz: ClassDeclaration): ts.Identifier { return clazz.name; } + getInternalNameOfClass(clazz: ClassDeclaration): ts.Identifier { + return clazz.name; + } - getAdjacentNameOfClass(clazz: ClassDeclaration): ts.Identifier { return clazz.name; } + getAdjacentNameOfClass(clazz: ClassDeclaration): ts.Identifier { + return clazz.name; + } protected getDirectImportOfIdentifier(id: ts.Identifier): Import|null { const symbol = this.checker.getSymbolAtLocation(id); @@ -305,12 +313,14 @@ export class TypeScriptReflectionHost implements ReflectionHost { if (symbol.valueDeclaration !== undefined) { return { node: symbol.valueDeclaration, - known: null, viaModule, + known: null, + viaModule, }; } else if (symbol.declarations !== undefined && symbol.declarations.length > 0) { return { node: symbol.declarations[0], - known: null, viaModule, + known: null, + viaModule, }; } else { return null; @@ -342,7 +352,9 @@ export class TypeScriptReflectionHost implements ReflectionHost { return { name: decoratorIdentifier.text, identifier: decoratorExpr, - import: importDecl, node, args, + import: importDecl, + node, + args, }; } @@ -382,8 +394,14 @@ export class TypeScriptReflectionHost implements ReflectionHost { return { node, - implementation: node, kind, - type: node.type || null, name, nameNode, decorators, value, isStatic, + implementation: node, + kind, + type: node.type || null, + name, + nameNode, + decorators, + value, + isStatic, }; } } @@ -405,7 +423,7 @@ export function reflectIdentifierOfDeclaration(decl: ts.Declaration): ts.Identif } export function reflectTypeEntityToDeclaration( - type: ts.EntityName, checker: ts.TypeChecker): {node: ts.Declaration, from: string | null} { + type: ts.EntityName, checker: ts.TypeChecker): {node: ts.Declaration, from: string|null} { let realSymbol = checker.getSymbolAtLocation(type); if (realSymbol === undefined) { throw new Error(`Cannot resolve type entity ${type.getText()} to symbol`); @@ -434,8 +452,8 @@ export function reflectTypeEntityToDeclaration( } const decl = symbol.declarations[0]; if (ts.isNamespaceImport(decl)) { - const clause = decl.parent !; - const importDecl = clause.parent !; + const clause = decl.parent!; + const importDecl = clause.parent!; if (!ts.isStringLiteral(importDecl.moduleSpecifier)) { throw new Error(`Module specifier is not a string`); } @@ -552,7 +570,7 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I * `NamespaceImport`. If not return `null`. */ function getContainingImportDeclaration(node: ts.Node): ts.ImportDeclaration|null { - return ts.isImportSpecifier(node) ? node.parent !.parent !.parent ! : + return ts.isImportSpecifier(node) ? node.parent!.parent!.parent! : ts.isNamespaceImport(node) ? node.parent.parent : null; } diff --git a/packages/compiler-cli/src/ngtsc/reflection/test/ts_host_spec.ts b/packages/compiler-cli/src/ngtsc/reflection/test/ts_host_spec.ts index 9427afb37613a..0c072f24512e6 100644 --- a/packages/compiler-cli/src/ngtsc/reflection/test/ts_host_spec.ts +++ b/packages/compiler-cli/src/ngtsc/reflection/test/ts_host_spec.ts @@ -34,7 +34,7 @@ runInEachFileSystem(() => { const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration); const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); - const args = host.getConstructorParameters(clazz) !; + const args = host.getConstructorParameters(clazz)!; expect(args.length).toBe(1); expectParameter(args[0], 'bar', 'Bar'); }); @@ -63,7 +63,7 @@ runInEachFileSystem(() => { const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration); const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); - const args = host.getConstructorParameters(clazz) !; + const args = host.getConstructorParameters(clazz)!; expect(args.length).toBe(1); expectParameter(args[0], 'bar', 'Bar', 'dec', './dec'); }); @@ -92,7 +92,7 @@ runInEachFileSystem(() => { const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration); const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); - const args = host.getConstructorParameters(clazz) !; + const args = host.getConstructorParameters(clazz)!; expect(args.length).toBe(1); expectParameter(args[0], 'bar', 'Bar', 'dec', './dec'); }); @@ -120,7 +120,7 @@ runInEachFileSystem(() => { const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration); const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); - const args = host.getConstructorParameters(clazz) !; + const args = host.getConstructorParameters(clazz)!; expect(args.length).toBe(2); expectParameter(args[0], 'bar', {moduleName: './bar', name: 'Bar'}); expectParameter(args[1], 'otherBar', {moduleName: './bar', name: 'Bar'}); @@ -148,7 +148,7 @@ runInEachFileSystem(() => { const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration); const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); - const args = host.getConstructorParameters(clazz) !; + const args = host.getConstructorParameters(clazz)!; expect(args.length).toBe(1); expectParameter(args[0], 'bar', {moduleName: './bar', name: 'Bar'}); }); @@ -175,7 +175,7 @@ runInEachFileSystem(() => { const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration); const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); - const args = host.getConstructorParameters(clazz) !; + const args = host.getConstructorParameters(clazz)!; expect(args.length).toBe(1); const param = args[0].typeValueReference; if (param === null || !param.local) { @@ -207,7 +207,7 @@ runInEachFileSystem(() => { const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration); const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); - const args = host.getConstructorParameters(clazz) !; + const args = host.getConstructorParameters(clazz)!; expect(args.length).toBe(1); expectParameter(args[0], 'bar', {moduleName: './bar', name: 'Bar'}); }); @@ -228,12 +228,11 @@ runInEachFileSystem(() => { const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration); const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); - const args = host.getConstructorParameters(clazz) !; + const args = host.getConstructorParameters(clazz)!; expect(args.length).toBe(2); expectParameter(args[0], 'bar', 'Bar'); expectParameter(args[1], 'baz', 'Baz'); }); - }); @@ -330,9 +329,9 @@ runInEachFileSystem(() => { } else if (directTargetDecl === null) { return fail('No declaration found for DirectTarget'); } - expect(targetDecl.node !.getSourceFile().fileName) + expect(targetDecl.node!.getSourceFile().fileName) .toBe(_('/node_modules/absolute/index.ts')); - expect(ts.isClassDeclaration(targetDecl.node !)).toBe(true); + expect(ts.isClassDeclaration(targetDecl.node!)).toBe(true); expect(directTargetDecl.viaModule).toBe('absolute'); expect(directTargetDecl.node).toBe(targetDecl.node); }); @@ -415,9 +414,9 @@ runInEachFileSystem(() => { const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); const exportedDeclarations = - host.getExportsOfModule(program.getSourceFile(_('/entry.ts')) !); - expect(Array.from(exportedDeclarations !.keys())).toEqual(['foo', 'x', 'T', 'I', 'E']); - expect(Array.from(exportedDeclarations !.values()).map(v => v.viaModule)).toEqual([ + host.getExportsOfModule(program.getSourceFile(_('/entry.ts'))!); + expect(Array.from(exportedDeclarations!.keys())).toEqual(['foo', 'x', 'T', 'I', 'E']); + expect(Array.from(exportedDeclarations!.values()).map(v => v.viaModule)).toEqual([ null, null, null, null, null ]); }); @@ -440,11 +439,11 @@ runInEachFileSystem(() => { const checker = program.getTypeChecker(); const host = new TypeScriptReflectionHost(checker); const exportedDeclarations = - host.getExportsOfModule(program.getSourceFile(_('/entry.ts')) !); - expect(Array.from(exportedDeclarations !.keys())).toEqual([ + host.getExportsOfModule(program.getSourceFile(_('/entry.ts'))!); + expect(Array.from(exportedDeclarations!.keys())).toEqual([ 'Target1', 'AliasTarget', 'AliasTarget2', 'Target' ]); - expect(Array.from(exportedDeclarations !.values()).map(v => v.viaModule)).toEqual([ + expect(Array.from(exportedDeclarations!.values()).map(v => v.viaModule)).toEqual([ null, null, null, null ]); }); @@ -452,9 +451,9 @@ runInEachFileSystem(() => { }); function expectParameter( - param: CtorParameter, name: string, type?: string | {name: string, moduleName: string}, + param: CtorParameter, name: string, type?: string|{name: string, moduleName: string}, decorator?: string, decoratorFrom?: string): void { - expect(param.name !).toEqual(name); + expect(param.name!).toEqual(name); if (type === undefined) { expect(param.typeValueReference).toBeNull(); } else { @@ -465,23 +464,23 @@ runInEachFileSystem(() => { expect(argExpressionToString(param.typeValueReference.expression)).toEqual(type); } else if (!param.typeValueReference.local && typeof type !== 'string') { expect(param.typeValueReference.moduleName).toEqual(type.moduleName); - expect(param.typeValueReference.name).toEqual(type.name); + expect(param.typeValueReference.importedName).toEqual(type.name); } else { - return fail( - `Mismatch between typeValueReference and expected type: ${param.name} / ${param.typeValueReference.local}`); + return fail(`Mismatch between typeValueReference and expected type: ${param.name} / ${ + param.typeValueReference.local}`); } } if (decorator !== undefined) { expect(param.decorators).not.toBeNull(); - expect(param.decorators !.length).toBeGreaterThan(0); - expect(param.decorators !.some( + expect(param.decorators!.length).toBeGreaterThan(0); + expect(param.decorators!.some( dec => dec.name === decorator && dec.import !== null && dec.import.from === decoratorFrom)) .toBe(true); } } - function argExpressionToString(name: ts.Node | null): string { + function argExpressionToString(name: ts.Node|null): string { if (name == null) { throw new Error('\'name\' argument can\'t be null'); } diff --git a/packages/compiler-cli/src/ngtsc/resource/src/loader.ts b/packages/compiler-cli/src/ngtsc/resource/src/loader.ts index 041b8f0594e40..67a698b534724 100644 --- a/packages/compiler-cli/src/ngtsc/resource/src/loader.ts +++ b/packages/compiler-cli/src/ngtsc/resource/src/loader.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {ResourceLoader} from '../../annotations'; import {ExtendedTsCompilerHost} from '../../core/api'; -import {AbsoluteFsPath, PathSegment, join} from '../../file_system'; +import {AbsoluteFsPath, join, PathSegment} from '../../file_system'; import {getRootDirs} from '../../util/src/typescript'; const CSS_PREPROCESSOR_EXT = /(\.scss|\.sass|\.less|\.styl)$/; @@ -101,7 +101,7 @@ export class HostResourceLoader implements ResourceLoader { */ load(resolvedUrl: string): string { if (this.cache.has(resolvedUrl)) { - return this.cache.get(resolvedUrl) !; + return this.cache.get(resolvedUrl)!; } const result = this.host.readResource ? this.host.readResource(resolvedUrl) : @@ -169,14 +169,15 @@ export class HostResourceLoader implements ResourceLoader { // but is marked @internal in TypeScript. See // https://github.com/Microsoft/TypeScript/issues/28770. type ResolvedModuleWithFailedLookupLocations = - ts.ResolvedModuleWithFailedLookupLocations & {failedLookupLocations: ReadonlyArray<string>}; + ts.ResolvedModuleWithFailedLookupLocations&{failedLookupLocations: ReadonlyArray<string>}; // clang-format off const failedLookup = ts.resolveModuleName(url + '.$ngresource$', fromFile, this.options, this.host) as ResolvedModuleWithFailedLookupLocations; // clang-format on if (failedLookup.failedLookupLocations === undefined) { throw new Error( - `Internal error: expected to find failedLookupLocations during resolution of resource '${url}' in context of ${fromFile}`); + `Internal error: expected to find failedLookupLocations during resolution of resource '${ + url}' in context of ${fromFile}`); } return failedLookup.failedLookupLocations diff --git a/packages/compiler-cli/src/ngtsc/routing/src/analyzer.ts b/packages/compiler-cli/src/ngtsc/routing/src/analyzer.ts index d6ef4b4020c97..f51ffb6d0df3a 100644 --- a/packages/compiler-cli/src/ngtsc/routing/src/analyzer.ts +++ b/packages/compiler-cli/src/ngtsc/routing/src/analyzer.ts @@ -12,7 +12,7 @@ import {ModuleResolver} from '../../imports'; import {PartialEvaluator} from '../../partial_evaluator'; import {scanForCandidateTransitiveModules, scanForRouteEntryPoints} from './lazy'; -import {RouterEntryPointManager, entryPointKeyFor} from './route'; +import {entryPointKeyFor, RouterEntryPointManager} from './route'; export interface NgModuleRawRouteData { sourceFile: ts.SourceFile; @@ -42,10 +42,13 @@ export class NgModuleRouteAnalyzer { if (this.modules.has(key)) { throw new Error(`Double route analyzing for '${key}'.`); } - this.modules.set( - key, { - sourceFile, moduleName, imports, exports, providers, - }); + this.modules.set(key, { + sourceFile, + moduleName, + imports, + exports, + providers, + }); } listLazyRoutes(entryModuleKey?: string|undefined): LazyRoute[] { @@ -63,7 +66,7 @@ export class NgModuleRouteAnalyzer { const scanRecursively = entryModuleKey !== undefined; while (pendingModuleKeys.length > 0) { - const key = pendingModuleKeys.pop() !; + const key = pendingModuleKeys.pop()!; if (scannedModuleKeys.has(key)) { continue; @@ -71,7 +74,7 @@ export class NgModuleRouteAnalyzer { scannedModuleKeys.add(key); } - const data = this.modules.get(key) !; + const data = this.modules.get(key)!; const entryPoints = scanForRouteEntryPoints( data.sourceFile, data.moduleName, data, this.entryPointManager, this.evaluator); diff --git a/packages/compiler-cli/src/ngtsc/routing/src/lazy.ts b/packages/compiler-cli/src/ngtsc/routing/src/lazy.ts index 3e1eb580c30c5..39b4ba334cbd9 100644 --- a/packages/compiler-cli/src/ngtsc/routing/src/lazy.ts +++ b/packages/compiler-cli/src/ngtsc/routing/src/lazy.ts @@ -12,7 +12,7 @@ import {Reference} from '../../imports'; import {ForeignFunctionResolver, PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; import {NgModuleRawRouteData} from './analyzer'; -import {RouterEntryPoint, RouterEntryPointManager, entryPointKeyFor} from './route'; +import {entryPointKeyFor, RouterEntryPoint, RouterEntryPointManager} from './route'; const ROUTES_MARKER = '__ngRoutesMarker__'; @@ -23,7 +23,7 @@ export interface LazyRouteEntry { } export function scanForCandidateTransitiveModules( - expr: ts.Expression | null, evaluator: PartialEvaluator): string[] { + expr: ts.Expression|null, evaluator: PartialEvaluator): string[] { if (expr === null) { return []; } @@ -38,7 +38,7 @@ export function scanForCandidateTransitiveModules( } } else if (entry instanceof Map) { if (entry.has('ngModule')) { - recursivelyAddModules(entry.get('ngModule') !); + recursivelyAddModules(entry.get('ngModule')!); } } else if ((entry instanceof Reference) && hasIdentifier(entry.node)) { const filePath = entry.node.getSourceFile().fileName; @@ -70,7 +70,9 @@ export function scanForRouteEntryPoints( const resolvedTo = entryPointManager.resolveLoadChildrenIdentifier(loadChildren, ngModule); if (resolvedTo !== null) { routes.push({ - loadChildren, from, resolvedTo, + loadChildren, + from, + resolvedTo, }); } } @@ -159,29 +161,27 @@ function scanForLazyRoutes(routes: ResolvedValue[]): string[] { const routerModuleFFR: ForeignFunctionResolver = function routerModuleFFR( ref: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>, - args: ReadonlyArray<ts.Expression>): ts.Expression | - null { - if (!isMethodNodeReference(ref) || !ts.isClassDeclaration(ref.node.parent)) { - return null; - } else if ( - ref.bestGuessOwningModule === null || - ref.bestGuessOwningModule.specifier !== '@angular/router') { - return null; - } else if ( - ref.node.parent.name === undefined || ref.node.parent.name.text !== 'RouterModule') { - return null; - } else if ( - !ts.isIdentifier(ref.node.name) || - (ref.node.name.text !== 'forRoot' && ref.node.name.text !== 'forChild')) { - return null; - } + args: ReadonlyArray<ts.Expression>): ts.Expression|null { + if (!isMethodNodeReference(ref) || !ts.isClassDeclaration(ref.node.parent)) { + return null; + } else if ( + ref.bestGuessOwningModule === null || + ref.bestGuessOwningModule.specifier !== '@angular/router') { + return null; + } else if (ref.node.parent.name === undefined || ref.node.parent.name.text !== 'RouterModule') { + return null; + } else if ( + !ts.isIdentifier(ref.node.name) || + (ref.node.name.text !== 'forRoot' && ref.node.name.text !== 'forChild')) { + return null; + } - const routes = args[0]; - return ts.createObjectLiteral([ - ts.createPropertyAssignment(ROUTES_MARKER, ts.createTrue()), - ts.createPropertyAssignment('routes', routes), - ]); - }; + const routes = args[0]; + return ts.createObjectLiteral([ + ts.createPropertyAssignment(ROUTES_MARKER, ts.createTrue()), + ts.createPropertyAssignment('routes', routes), + ]); +}; function hasIdentifier(node: ts.Node): node is ts.Node&{name: ts.Identifier} { const node_ = node as ts.NamedDeclaration; diff --git a/packages/compiler-cli/src/ngtsc/routing/src/route.ts b/packages/compiler-cli/src/ngtsc/routing/src/route.ts index 2764b09de3855..d6f11b9a6e5b0 100644 --- a/packages/compiler-cli/src/ngtsc/routing/src/route.ts +++ b/packages/compiler-cli/src/ngtsc/routing/src/route.ts @@ -22,10 +22,14 @@ export abstract class RouterEntryPoint { class RouterEntryPointImpl implements RouterEntryPoint { constructor(readonly filePath: string, readonly moduleName: string) {} - get name(): string { return this.moduleName; } + get name(): string { + return this.moduleName; + } // For debugging purposes. - toString(): string { return `RouterEntryPoint(name: ${this.name}, filePath: ${this.filePath})`; } + toString(): string { + return `RouterEntryPoint(name: ${this.name}, filePath: ${this.filePath})`; + } } export class RouterEntryPointManager { @@ -51,7 +55,7 @@ export class RouterEntryPointManager { if (!this.map.has(key)) { this.map.set(key, new RouterEntryPointImpl(sf.fileName, moduleName)); } - return this.map.get(key) !; + return this.map.get(key)!; } } diff --git a/packages/compiler-cli/src/ngtsc/scope/src/dependency.ts b/packages/compiler-cli/src/ngtsc/scope/src/dependency.ts index ab441af285fb2..94479f97b96ed 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/dependency.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/dependency.ts @@ -47,12 +47,12 @@ export class MetadataDtsModuleScopeResolver implements DtsModuleScopeResolver { const clazz = ref.node; const sourceFile = clazz.getSourceFile(); if (!sourceFile.isDeclarationFile) { - throw new Error( - `Debug error: DtsModuleScopeResolver.read(${ref.debugName} from ${sourceFile.fileName}), but not a .d.ts file`); + throw new Error(`Debug error: DtsModuleScopeResolver.read(${ref.debugName} from ${ + sourceFile.fileName}), but not a .d.ts file`); } if (this.cache.has(clazz)) { - return this.cache.get(clazz) !; + return this.cache.get(clazz)!; } // Build up the export scope - those directives and pipes made visible by this module. diff --git a/packages/compiler-cli/src/ngtsc/scope/src/local.ts b/packages/compiler-cli/src/ngtsc/scope/src/local.ts index ad505ed1b64c5..5cc992b4e98b7 100644 --- a/packages/compiler-cli/src/ngtsc/scope/src/local.ts +++ b/packages/compiler-cli/src/ngtsc/scope/src/local.ts @@ -143,7 +143,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null|'error' { const scope = !this.declarationToModule.has(clazz) ? null : - this.getScopeOfModule(this.declarationToModule.get(clazz) !.ngModule); + this.getScopeOfModule(this.declarationToModule.get(clazz)!.ngModule); return scope; } @@ -159,7 +159,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop return null; } - return Array.from(this.duplicateDeclarations.get(node) !.values()); + return Array.from(this.duplicateDeclarations.get(node)!.values()); } /** @@ -172,7 +172,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop */ getScopeOfModule(clazz: ClassDeclaration): LocalModuleScope|'error'|null { const scope = this.moduleToRef.has(clazz) ? - this.getScopeOfModuleReference(this.moduleToRef.get(clazz) !) : + this.getScopeOfModuleReference(this.moduleToRef.get(clazz)!) : null; // If the NgModule class is marked as tainted, consider it an error. if (this.taintedModules.has(clazz)) { @@ -193,7 +193,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop this.getScopeOfModule(clazz); if (this.scopeErrors.has(clazz)) { - return this.scopeErrors.get(clazz) !; + return this.scopeErrors.get(clazz)!; } else { return null; } @@ -218,21 +218,22 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop rawDeclarations: ts.Expression|null): void { const declData: DeclarationData = { ngModule, - ref: decl, rawDeclarations, + ref: decl, + rawDeclarations, }; // First, check for duplicate declarations of the same directive/pipe. if (this.duplicateDeclarations.has(decl.node)) { // This directive/pipe has already been identified as being duplicated. Add this module to the // map of modules for which a duplicate declaration exists. - this.duplicateDeclarations.get(decl.node) !.set(ngModule, declData); + this.duplicateDeclarations.get(decl.node)!.set(ngModule, declData); } else if ( this.declarationToModule.has(decl.node) && - this.declarationToModule.get(decl.node) !.ngModule !== ngModule) { + this.declarationToModule.get(decl.node)!.ngModule !== ngModule) { // This directive/pipe is already registered as declared in another module. Mark it as a // duplicate instead. const duplicateDeclMap = new Map<ClassDeclaration, DeclarationData>(); - const firstDeclData = this.declarationToModule.get(decl.node) !; + const firstDeclData = this.declarationToModule.get(decl.node)!; // Mark both modules as tainted, since their declarations are missing a component. this.taintedModules.add(firstDeclData.ngModule); @@ -341,11 +342,13 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop } else { this.taintedModules.add(ngModule.ref.node); - const errorNode = decl.getOriginForDiagnostics(ngModule.rawDeclarations !); + const errorNode = decl.getOriginForDiagnostics(ngModule.rawDeclarations!); diagnostics.push(makeDiagnostic( ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `The class '${decl.node.name.text}' is listed in the declarations ` + - `of the NgModule '${ngModule.ref.node.name.text}', but is not a directive, a component, or a pipe. ` + + `of the NgModule '${ + ngModule.ref.node.name + .text}', but is not a directive, a component, or a pipe. ` + `Either remove it from the NgModule's declarations, or add an appropriate Angular decorator.`, [{node: decl.node.name, messageText: `'${decl.node.name.text}' is declared here.`}])); continue; @@ -378,11 +381,11 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop } } else if (compilationDirectives.has(decl.node)) { // decl is a directive or component in the compilation scope of this NgModule. - const directive = compilationDirectives.get(decl.node) !; + const directive = compilationDirectives.get(decl.node)!; exportDirectives.set(decl.node, directive); } else if (compilationPipes.has(decl.node)) { // decl is a pipe in the compilation scope of this NgModule. - const pipe = compilationPipes.get(decl.node) !; + const pipe = compilationPipes.get(decl.node)!; exportPipes.set(decl.node, pipe); } else { // decl is an unknown export. @@ -433,7 +436,9 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop /** * Check whether a component requires remote scoping. */ - getRequiresRemoteScope(node: ClassDeclaration): boolean { return this.remoteScoping.has(node); } + getRequiresRemoteScope(node: ClassDeclaration): boolean { + return this.remoteScoping.has(node); + } /** * Set a component as requiring remote scoping. @@ -466,7 +471,8 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop ErrorCode.NGMODULE_INVALID_EXPORT; diagnostics.push(makeDiagnostic( code, identifierOfNode(ref.node) || ref.node, - `Appears in the NgModule.${type}s of ${nodeNameForError(ownerForErrors)}, but could not be resolved to an NgModule`)); + `Appears in the NgModule.${type}s of ${ + nodeNameForError(ownerForErrors)}, but could not be resolved to an NgModule`)); return undefined; } return this.dependencyScopeReader.resolve(ref); @@ -496,16 +502,16 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop return; } const isReExport = !declared.has(exportRef.node); - const exportName = this.aliasingHost !.maybeAliasSymbolAs( + const exportName = this.aliasingHost!.maybeAliasSymbolAs( exportRef, sourceFile, ngModule.ref.node.name.text, isReExport); if (exportName === null) { return; } if (!reexportMap.has(exportName)) { if (exportRef.alias && exportRef.alias instanceof ExternalExpr) { - reexports !.push({ - fromModule: exportRef.alias.value.moduleName !, - symbolName: exportRef.alias.value.name !, + reexports!.push({ + fromModule: exportRef.alias.value.moduleName!, + symbolName: exportRef.alias.value.name!, asAlias: exportName, }); } else { @@ -514,7 +520,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop expr.value.name === null) { throw new Error('Expected ExternalExpr'); } - reexports !.push({ + reexports!.push({ fromModule: expr.value.moduleName, symbolName: expr.value.name, asAlias: exportName, @@ -523,7 +529,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop reexportMap.set(exportName, exportRef); } else { // Another re-export already used this name. Produce a diagnostic. - const prevRef = reexportMap.get(exportName) !; + const prevRef = reexportMap.get(exportName)!; diagnostics.push(reexportCollision(ngModuleRef.node, prevRef, exportRef)); } }; @@ -548,12 +554,13 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop */ function invalidRef( clazz: ts.Declaration, decl: Reference<ts.Declaration>, - type: 'import' | 'export'): ts.Diagnostic { + type: 'import'|'export'): ts.Diagnostic { const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT; const resolveTarget = type === 'import' ? 'NgModule' : 'NgModule, Component, Directive, or Pipe'; let message = - `Appears in the NgModule.${type}s of ${nodeNameForError(clazz)}, but could not be resolved to an ${resolveTarget} class.` + + `Appears in the NgModule.${type}s of ${ + nodeNameForError(clazz)}, but could not be resolved to an ${resolveTarget} class.` + '\n\n'; const library = decl.ownedByModuleGuess !== null ? ` (${decl.ownedByModuleGuess})` : ''; const sf = decl.node.getSourceFile(); @@ -573,8 +580,8 @@ function invalidRef( } else { // This is a monorepo style local dependency. Unfortunately these are too different to really // offer much more advice than this. - message += - `This likely means that the dependency${library} which declares ${decl.debugName} has not been processed correctly by ngcc.`; + message += `This likely means that the dependency${library} which declares ${ + decl.debugName} has not been processed correctly by ngcc.`; } return makeDiagnostic(code, identifierOfNode(decl.node) || decl.node, message); @@ -585,7 +592,7 @@ function invalidRef( */ function invalidTransitiveNgModuleRef( clazz: ts.Declaration, decl: Reference<ts.Declaration>, - type: 'import' | 'export'): ts.Diagnostic { + type: 'import'|'export'): ts.Diagnostic { const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT; return makeDiagnostic( @@ -600,7 +607,8 @@ function invalidTransitiveNgModuleRef( function invalidReexport(clazz: ts.Declaration, decl: Reference<ts.Declaration>): ts.Diagnostic { return makeDiagnostic( ErrorCode.NGMODULE_INVALID_REEXPORT, identifierOfNode(decl.node) || decl.node, - `Present in the NgModule.exports of ${nodeNameForError(clazz)} but neither declared nor imported`); + `Present in the NgModule.exports of ${ + nodeNameForError(clazz)} but neither declared nor imported`); } /** @@ -609,11 +617,13 @@ function invalidReexport(clazz: ts.Declaration, decl: Reference<ts.Declaration>) function reexportCollision( module: ClassDeclaration, refA: Reference<ClassDeclaration>, refB: Reference<ClassDeclaration>): ts.Diagnostic { - const childMessageText = - `This directive/pipe is part of the exports of '${module.name.text}' and shares the same name as another exported directive/pipe.`; + const childMessageText = `This directive/pipe is part of the exports of '${ + module.name.text}' and shares the same name as another exported directive/pipe.`; return makeDiagnostic( - ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION, module.name, ` - There was a name collision between two classes named '${refA.node.name.text}', which are both part of the exports of '${module.name.text}'. + ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION, module.name, + ` + There was a name collision between two classes named '${ + refA.node.name.text}', which are both part of the exports of '${module.name.text}'. Angular generates re-exports of an NgModule's exported directives/pipes from the module's source file in certain cases, using the declared name of the class. If two classes of the same name are exported, this automatic naming does not work. diff --git a/packages/compiler-cli/src/ngtsc/scope/test/dependency_spec.ts b/packages/compiler-cli/src/ngtsc/scope/test/dependency_spec.ts index 5e5e36522a2b1..512d5bb966282 100644 --- a/packages/compiler-cli/src/ngtsc/scope/test/dependency_spec.ts +++ b/packages/compiler-cli/src/ngtsc/scope/test/dependency_spec.ts @@ -22,7 +22,7 @@ const MODULE_FROM_NODE_MODULES_PATH = /.*node_modules\/(\w+)\/index\.d\.ts$/; const testHost: UnifiedModulesHost = { fileNameToModuleName: function(imported: string): string { - const res = MODULE_FROM_NODE_MODULES_PATH.exec(imported) !; + const res = MODULE_FROM_NODE_MODULES_PATH.exec(imported)!; return 'root/' + res[1]; } }; @@ -44,7 +44,7 @@ export declare type PipeMeta<A, B> = never; * destructured to retrieve references to specific declared classes. */ function makeTestEnv( - modules: {[module: string]: string}, aliasGenerator: AliasingHost | null = null): { + modules: {[module: string]: string}, aliasGenerator: AliasingHost|null = null): { refs: {[name: string]: Reference<ClassDeclaration>}, resolver: MetadataDtsModuleScopeResolver, } { @@ -64,11 +64,11 @@ function makeTestEnv( // Resolver for the refs object. const get = (target: {}, name: string): Reference<ts.ClassDeclaration> => { for (const sf of program.getSourceFiles()) { - const symbol = checker.getSymbolAtLocation(sf) !; - const exportedSymbol = symbol.exports !.get(name as ts.__String); + const symbol = checker.getSymbolAtLocation(sf)!; + const exportedSymbol = symbol.exports!.get(name as ts.__String); if (exportedSymbol !== undefined) { const decl = exportedSymbol.valueDeclaration as ts.ClassDeclaration; - const specifier = MODULE_FROM_NODE_MODULES_PATH.exec(sf.fileName) ![1]; + const specifier = MODULE_FROM_NODE_MODULES_PATH.exec(sf.fileName)![1]; return new Reference(decl, {specifier, resolutionContext: sf.fileName}); } } @@ -97,7 +97,7 @@ runInEachFileSystem(() => { ` }); const {Dir, Module} = refs; - const scope = resolver.resolve(Module) !; + const scope = resolver.resolve(Module)!; expect(scopeToRefs(scope)).toEqual([Dir]); }); @@ -118,7 +118,7 @@ runInEachFileSystem(() => { ` }); const {Dir, ModuleB} = refs; - const scope = resolver.resolve(ModuleB) !; + const scope = resolver.resolve(ModuleB)!; expect(scopeToRefs(scope)).toEqual([Dir]); }); @@ -142,7 +142,7 @@ runInEachFileSystem(() => { ` }); const {Dir, ModuleB} = refs; - const scope = resolver.resolve(ModuleB) !; + const scope = resolver.resolve(ModuleB)!; expect(scopeToRefs(scope)).toEqual([Dir]); // Explicitly verify that the directive has the correct owning module. @@ -186,7 +186,7 @@ runInEachFileSystem(() => { }, new UnifiedModulesAliasingHost(testHost)); const {ShallowModule} = refs; - const scope = resolver.resolve(ShallowModule) !; + const scope = resolver.resolve(ShallowModule)!; const [DeepDir, MiddleDir, ShallowDir] = scopeToRefs(scope); expect(getAlias(DeepDir)).toEqual({ moduleName: 'root/shallow', @@ -236,7 +236,7 @@ runInEachFileSystem(() => { }, new UnifiedModulesAliasingHost(testHost)); const {ShallowModule} = refs; - const scope = resolver.resolve(ShallowModule) !; + const scope = resolver.resolve(ShallowModule)!; const [DeepDir, MiddleDir, ShallowDir] = scopeToRefs(scope); expect(getAlias(DeepDir)).toEqual({ moduleName: 'root/shallow', @@ -269,7 +269,7 @@ runInEachFileSystem(() => { }, new UnifiedModulesAliasingHost(testHost)); const {DeepExportModule} = refs; - const scope = resolver.resolve(DeepExportModule) !; + const scope = resolver.resolve(DeepExportModule)!; const [DeepDir] = scopeToRefs(scope); expect(getAlias(DeepDir)).toBeNull(); }); @@ -278,7 +278,7 @@ runInEachFileSystem(() => { function scopeToRefs(scope: ExportScope): Reference<ClassDeclaration>[] { const directives = scope.exported.directives.map(dir => dir.ref); const pipes = scope.exported.pipes.map(pipe => pipe.ref); - return [...directives, ...pipes].sort((a, b) => a.debugName !.localeCompare(b.debugName !)); + return [...directives, ...pipes].sort((a, b) => a.debugName!.localeCompare(b.debugName!)); } function getAlias(ref: Reference<ClassDeclaration>): ExternalReference|null { diff --git a/packages/compiler-cli/src/ngtsc/scope/test/local_spec.ts b/packages/compiler-cli/src/ngtsc/scope/test/local_spec.ts index 05d06dda5e91f..a496754060932 100644 --- a/packages/compiler-cli/src/ngtsc/scope/test/local_spec.ts +++ b/packages/compiler-cli/src/ngtsc/scope/test/local_spec.ts @@ -34,8 +34,8 @@ function registerFakeRefs(registry: MetadataRegistry): describe('LocalModuleScopeRegistry', () => { const refEmitter = new ReferenceEmitter([]); - let scopeRegistry !: LocalModuleScopeRegistry; - let metaRegistry !: MetadataRegistry; + let scopeRegistry!: LocalModuleScopeRegistry; + let metaRegistry!: MetadataRegistry; beforeEach(() => { const localRegistry = new LocalMetadataRegistry(); @@ -230,7 +230,7 @@ describe('LocalModuleScopeRegistry', () => { }); function fakeDirective(ref: Reference<ClassDeclaration>): DirectiveMeta { - const name = ref.debugName !; + const name = ref.debugName!; return { ref, name, @@ -248,16 +248,18 @@ function fakeDirective(ref: Reference<ClassDeclaration>): DirectiveMeta { } function fakePipe(ref: Reference<ClassDeclaration>): PipeMeta { - const name = ref.debugName !; + const name = ref.debugName!; return {ref, name}; } class MockDtsModuleScopeResolver implements DtsModuleScopeResolver { - resolve(ref: Reference<ClassDeclaration>): null { return null; } + resolve(ref: Reference<ClassDeclaration>): null { + return null; + } } function scopeToRefs(scopeData: ScopeData): Reference<ClassDeclaration>[] { const directives = scopeData.directives.map(dir => dir.ref); const pipes = scopeData.pipes.map(pipe => pipe.ref); - return [...directives, ...pipes].sort((a, b) => a.debugName !.localeCompare(b.debugName !)); + return [...directives, ...pipes].sort((a, b) => a.debugName!.localeCompare(b.debugName!)); } diff --git a/packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts b/packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts index 426687636ca97..4bca52c413ce3 100644 --- a/packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts +++ b/packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts @@ -7,7 +7,7 @@ */ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom, basename} from '../../file_system'; +import {absoluteFrom, AbsoluteFsPath, basename} from '../../file_system'; import {ImportRewriter} from '../../imports'; import {isNonDeclarationTsPath} from '../../util/src/typescript'; @@ -24,15 +24,21 @@ const STRIP_NG_FACTORY = /(.*)NgFactory$/; export class FactoryGenerator implements ShimGenerator { private constructor(private map: Map<AbsoluteFsPath, AbsoluteFsPath>) {} - get factoryFileMap(): Map<AbsoluteFsPath, AbsoluteFsPath> { return this.map; } + get factoryFileMap(): Map<AbsoluteFsPath, AbsoluteFsPath> { + return this.map; + } - get factoryFileNames(): AbsoluteFsPath[] { return Array.from(this.map.keys()); } + get factoryFileNames(): AbsoluteFsPath[] { + return Array.from(this.map.keys()); + } - recognize(fileName: AbsoluteFsPath): boolean { return this.map.has(fileName); } + recognize(fileName: AbsoluteFsPath): boolean { + return this.map.has(fileName); + } generate(genFilePath: AbsoluteFsPath, readFile: (fileName: string) => ts.SourceFile | null): ts.SourceFile|null { - const originalPath = this.map.get(genFilePath) !; + const originalPath = this.map.get(genFilePath)!; const original = readFile(originalPath); if (original === null) { return null; @@ -53,7 +59,7 @@ export class FactoryGenerator implements ShimGenerator { decl => isExported(decl) && decl.decorators !== undefined && decl.name !== undefined) // Grab the symbol name. - .map(decl => decl.name !.text); + .map(decl => decl.name!.text); let sourceText = ''; @@ -71,8 +77,8 @@ export class FactoryGenerator implements ShimGenerator { // This will encompass a lot of symbols which don't need factories, but that's okay // because it won't miss any that do. const varLines = symbolNames.map( - name => - `export const ${name}NgFactory: i0.ɵNgModuleFactory<any> = new i0.ɵNgModuleFactory(${name});`); + name => `export const ${ + name}NgFactory: i0.ɵNgModuleFactory<any> = new i0.ɵNgModuleFactory(${name});`); sourceText += [ // This might be incorrect if the current package being compiled is Angular core, but it's // okay to leave in at type checking time. TypeScript can handle this reference via its path @@ -136,7 +142,7 @@ function transformFactorySourceFile( return file; } - const {moduleSymbolNames, sourceFilePath} = factoryMap.get(file.fileName) !; + const {moduleSymbolNames, sourceFilePath} = factoryMap.get(file.fileName)!; file = ts.getMutableClone(file); diff --git a/packages/compiler-cli/src/ngtsc/shims/src/factory_tracker.ts b/packages/compiler-cli/src/ngtsc/shims/src/factory_tracker.ts index a9b58b37e4593..a4ccb13302078 100644 --- a/packages/compiler-cli/src/ngtsc/shims/src/factory_tracker.ts +++ b/packages/compiler-cli/src/ngtsc/shims/src/factory_tracker.ts @@ -32,7 +32,7 @@ export class FactoryTracker { track(sf: ts.SourceFile, factorySymbolName: string): void { if (this.sourceToFactorySymbols.has(sf.fileName)) { - this.sourceToFactorySymbols.get(sf.fileName) !.add(factorySymbolName); + this.sourceToFactorySymbols.get(sf.fileName)!.add(factorySymbolName); } } } diff --git a/packages/compiler-cli/src/ngtsc/shims/src/summary_generator.ts b/packages/compiler-cli/src/ngtsc/shims/src/summary_generator.ts index f3eb5c10c7a0d..ab6caddcdf887 100644 --- a/packages/compiler-cli/src/ngtsc/shims/src/summary_generator.ts +++ b/packages/compiler-cli/src/ngtsc/shims/src/summary_generator.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom} from '../../file_system'; +import {absoluteFrom, AbsoluteFsPath} from '../../file_system'; import {isNonDeclarationTsPath} from '../../util/src/typescript'; import {ShimGenerator} from './api'; @@ -17,13 +17,17 @@ import {generatedModuleName} from './util'; export class SummaryGenerator implements ShimGenerator { private constructor(private map: Map<AbsoluteFsPath, AbsoluteFsPath>) {} - getSummaryFileNames(): AbsoluteFsPath[] { return Array.from(this.map.keys()); } + getSummaryFileNames(): AbsoluteFsPath[] { + return Array.from(this.map.keys()); + } - recognize(fileName: AbsoluteFsPath): boolean { return this.map.has(fileName); } + recognize(fileName: AbsoluteFsPath): boolean { + return this.map.has(fileName); + } generate(genFilePath: AbsoluteFsPath, readFile: (fileName: string) => ts.SourceFile | null): ts.SourceFile|null { - const originalPath = this.map.get(genFilePath) !; + const originalPath = this.map.get(genFilePath)!; const original = readFile(originalPath); if (original === null) { return null; diff --git a/packages/compiler-cli/src/ngtsc/shims/src/typecheck_shim.ts b/packages/compiler-cli/src/ngtsc/shims/src/typecheck_shim.ts index 8f9d5247db2cc..57ebf91b54700 100644 --- a/packages/compiler-cli/src/ngtsc/shims/src/typecheck_shim.ts +++ b/packages/compiler-cli/src/ngtsc/shims/src/typecheck_shim.ts @@ -22,7 +22,9 @@ import {ShimGenerator} from './api'; export class TypeCheckShimGenerator implements ShimGenerator { constructor(private typeCheckFile: AbsoluteFsPath) {} - recognize(fileName: AbsoluteFsPath): boolean { return fileName === this.typeCheckFile; } + recognize(fileName: AbsoluteFsPath): boolean { + return fileName === this.typeCheckFile; + } generate(genFileName: AbsoluteFsPath, readFile: (fileName: string) => ts.SourceFile | null): ts.SourceFile|null { diff --git a/packages/compiler-cli/src/ngtsc/switch/src/switch.ts b/packages/compiler-cli/src/ngtsc/switch/src/switch.ts index 017458bcab9b5..ea1619c9e3380 100644 --- a/packages/compiler-cli/src/ngtsc/switch/src/switch.ts +++ b/packages/compiler-cli/src/ngtsc/switch/src/switch.ts @@ -107,8 +107,8 @@ function flipIvySwitchesInVariableStatement( // reported as a thrown error and not a diagnostic as transformers cannot output diagnostics. let newIdentifier = findPostSwitchIdentifier(statements, postSwitchName); if (newIdentifier === null) { - throw new Error( - `Unable to find identifier ${postSwitchName} in ${stmt.getSourceFile().fileName} for the Ivy switch.`); + throw new Error(`Unable to find identifier ${postSwitchName} in ${ + stmt.getSourceFile().fileName} for the Ivy switch.`); } // Copy the identifier with updateIdentifier(). This copies the internal information which diff --git a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts index a71b7d21c3f54..b50751f088dd3 100644 --- a/packages/compiler-cli/src/ngtsc/testing/src/utils.ts +++ b/packages/compiler-cli/src/ngtsc/testing/src/utils.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; -import {AbsoluteFsPath, NgtscCompilerHost, dirname, getFileSystem, getSourceFileOrError} from '../../file_system'; +import {AbsoluteFsPath, dirname, getFileSystem, getSourceFileOrError, NgtscCompilerHost} from '../../file_system'; export function makeProgram( files: {name: AbsoluteFsPath, contents: string, isRoot?: boolean}[], @@ -25,7 +25,8 @@ export function makeProgram( const compilerOptions = { noLib: true, experimentalDecorators: true, - moduleResolution: ts.ModuleResolutionKind.NodeJs, ...options + moduleResolution: ts.ModuleResolutionKind.NodeJs, + ...options }; const compilerHost = new NgtscCompilerHost(fs, compilerOptions); const rootNames = files.filter(file => file.isRoot !== false) @@ -38,7 +39,7 @@ export function makeProgram( let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); if (diagnostic.file) { const {line, character} = - diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start !); + diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); message = `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`; } return `Error: ${message}`; diff --git a/packages/compiler-cli/src/ngtsc/transform/src/alias.ts b/packages/compiler-cli/src/ngtsc/transform/src/alias.ts index f7698f29e6cbe..5cba3e6d50b1a 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/alias.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/alias.ts @@ -17,7 +17,7 @@ export function aliasTransformFactory(exportStatements: Map<string, Map<string, } const statements = [...file.statements]; - exportStatements.get(file.fileName) !.forEach(([moduleName, symbolName], aliasName) => { + exportStatements.get(file.fileName)!.forEach(([moduleName, symbolName], aliasName) => { const stmt = ts.createExportDeclaration( /* decorators */ undefined, /* modifiers */ undefined, diff --git a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts index e103aadd316ae..250360d70e978 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts @@ -94,9 +94,13 @@ export class TraitCompiler { } } - analyzeSync(sf: ts.SourceFile): void { this.analyze(sf, false); } + analyzeSync(sf: ts.SourceFile): void { + this.analyze(sf, false); + } - analyzeAsync(sf: ts.SourceFile): Promise<void>|undefined { return this.analyze(sf, true); } + analyzeAsync(sf: ts.SourceFile): Promise<void>|undefined { + return this.analyze(sf, true); + } private analyze(sf: ts.SourceFile, preanalyze: false): void; private analyze(sf: ts.SourceFile, preanalyze: true): Promise<void>|undefined; @@ -138,7 +142,7 @@ export class TraitCompiler { recordFor(clazz: ClassDeclaration): ClassRecord|null { if (this.classes.has(clazz)) { - return this.classes.get(clazz) !; + return this.classes.get(clazz)!; } else { return null; } @@ -149,8 +153,8 @@ export class TraitCompiler { return null; } const records: ClassRecord[] = []; - for (const clazz of this.fileToClasses.get(sf) !) { - records.push(this.classes.get(clazz) !); + for (const clazz of this.fileToClasses.get(sf)!) { + records.push(this.classes.get(clazz)!); } return records; } @@ -173,7 +177,7 @@ export class TraitCompiler { }; for (const priorTrait of priorRecord.traits) { - const handler = this.handlersByName.get(priorTrait.handler.name) !; + const handler = this.handlersByName.get(priorTrait.handler.name)!; let trait: Trait<unknown, unknown, unknown> = Trait.pending(handler, priorTrait.detected); if (priorTrait.state === TraitState.ANALYZED || priorTrait.state === TraitState.RESOLVED) { @@ -195,7 +199,7 @@ export class TraitCompiler { if (!this.fileToClasses.has(sf)) { this.fileToClasses.set(sf, new Set<ClassDeclaration>()); } - this.fileToClasses.get(sf) !.add(record.node); + this.fileToClasses.get(sf)!.add(record.node); } private scanClassForTraits(clazz: ClassDeclaration): @@ -242,7 +246,7 @@ export class TraitCompiler { if (!this.fileToClasses.has(sf)) { this.fileToClasses.set(sf, new Set<ClassDeclaration>()); } - this.fileToClasses.get(sf) !.add(clazz); + this.fileToClasses.get(sf)!.add(clazz); } else { // This is at least the second handler to match this class. This is a slower path that some // classes will go through, which validates that the set of decorators applied to the class @@ -317,7 +321,7 @@ export class TraitCompiler { } } if (preanalysis !== null) { - preanalyzeQueue !.push(preanalysis.then(analyze)); + preanalyzeQueue!.push(preanalysis.then(analyze)); } else { analyze(); } @@ -328,8 +332,8 @@ export class TraitCompiler { clazz: ClassDeclaration, trait: Trait<unknown, unknown, unknown>, flags?: HandlerFlags): void { if (trait.state !== TraitState.PENDING) { - throw new Error( - `Attempt to analyze trait of ${clazz.name.text} in state ${TraitState[trait.state]} (expected DETECTED)`); + throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${ + TraitState[trait.state]} (expected DETECTED)`); } // Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does. @@ -363,7 +367,7 @@ export class TraitCompiler { resolve(): void { const classes = Array.from(this.classes.keys()); for (const clazz of classes) { - const record = this.classes.get(clazz) !; + const record = this.classes.get(clazz)!; for (let trait of record.traits) { const handler = trait.handler; switch (trait.state) { @@ -371,8 +375,8 @@ export class TraitCompiler { case TraitState.ERRORED: continue; case TraitState.PENDING: - throw new Error( - `Resolving a trait that hasn't been analyzed: ${clazz.name.text} / ${Object.getPrototypeOf(trait.handler).constructor.name}`); + throw new Error(`Resolving a trait that hasn't been analyzed: ${clazz.name.text} / ${ + Object.getPrototypeOf(trait.handler).constructor.name}`); case TraitState.RESOLVED: throw new Error(`Resolving an already resolved trait`); } @@ -410,7 +414,7 @@ export class TraitCompiler { if (!this.reexportMap.has(fileName)) { this.reexportMap.set(fileName, new Map<string, [string, string]>()); } - const fileReexports = this.reexportMap.get(fileName) !; + const fileReexports = this.reexportMap.get(fileName)!; for (const reexport of result.reexports) { fileReexports.set(reexport.asAlias, [reexport.fromModule, reexport.symbolName]); } @@ -421,7 +425,7 @@ export class TraitCompiler { typeCheck(ctx: TypeCheckContext): void { for (const clazz of this.classes.keys()) { - const record = this.classes.get(clazz) !; + const record = this.classes.get(clazz)!; for (const trait of record.traits) { if (trait.state !== TraitState.RESOLVED) { continue; @@ -435,7 +439,7 @@ export class TraitCompiler { index(ctx: IndexingContext): void { for (const clazz of this.classes.keys()) { - const record = this.classes.get(clazz) !; + const record = this.classes.get(clazz)!; for (const trait of record.traits) { if (trait.state !== TraitState.RESOLVED) { // Skip traits that haven't been resolved successfully. @@ -457,7 +461,7 @@ export class TraitCompiler { return null; } - const record = this.classes.get(original) !; + const record = this.classes.get(original)!; let res: CompileResult[] = []; @@ -496,7 +500,7 @@ export class TraitCompiler { return []; } - const record = this.classes.get(original) !; + const record = this.classes.get(original)!; const decorators: ts.Decorator[] = []; for (const trait of record.traits) { @@ -515,7 +519,7 @@ export class TraitCompiler { get diagnostics(): ReadonlyArray<ts.Diagnostic> { const diagnostics: ts.Diagnostic[] = []; for (const clazz of this.classes.keys()) { - const record = this.classes.get(clazz) !; + const record = this.classes.get(clazz)!; if (record.metaDiagnostics !== null) { diagnostics.push(...record.metaDiagnostics); } @@ -528,5 +532,7 @@ export class TraitCompiler { return diagnostics; } - get exportStatements(): Map<string, Map<string, [string, string]>> { return this.reexportMap; } + get exportStatements(): Map<string, Map<string, [string, string]>> { + return this.reexportMap; + } } diff --git a/packages/compiler-cli/src/ngtsc/transform/src/declaration.ts b/packages/compiler-cli/src/ngtsc/transform/src/declaration.ts index ee4e47b795829..36cee9b39a46f 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/declaration.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/declaration.ts @@ -28,14 +28,14 @@ export class DtsTransformRegistry { if (!this.ivyDeclarationTransforms.has(sf)) { this.ivyDeclarationTransforms.set(sf, new IvyDeclarationDtsTransform()); } - return this.ivyDeclarationTransforms.get(sf) !; + return this.ivyDeclarationTransforms.get(sf)!; } getReturnTypeTransform(sf: ts.SourceFile): ReturnTypeTransform { if (!this.returnTypeTransforms.has(sf)) { this.returnTypeTransforms.set(sf, new ReturnTypeTransform()); } - return this.returnTypeTransforms.get(sf) !; + return this.returnTypeTransforms.get(sf)!; } /** @@ -55,11 +55,11 @@ export class DtsTransformRegistry { let transforms: DtsTransform[]|null = null; if (this.ivyDeclarationTransforms.has(originalSf)) { transforms = []; - transforms.push(this.ivyDeclarationTransforms.get(originalSf) !); + transforms.push(this.ivyDeclarationTransforms.get(originalSf)!); } if (this.returnTypeTransforms.has(originalSf)) { transforms = transforms || []; - transforms.push(this.returnTypeTransforms.get(originalSf) !); + transforms.push(this.returnTypeTransforms.get(originalSf)!); } return transforms; } @@ -200,12 +200,12 @@ export class IvyDeclarationDtsTransform implements DtsTransform { if (!this.declarationFields.has(original)) { return clazz; } - const fields = this.declarationFields.get(original) !; + const fields = this.declarationFields.get(original)!; const newMembers = fields.map(decl => { const modifiers = [ts.createModifier(ts.SyntaxKind.StaticKeyword)]; const typeRef = translateType(decl.type, imports); - emitAsSingleLine(typeRef); + markForEmitAsSingleLine(typeRef); return ts.createProperty( /* decorators */ undefined, /* modifiers */ modifiers, @@ -226,9 +226,9 @@ export class IvyDeclarationDtsTransform implements DtsTransform { } } -function emitAsSingleLine(node: ts.Node) { +function markForEmitAsSingleLine(node: ts.Node) { ts.setEmitFlags(node, ts.EmitFlags.SingleLine); - ts.forEachChild(node, emitAsSingleLine); + ts.forEachChild(node, markForEmitAsSingleLine); } export class ReturnTypeTransform implements DtsTransform { @@ -247,7 +247,7 @@ export class ReturnTypeTransform implements DtsTransform { if (!this.typeReplacements.has(original)) { return element; } - const returnType = this.typeReplacements.get(original) !; + const returnType = this.typeReplacements.get(original)!; const tsReturnType = translateType(returnType, imports); const methodSignature = ts.updateMethodSignature( @@ -273,7 +273,7 @@ export class ReturnTypeTransform implements DtsTransform { if (!this.typeReplacements.has(original)) { return element; } - const returnType = this.typeReplacements.get(original) !; + const returnType = this.typeReplacements.get(original)!; const tsReturnType = translateType(returnType, imports); return ts.updateFunctionDeclaration( diff --git a/packages/compiler-cli/src/ngtsc/transform/src/trait.ts b/packages/compiler-cli/src/ngtsc/transform/src/trait.ts index 1bdd897354a06..e6b5fb83d3830 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/trait.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/trait.ts @@ -50,8 +50,8 @@ export enum TraitState { * This not only simplifies the implementation, but ensures traits are monomorphic objects as * they're all just "views" in the type system of the same object (which never changes shape). */ -export type Trait<D, A, R> = PendingTrait<D, A, R>| SkippedTrait<D, A, R>| AnalyzedTrait<D, A, R>| - ResolvedTrait<D, A, R>| ErroredTrait<D, A, R>; +export type Trait<D, A, R> = PendingTrait<D, A, R>|SkippedTrait<D, A, R>|AnalyzedTrait<D, A, R>| + ResolvedTrait<D, A, R>|ErroredTrait<D, A, R>; /** * The value side of `Trait` exposes a helper to create a `Trait` in a pending state (by delegating @@ -59,7 +59,7 @@ export type Trait<D, A, R> = PendingTrait<D, A, R>| SkippedTrait<D, A, R>| Analy */ export const Trait = { pending: <D, A, R>(handler: DecoratorHandler<D, A, R>, detected: DetectResult<D>): - PendingTrait<D, A, R> => TraitImpl.pending(handler, detected), + PendingTrait<D, A, R> => TraitImpl.pending(handler, detected), }; /** @@ -137,7 +137,9 @@ export interface ErroredTrait<D, A, R> extends TraitBase<D, A, R> { * * This is a terminal state. */ -export interface SkippedTrait<D, A, R> extends TraitBase<D, A, R> { state: TraitState.SKIPPED; } +export interface SkippedTrait<D, A, R> extends TraitBase<D, A, R> { + state: TraitState.SKIPPED; +} /** * The part of the `Trait` interface for any trait which has been successfully analyzed. @@ -251,8 +253,8 @@ class TraitImpl<D, A, R> { */ private assertTransitionLegal(allowedState: TraitState, transitionTo: TraitState): void { if (!(this.state & allowedState)) { - throw new Error( - `Assertion failure: cannot transition from ${TraitState[this.state]} to ${TraitState[transitionTo]}.`); + throw new Error(`Assertion failure: cannot transition from ${TraitState[this.state]} to ${ + TraitState[transitionTo]}.`); } } diff --git a/packages/compiler-cli/src/ngtsc/transform/src/transform.ts b/packages/compiler-cli/src/ngtsc/transform/src/transform.ts index 23c7198e9749c..58704a13694e7 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/transform.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/transform.ts @@ -12,7 +12,7 @@ import * as ts from 'typescript'; import {DefaultImportRecorder, ImportRewriter} from '../../imports'; import {Decorator, ReflectionHost} from '../../reflection'; import {ImportManager, translateExpression, translateStatement} from '../../translator'; -import {VisitListEntryResult, Visitor, visit} from '../../util/src/visitor'; +import {visit, VisitListEntryResult, Visitor} from '../../util/src/visitor'; import {TraitCompiler} from './compilation'; import {addImports} from './utils'; @@ -47,7 +47,8 @@ class IvyVisitor extends Visitor { constructor( private compilation: TraitCompiler, private reflector: ReflectionHost, private importManager: ImportManager, private defaultImportRecorder: DefaultImportRecorder, - private isCore: boolean, private constantPool: ConstantPool) { + private isClosureCompilerEnabled: boolean, private isCore: boolean, + private constantPool: ConstantPool) { super(); } @@ -73,6 +74,16 @@ class IvyVisitor extends Visitor { undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], field.name, undefined, undefined, exprNode); + if (this.isClosureCompilerEnabled) { + // Closure compiler transforms the form `Service.ɵprov = X` into `Service$ɵprov = X`. To + // prevent this transformation, such assignments need to be annotated with @nocollapse. + // Note that tsickle is typically responsible for adding such annotations, however it + // doesn't yet handle synthetic fields added during other transformations. + ts.addSyntheticLeadingComment( + property, ts.SyntaxKind.MultiLineCommentTrivia, '* @nocollapse ', + /* hasTrailingNewLine */ false); + } + field.statements .map( stmt => translateStatement( @@ -215,7 +226,8 @@ function transformIvySourceFile( // Recursively scan through the AST and perform any updates requested by the IvyCompilation. const visitor = new IvyVisitor( - compilation, reflector, importManager, defaultImportRecorder, isCore, constantPool); + compilation, reflector, importManager, defaultImportRecorder, isClosureCompilerEnabled, + isCore, constantPool); let sf = visit(file, visitor, context); // Generate the constant statements first, as they may involve adding additional imports @@ -273,7 +285,7 @@ function setFileOverviewComment(sf: ts.SourceFile, fileoverview: FileOverviewMet } function maybeFilterDecorator( - decorators: ts.NodeArray<ts.Decorator>| undefined, + decorators: ts.NodeArray<ts.Decorator>|undefined, toRemove: ts.Decorator[]): ts.NodeArray<ts.Decorator>|undefined { if (decorators === undefined) { return undefined; diff --git a/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts index 8608e4364fe32..e90f61659d641 100644 --- a/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts +++ b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts @@ -24,11 +24,15 @@ runInEachFileSystem(() => { name = 'FakeDecoratorHandler'; precedence = HandlerPrecedence.PRIMARY; - detect(): undefined { throw new Error('detect should not have been called'); } + detect(): undefined { + throw new Error('detect should not have been called'); + } analyze(): AnalysisOutput<unknown> { throw new Error('analyze should not have been called'); } - compile(): CompileResult { throw new Error('compile should not have been called'); } + compile(): CompileResult { + throw new Error('compile should not have been called'); + } } const {program} = makeProgram([{ @@ -40,7 +44,7 @@ runInEachFileSystem(() => { const compiler = new TraitCompiler( [new FakeDecoratorHandler()], reflectionHost, NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD, true, new DtsTransformRegistry()); - const sourceFile = program.getSourceFile('lib.d.ts') !; + const sourceFile = program.getSourceFile('lib.d.ts')!; const analysis = compiler.analyzeSync(sourceFile); expect(sourceFile.isDeclarationFile).toBe(true); diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index a41a9fc8a3a73..aed4303eea96b 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeVisitor, TypeofExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; +import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeofExpr, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler'; import {LocalizedString} from '@angular/compiler/src/output/output_ast'; import * as ts from 'typescript'; @@ -15,9 +15,13 @@ import {DefaultImportRecorder, ImportRewriter, NOOP_DEFAULT_IMPORT_RECORDER, Noo export class Context { constructor(readonly isStatement: boolean) {} - get withExpressionMode(): Context { return this.isStatement ? new Context(false) : this; } + get withExpressionMode(): Context { + return this.isStatement ? new Context(false) : this; + } - get withStatementMode(): Context { return !this.isStatement ? new Context(true) : this; } + get withStatementMode(): Context { + return !this.isStatement ? new Context(true) : this; + } } const BINARY_OPERATORS = new Map<BinaryOperator, ts.BinaryOperator>([ @@ -83,7 +87,7 @@ export class ImportManager { if (!this.specifierToIdentifier.has(moduleName)) { this.specifierToIdentifier.set(moduleName, `${this.prefix}${this.nextIndex++}`); } - const moduleImport = this.specifierToIdentifier.get(moduleName) !; + const moduleImport = this.specifierToIdentifier.get(moduleName)!; return {moduleImport, symbol}; } @@ -130,19 +134,21 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor ts.NodeFlags.Const : ts.NodeFlags.None; return ts.createVariableStatement( - undefined, ts.createVariableDeclarationList( - [ts.createVariableDeclaration( - stmt.name, undefined, stmt.value && - stmt.value.visitExpression(this, context.withExpressionMode))], - nodeFlags)); + undefined, + ts.createVariableDeclarationList( + [ts.createVariableDeclaration( + stmt.name, undefined, + stmt.value && stmt.value.visitExpression(this, context.withExpressionMode))], + nodeFlags)); } visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: Context): ts.FunctionDeclaration { return ts.createFunctionDeclaration( undefined, undefined, undefined, stmt.name, undefined, stmt.params.map(param => ts.createParameter(undefined, undefined, undefined, param.name)), - undefined, ts.createBlock(stmt.statements.map( - child => child.visitStatement(this, context.withStatementMode)))); + undefined, + ts.createBlock( + stmt.statements.map(child => child.visitStatement(this, context.withStatementMode)))); } visitExpressionStmt(stmt: ExpressionStatement, context: Context): ts.ExpressionStatement { @@ -194,7 +200,7 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor } visitReadVarExpr(ast: ReadVarExpr, context: Context): ts.Identifier { - const identifier = ts.createIdentifier(ast.name !); + const identifier = ts.createIdentifier(ast.name!); this.setSourceMapRange(identifier, ast); return identifier; } @@ -322,7 +328,7 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor return ts.createConditional( cond, ast.trueCase.visitExpression(this, context), - ast.falseCase !.visitExpression(this, context)); + ast.falseCase!.visitExpression(this, context)); } visitNotExpr(ast: NotExpr, context: Context): ts.PrefixUnaryExpression { @@ -352,7 +358,7 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor throw new Error(`Unknown binary operator: ${BinaryOperator[ast.operator]}`); } return ts.createBinary( - ast.lhs.visitExpression(this, context), BINARY_OPERATORS.get(ast.operator) !, + ast.lhs.visitExpression(this, context), BINARY_OPERATORS.get(ast.operator)!, ast.rhs.visitExpression(this, context)); } @@ -447,12 +453,12 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { `An ExpressionType with type arguments cannot have multiple levels of type arguments`); } - const typeArgs = type.typeParams.map(param => param.visitType(this, context)); + const typeArgs = type.typeParams.map(param => this.translateType(param, context)); return ts.createTypeReferenceNode(typeNode.typeName, typeArgs); } visitArrayType(type: ArrayType, context: Context): ts.ArrayTypeNode { - return ts.createArrayTypeNode(this.translateType(type, context)); + return ts.createArrayTypeNode(this.translateType(type.of, context)); } visitMapType(type: MapType, context: Context): ts.TypeLiteralNode { @@ -497,8 +503,18 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { throw new Error('Method not implemented.'); } - visitLiteralExpr(ast: LiteralExpr, context: Context): ts.LiteralTypeNode { - return ts.createLiteralTypeNode(ts.createLiteral(ast.value as string)); + visitLiteralExpr(ast: LiteralExpr, context: Context): ts.TypeNode { + if (ast.value === null) { + return ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword); + } else if (ast.value === undefined) { + return ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword); + } else if (typeof ast.value === 'boolean') { + return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); + } else if (typeof ast.value === 'number') { + return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); + } else { + return ts.createLiteralTypeNode(ts.createLiteral(ast.value)); + } } visitLocalizedString(ast: LocalizedString, context: Context): never { @@ -527,13 +543,17 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { throw new Error('Method not implemented.'); } - visitNotExpr(ast: NotExpr, context: Context) { throw new Error('Method not implemented.'); } + visitNotExpr(ast: NotExpr, context: Context) { + throw new Error('Method not implemented.'); + } visitAssertNotNullExpr(ast: AssertNotNull, context: Context) { throw new Error('Method not implemented.'); } - visitCastExpr(ast: CastExpr, context: Context) { throw new Error('Method not implemented.'); } + visitCastExpr(ast: CastExpr, context: Context) { + throw new Error('Method not implemented.'); + } visitFunctionExpr(ast: FunctionExpr, context: Context) { throw new Error('Method not implemented.'); @@ -570,7 +590,9 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return ts.createTypeLiteralNode(entries); } - visitCommaExpr(ast: CommaExpr, context: Context) { throw new Error('Method not implemented.'); } + visitCommaExpr(ast: CommaExpr, context: Context) { + throw new Error('Method not implemented.'); + } visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: Context): ts.TypeNode { const node: ts.Node = ast.node; @@ -578,6 +600,8 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return ts.createTypeReferenceNode(node, /* typeArguments */ undefined); } else if (ts.isTypeNode(node)) { return node; + } else if (ts.isLiteralExpression(node)) { + return ts.createLiteralTypeNode(node); } else { throw new Error( `Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts.SyntaxKind[node.kind]}`); @@ -590,8 +614,8 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor { return ts.createTypeQueryNode(expr as ts.Identifier); } - private translateType(expr: Type, context: Context): ts.TypeNode { - const typeNode = expr.visitType(this, context); + private translateType(type: Type, context: Context): ts.TypeNode { + const typeNode = type.visitType(this, context); if (!ts.isTypeNode(typeNode)) { throw new Error( `A Type must translate to a TypeNode, but was ${ts.SyntaxKind[typeNode.kind]}`); diff --git a/packages/compiler-cli/src/ngtsc/tsc_plugin.ts b/packages/compiler-cli/src/ngtsc/tsc_plugin.ts index d533b8a3ee3bf..660d48d132cd9 100644 --- a/packages/compiler-cli/src/ngtsc/tsc_plugin.ts +++ b/packages/compiler-cli/src/ngtsc/tsc_plugin.ts @@ -71,12 +71,14 @@ export class NgTscPlugin implements TscPlugin { return this._compiler; } - constructor(private ngOptions: {}) { setFileSystem(new NodeJSFileSystem()); } + constructor(private ngOptions: {}) { + setFileSystem(new NodeJSFileSystem()); + } wrapHost( host: ts.CompilerHost&UnifiedModulesHost, inputFiles: readonly string[], options: ts.CompilerOptions): PluginCompilerHost { - this.options = {...this.ngOptions, ...options } as NgCompilerOptions; + this.options = {...this.ngOptions, ...options} as NgCompilerOptions; this.host = NgCompilerHost.wrap(host, inputFiles, this.options); return this.host; } @@ -100,9 +102,15 @@ export class NgTscPlugin implements TscPlugin { return this.compiler.getDiagnostics(file); } - getOptionDiagnostics(): ts.Diagnostic[] { return this.compiler.getOptionDiagnostics(); } + getOptionDiagnostics(): ts.Diagnostic[] { + return this.compiler.getOptionDiagnostics(); + } - getNextProgram(): ts.Program { return this.compiler.getNextProgram(); } + getNextProgram(): ts.Program { + return this.compiler.getNextProgram(); + } - createTransformers(): ts.CustomTransformers { return this.compiler.prepareEmit().transformers; } + createTransformers(): ts.CustomTransformers { + return this.compiler.prepareEmit().transformers; + } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/api.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/api.ts index 3134a3db8a189..f36f7983f0ab5 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/api.ts @@ -25,7 +25,7 @@ export interface TypeCheckableDirectiveMeta extends DirectiveMeta { hasNgTemplateContextGuard: boolean; } -export type TemplateId = string & {__brand: 'TemplateId'}; +export type TemplateId = string&{__brand: 'TemplateId'}; /** * Metadata required in addition to a component class in order to generate a type check block (TCB) @@ -233,7 +233,7 @@ export interface TypeCheckingConfig { export type TemplateSourceMapping = - DirectTemplateSourceMapping | IndirectTemplateSourceMapping | ExternalTemplateSourceMapping; + DirectTemplateSourceMapping|IndirectTemplateSourceMapping|ExternalTemplateSourceMapping; /** * A mapping to an inline template in a TS file. diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts index 1538b964a2aaa..58b866f0f9651 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts @@ -14,7 +14,7 @@ import {NoopImportRewriter, Reference, ReferenceEmitter} from '../../imports'; import {ClassDeclaration, ReflectionHost} from '../../reflection'; import {ImportManager} from '../../translator'; -import {TemplateSourceMapping, TypeCheckBlockMetadata, TypeCheckableDirectiveMeta, TypeCheckingConfig, TypeCtorMetadata} from './api'; +import {TemplateSourceMapping, TypeCheckableDirectiveMeta, TypeCheckBlockMetadata, TypeCheckingConfig, TypeCtorMetadata} from './api'; import {shouldReportDiagnostic, translateDiagnostic} from './diagnostics'; import {DomSchemaChecker, RegistryDomSchemaChecker} from './dom'; import {Environment} from './environment'; @@ -125,7 +125,7 @@ export class TypeCheckContext { if (!this.opMap.has(sf)) { this.opMap.set(sf, []); } - const ops = this.opMap.get(sf) !; + const ops = this.opMap.get(sf)!; // Push a `TypeCtorOp` into the operation queue for the source file. ops.push(new TypeCtorOp(ref, ctorMeta)); @@ -152,7 +152,7 @@ export class TypeCheckContext { // Each Op has a splitPoint index into the text where it needs to be inserted. Split the // original source text into chunks at these split points, where code will be inserted between // the chunks. - const ops = this.opMap.get(sf) !.sort(orderOps); + const ops = this.opMap.get(sf)!.sort(orderOps); const textParts = splitStringAtPoints(sf.text, ops.map(op => op.splitPoint)); // Use a `ts.Printer` to generate source code. @@ -238,7 +238,7 @@ export class TypeCheckContext { if (!this.opMap.has(sf)) { this.opMap.set(sf, []); } - const ops = this.opMap.get(sf) !; + const ops = this.opMap.get(sf)!; ops.push(new TcbOp( ref, tcbMeta, this.config, this.reflector, this.domSchemaChecker, this.oobRecorder)); } @@ -278,7 +278,9 @@ class TcbOp implements Op { /** * Type check blocks are inserted immediately after the end of the component class. */ - get splitPoint(): number { return this.ref.node.end + 1; } + get splitPoint(): number { + return this.ref.node.end + 1; + } execute(im: ImportManager, sf: ts.SourceFile, refEmitter: ReferenceEmitter, printer: ts.Printer): string { @@ -301,7 +303,9 @@ class TypeCtorOp implements Op { /** * Type constructor operations are inserted immediately before the end of the directive class. */ - get splitPoint(): number { return this.ref.node.end - 1; } + get splitPoint(): number { + return this.ref.node.end - 1; + } execute(im: ImportManager, sf: ts.SourceFile, refEmitter: ReferenceEmitter, printer: ts.Printer): string { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts index b65fa8d81810d..f5d847938e6d9 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/diagnostics.ts @@ -70,7 +70,7 @@ export function ignoreDiagnostics(node: ts.Node): void { * Adds a synthetic comment to the expression that represents the parse span of the provided node. * This comment can later be retrieved as trivia of a node to recover original source locations. */ -export function addParseSpanInfo(node: ts.Node, span: AbsoluteSourceSpan | ParseSourceSpan): void { +export function addParseSpanInfo(node: ts.Node, span: AbsoluteSourceSpan|ParseSourceSpan): void { let commentText: string; if (span instanceof AbsoluteSourceSpan) { commentText = `${span.start},${span.end}`; @@ -145,7 +145,7 @@ export function translateDiagnostic( */ export function makeTemplateDiagnostic( mapping: TemplateSourceMapping, span: ParseSourceSpan, category: ts.DiagnosticCategory, - code: number, messageText: string | ts.DiagnosticMessageChain, relatedMessage?: { + code: number, messageText: string|ts.DiagnosticMessageChain, relatedMessage?: { text: string, span: ParseSourceSpan, }): TemplateDiagnostic { @@ -172,7 +172,8 @@ export function makeTemplateDiagnostic( file: mapping.node.getSourceFile(), componentFile: mapping.node.getSourceFile(), start: span.start.offset, - length: span.end.offset - span.start.offset, relatedInformation, + length: span.end.offset - span.start.offset, + relatedInformation, }; } else if (mapping.type === 'indirect' || mapping.type === 'external') { // For indirect mappings (template was declared inline, but ngtsc couldn't map it directly diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts index 480577cbdc226..2e2bfe8631fce 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts @@ -12,7 +12,7 @@ import * as ts from 'typescript'; import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {TemplateId} from './api'; -import {TemplateSourceResolver, makeTemplateDiagnostic} from './diagnostics'; +import {makeTemplateDiagnostic, TemplateSourceResolver} from './diagnostics'; const REGISTRY = new DomElementSchemaRegistry(); const REMOVE_XHTML_REGEX = /^:xhtml:/; @@ -66,7 +66,9 @@ export interface DomSchemaChecker { export class RegistryDomSchemaChecker implements DomSchemaChecker { private _diagnostics: ts.Diagnostic[] = []; - get diagnostics(): ReadonlyArray<ts.Diagnostic> { return this._diagnostics; } + get diagnostics(): ReadonlyArray<ts.Diagnostic> { + return this._diagnostics; + } constructor(private resolver: TemplateSourceResolver) {} @@ -83,8 +85,8 @@ export class RegistryDomSchemaChecker implements DomSchemaChecker { errorMsg += `1. If '${name}' is an Angular component, then verify that it is part of this module.\n`; if (name.indexOf('-') > -1) { - errorMsg += - `2. If '${name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`; + errorMsg += `2. If '${ + name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`; } else { errorMsg += `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; @@ -107,12 +109,16 @@ export class RegistryDomSchemaChecker implements DomSchemaChecker { `Can't bind to '${name}' since it isn't a known property of '${element.name}'.`; if (element.name.startsWith('ng-')) { errorMsg += - `\n1. If '${name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` + + `\n1. If '${ + name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` + `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } else if (element.name.indexOf('-') > -1) { errorMsg += - `\n1. If '${element.name}' is an Angular component and it has '${name}' input, then verify that it is part of this module.` + - `\n2. If '${element.name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` + + `\n1. If '${element.name}' is an Angular component and it has '${ + name}' input, then verify that it is part of this module.` + + `\n2. If '${ + element + .name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` + `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts index 5f48a9c04b3c2..300616c47f17b 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts @@ -59,7 +59,7 @@ export class Environment { const dirRef = dir.ref as Reference<ClassDeclaration<ts.ClassDeclaration>>; const node = dirRef.node; if (this.typeCtors.has(node)) { - return this.typeCtors.get(node) !; + return this.typeCtors.get(node)!; } if (requiresInlineTypeCtor(node, this.reflector)) { @@ -101,7 +101,7 @@ export class Environment { */ pipeInst(ref: Reference<ClassDeclaration<ts.ClassDeclaration>>): ts.Expression { if (this.pipeInsts.has(ref.node)) { - return this.pipeInsts.get(ref.node) !; + return this.pipeInsts.get(ref.node)!; } const pipeType = this.referenceType(ref); @@ -142,7 +142,8 @@ export class Environment { /* dotDotDotToken */ undefined, /* name */ 'cb', /* questionToken */ undefined, - /* type */ ts.createFunctionTypeNode( + /* type */ + ts.createFunctionTypeNode( /* typeParameters */ undefined, /* parameters */[ts.createParameter( /* decorators */ undefined, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts index fd1fa179c77b3..0d7901332c090 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler'; +import {AST, AstVisitor, ASTWithSource, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler'; import * as ts from 'typescript'; import {TypeCheckingConfig} from './api'; @@ -103,7 +103,7 @@ class AstTranslator implements AstVisitor { } visitFunctionCall(ast: FunctionCall): ts.Expression { - const receiver = wrapForDiagnostics(this.translate(ast.target !)); + const receiver = wrapForDiagnostics(this.translate(ast.target!)); const args = ast.args.map(expr => this.translate(expr)); const node = ts.createCall(receiver, undefined, args); addParseSpanInfo(node, ast.sourceSpan); @@ -192,7 +192,9 @@ class AstTranslator implements AstVisitor { return node; } - visitPipe(ast: BindingPipe): never { throw new Error('Method not implemented.'); } + visitPipe(ast: BindingPipe): never { + throw new Error('Method not implemented.'); + } visitPrefixNot(ast: PrefixNot): ts.Expression { const expression = wrapForDiagnostics(this.translate(ast.expression)); @@ -221,7 +223,9 @@ class AstTranslator implements AstVisitor { return node; } - visitQuote(ast: Quote): never { throw new Error('Method not implemented.'); } + visitQuote(ast: Quote): never { + throw new Error('Method not implemented.'); + } visitSafeMethodCall(ast: SafeMethodCall): ts.Expression { // See the comments in SafePropertyRead above for an explanation of the cases here. @@ -296,28 +300,64 @@ class VeSafeLhsInferenceBugDetector implements AstVisitor { return ast.receiver.visit(VeSafeLhsInferenceBugDetector.SINGLETON); } - visitBinary(ast: Binary): boolean { return ast.left.visit(this) || ast.right.visit(this); } - visitChain(ast: Chain): boolean { return false; } + visitBinary(ast: Binary): boolean { + return ast.left.visit(this) || ast.right.visit(this); + } + visitChain(ast: Chain): boolean { + return false; + } visitConditional(ast: Conditional): boolean { return ast.condition.visit(this) || ast.trueExp.visit(this) || ast.falseExp.visit(this); } - visitFunctionCall(ast: FunctionCall): boolean { return true; } - visitImplicitReceiver(ast: ImplicitReceiver): boolean { return false; } + visitFunctionCall(ast: FunctionCall): boolean { + return true; + } + visitImplicitReceiver(ast: ImplicitReceiver): boolean { + return false; + } visitInterpolation(ast: Interpolation): boolean { return ast.expressions.some(exp => exp.visit(this)); } - visitKeyedRead(ast: KeyedRead): boolean { return false; } - visitKeyedWrite(ast: KeyedWrite): boolean { return false; } - visitLiteralArray(ast: LiteralArray): boolean { return true; } - visitLiteralMap(ast: LiteralMap): boolean { return true; } - visitLiteralPrimitive(ast: LiteralPrimitive): boolean { return false; } - visitMethodCall(ast: MethodCall): boolean { return true; } - visitPipe(ast: BindingPipe): boolean { return true; } - visitPrefixNot(ast: PrefixNot): boolean { return ast.expression.visit(this); } - visitNonNullAssert(ast: PrefixNot): boolean { return ast.expression.visit(this); } - visitPropertyRead(ast: PropertyRead): boolean { return false; } - visitPropertyWrite(ast: PropertyWrite): boolean { return false; } - visitQuote(ast: Quote): boolean { return false; } - visitSafeMethodCall(ast: SafeMethodCall): boolean { return true; } - visitSafePropertyRead(ast: SafePropertyRead): boolean { return false; } + visitKeyedRead(ast: KeyedRead): boolean { + return false; + } + visitKeyedWrite(ast: KeyedWrite): boolean { + return false; + } + visitLiteralArray(ast: LiteralArray): boolean { + return true; + } + visitLiteralMap(ast: LiteralMap): boolean { + return true; + } + visitLiteralPrimitive(ast: LiteralPrimitive): boolean { + return false; + } + visitMethodCall(ast: MethodCall): boolean { + return true; + } + visitPipe(ast: BindingPipe): boolean { + return true; + } + visitPrefixNot(ast: PrefixNot): boolean { + return ast.expression.visit(this); + } + visitNonNullAssert(ast: PrefixNot): boolean { + return ast.expression.visit(this); + } + visitPropertyRead(ast: PropertyRead): boolean { + return false; + } + visitPropertyWrite(ast: PropertyWrite): boolean { + return false; + } + visitQuote(ast: Quote): boolean { + return false; + } + visitSafeMethodCall(ast: SafeMethodCall): boolean { + return true; + } + visitSafePropertyRead(ast: SafePropertyRead): boolean { + return false; + } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/host.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/host.ts index 8bbc0edd4c409..a1d68b541cc12 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/host.ts @@ -25,7 +25,7 @@ export class TypeCheckProgramHost implements ts.CompilerHost { this.sfMap = sfMap; if (delegate.getDirectories !== undefined) { - this.getDirectories = (path: string) => delegate.getDirectories !(path); + this.getDirectories = (path: string) => delegate.getDirectories!(path); } if (delegate.resolveModuleNames !== undefined) { @@ -69,7 +69,9 @@ export class TypeCheckProgramHost implements ts.CompilerHost { throw new Error(`TypeCheckProgramHost should never write files`); } - getCurrentDirectory(): string { return this.delegate.getCurrentDirectory(); } + getCurrentDirectory(): string { + return this.delegate.getCurrentDirectory(); + } getDirectories?: (path: string) => string[]; @@ -77,13 +79,19 @@ export class TypeCheckProgramHost implements ts.CompilerHost { return this.delegate.getCanonicalFileName(fileName); } - useCaseSensitiveFileNames(): boolean { return this.delegate.useCaseSensitiveFileNames(); } + useCaseSensitiveFileNames(): boolean { + return this.delegate.useCaseSensitiveFileNames(); + } - getNewLine(): string { return this.delegate.getNewLine(); } + getNewLine(): string { + return this.delegate.getNewLine(); + } fileExists(fileName: string): boolean { return this.sfMap.has(fileName) || this.delegate.fileExists(fileName); } - readFile(fileName: string): string|undefined { return this.delegate.readFile(fileName); } + readFile(fileName: string): string|undefined { + return this.delegate.readFile(fileName); + } } \ No newline at end of file diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts index 2f1a8b6e9c834..18ee794e6d96c 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts @@ -12,7 +12,7 @@ import * as ts from 'typescript'; import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {TemplateId} from './api'; -import {TemplateSourceResolver, makeTemplateDiagnostic} from './diagnostics'; +import {makeTemplateDiagnostic, TemplateSourceResolver} from './diagnostics'; @@ -68,7 +68,9 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor constructor(private resolver: TemplateSourceResolver) {} - get diagnostics(): ReadonlyArray<ts.Diagnostic> { return this._diagnostics; } + get diagnostics(): ReadonlyArray<ts.Diagnostic> { + return this._diagnostics; + } missingReferenceTarget(templateId: TemplateId, ref: TmplAstReference): void { const mapping = this.resolver.getSourceMapping(templateId); @@ -97,8 +99,9 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor illegalAssignmentToTemplateVar( templateId: TemplateId, assignment: PropertyWrite, target: TmplAstVariable): void { const mapping = this.resolver.getSourceMapping(templateId); - const errorMsg = - `Cannot use variable '${assignment.name}' as the left-hand side of an assignment expression. Template variables are read-only.`; + const errorMsg = `Cannot use variable '${ + assignment + .name}' as the left-hand side of an assignment expression. Template variables are read-only.`; const sourceSpan = this.resolver.toParseSourceSpan(templateId, assignment.sourceSpan); if (sourceSpan === null) { @@ -115,8 +118,8 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor duplicateTemplateVar( templateId: TemplateId, variable: TmplAstVariable, firstDecl: TmplAstVariable): void { const mapping = this.resolver.getSourceMapping(templateId); - const errorMsg = - `Cannot redeclare variable '${variable.name}' as it was previously declared elsewhere for the same template.`; + const errorMsg = `Cannot redeclare variable '${ + variable.name}' as it was previously declared elsewhere for the same template.`; // The allocation of the error here is pretty useless for variables declared in microsyntax, // since the sourceSpan refers to the entire microsyntax property, not a span for the specific diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/source.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/source.ts index 22ea319fff4cc..9f37c36c19ceb 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/source.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/source.ts @@ -65,14 +65,14 @@ export class TemplateSourceManager implements TemplateSourceResolver { if (!this.templateSources.has(id)) { throw new Error(`Unexpected unknown template ID: ${id}`); } - return this.templateSources.get(id) !.mapping; + return this.templateSources.get(id)!.mapping; } toParseSourceSpan(id: TemplateId, span: AbsoluteSourceSpan): ParseSourceSpan|null { if (!this.templateSources.has(id)) { return null; } - const templateSource = this.templateSources.get(id) !; + const templateSource = this.templateSources.get(id)!; return templateSource.toParseSourceSpan(span.start, span.end); } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index 667957411a4ec..7911baacbdd87 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -6,17 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, BindingPipe, BindingType, BoundTarget, DYNAMIC_TYPE, ImplicitReceiver, MethodCall, ParseSourceSpan, ParsedEventType, PropertyRead, PropertyWrite, SchemaMetadata, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler'; +import {AST, BindingPipe, BindingType, BoundTarget, DYNAMIC_TYPE, ImplicitReceiver, MethodCall, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SchemaMetadata, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler'; import * as ts from 'typescript'; import {Reference} from '../../imports'; import {ClassDeclaration} from '../../reflection'; -import {TemplateId, TypeCheckBlockMetadata, TypeCheckableDirectiveMeta} from './api'; +import {TemplateId, TypeCheckableDirectiveMeta, TypeCheckBlockMetadata} from './api'; import {addParseSpanInfo, addTemplateId, ignoreDiagnostics, wrapForDiagnostics} from './diagnostics'; import {DomSchemaChecker} from './dom'; import {Environment} from './environment'; -import {NULL_AS_ANY, astToTypescript} from './expression'; +import {astToTypescript, NULL_AS_ANY} from './expression'; import {OutOfBandDiagnosticRecorder} from './oob'; import {ExpressionSemanticVisitor} from './template_semantics'; import {checkIfClassIsExported, checkIfGenericTypesAreUnbound, tsCallMethod, tsCastToAny, tsCreateElement, tsCreateVariable, tsDeclareVariable} from './ts_util'; @@ -109,7 +109,9 @@ abstract class TcbOp { * `TcbOp` can be returned in cases where additional code generation is necessary to deal with * circular references. */ - circularFallback(): TcbOp|ts.Expression { return INFER_TYPE_FOR_CIRCULAR_OP_EXPR; } + circularFallback(): TcbOp|ts.Expression { + return INFER_TYPE_FOR_CIRCULAR_OP_EXPR; + } } /** @@ -170,7 +172,9 @@ class TcbVariableOp extends TcbOp { * Executing this operation returns a reference to the template's context variable. */ class TcbTemplateContextOp extends TcbOp { - constructor(private tcb: Context, private scope: Scope) { super(); } + constructor(private tcb: Context, private scope: Scope) { + super(); + } execute(): ts.Identifier { // Allocate a template ctx variable and declare it with an 'any' type. The type of this variable @@ -219,7 +223,7 @@ class TcbTemplateBodyOp extends TcbOp { // For each template guard function on the directive, look for a binding to that input. const boundInput = this.template.inputs.find(i => i.name === guard.inputName) || this.template.templateAttrs.find( - (i: TmplAstTextAttribute | TmplAstBoundAttribute): i is TmplAstBoundAttribute => + (i: TmplAstTextAttribute|TmplAstBoundAttribute): i is TmplAstBoundAttribute => i instanceof TmplAstBoundAttribute && i.name === guard.inputName); if (boundInput !== undefined) { // If there is such a binding, generate an expression for it. @@ -266,7 +270,7 @@ class TcbTemplateBodyOp extends TcbOp { guard = directiveGuards.reduce( (expr, dirGuard) => ts.createBinary(expr, ts.SyntaxKind.AmpersandAmpersandToken, dirGuard), - directiveGuards.pop() !); + directiveGuards.pop()!); } // Create a new Scope for the template. This constructs the list of operations for the template @@ -514,7 +518,7 @@ class TcbDirectiveOutputsOp extends TcbOp { if (output.type !== ParsedEventType.Regular || !fieldByEventName.has(output.name)) { continue; } - const field = fieldByEventName.get(output.name) !; + const field = fieldByEventName.get(output.name)!; if (this.tcb.env.config.checkTypeOfOutputEvents) { // For strict checking of directive events, generate a call to the `subscribe` method @@ -644,13 +648,15 @@ export class Context { * Currently this uses a monotonically increasing counter, but in the future the variable name * might change depending on the type of data being stored. */ - allocateId(): ts.Identifier { return ts.createIdentifier(`_t${this.nextId++}`); } + allocateId(): ts.Identifier { + return ts.createIdentifier(`_t${this.nextId++}`); + } getPipeByName(name: string): ts.Expression|null { if (!this.pipes.has(name)) { return null; } - return this.env.pipeInst(this.pipes.get(name) !); + return this.env.pipeInst(this.pipes.get(name)!); } } @@ -745,7 +751,7 @@ class Scope { if (!varMap.has(v.name)) { varMap.set(v.name, v); } else { - const firstDecl = varMap.get(v.name) !; + const firstDecl = varMap.get(v.name)!; tcb.oobRecorder.duplicateTemplateVar(tcb.id, v, firstDecl); } @@ -796,7 +802,9 @@ class Scope { /** * Add a statement to this scope. */ - addStatement(stmt: ts.Statement): void { this.statements.push(stmt); } + addStatement(stmt: ts.Statement): void { + this.statements.push(stmt); + } /** * Get the statements. @@ -840,26 +848,26 @@ class Scope { if (ref instanceof TmplAstVariable && this.varMap.has(ref)) { // Resolving a context variable for this template. // Execute the `TcbVariableOp` associated with the `TmplAstVariable`. - return this.resolveOp(this.varMap.get(ref) !); + return this.resolveOp(this.varMap.get(ref)!); } else if ( ref instanceof TmplAstTemplate && directive === undefined && this.templateCtxOpMap.has(ref)) { // Resolving the context of the given sub-template. // Execute the `TcbTemplateContextOp` for the template. - return this.resolveOp(this.templateCtxOpMap.get(ref) !); + return this.resolveOp(this.templateCtxOpMap.get(ref)!); } else if ( (ref instanceof TmplAstElement || ref instanceof TmplAstTemplate) && directive !== undefined && this.directiveOpMap.has(ref)) { // Resolving a directive on an element or sub-template. - const dirMap = this.directiveOpMap.get(ref) !; + const dirMap = this.directiveOpMap.get(ref)!; if (dirMap.has(directive)) { - return this.resolveOp(dirMap.get(directive) !); + return this.resolveOp(dirMap.get(directive)!); } else { return null; } } else if (ref instanceof TmplAstElement && this.elementOpMap.has(ref)) { // Resolving the DOM node of an element in this template. - return this.resolveOp(this.elementOpMap.get(ref) !); + return this.resolveOp(this.elementOpMap.get(ref)!); } else { return null; } @@ -1261,14 +1269,13 @@ function tcbCallTypeCtor( type TcbDirectiveInput = { type: 'binding'; field: string; expression: ts.Expression; sourceSpan: ParseSourceSpan; -} | -{ +}|{ type: 'unset'; field: string; }; function tcbGetDirectiveInputs( - el: TmplAstElement | TmplAstTemplate, dir: TypeCheckableDirectiveMeta, tcb: Context, + el: TmplAstElement|TmplAstTemplate, dir: TypeCheckableDirectiveMeta, tcb: Context, scope: Scope): TcbDirectiveInput[] { // Only the first binding to a property is written. // TODO(alxhub): produce an error for duplicate bindings to the same property, independently of @@ -1306,7 +1313,7 @@ function tcbGetDirectiveInputs( * Add a binding expression to the map for each input/template attribute of the directive that has * a matching binding. */ - function processAttribute(attr: TmplAstBoundAttribute | TmplAstTextAttribute): void { + function processAttribute(attr: TmplAstBoundAttribute|TmplAstTextAttribute): void { // Skip non-property bindings. if (attr instanceof TmplAstBoundAttribute && attr.type !== BindingType.Property) { return; @@ -1321,7 +1328,7 @@ function tcbGetDirectiveInputs( if (!propMatch.has(attr.name)) { return; } - const field = propMatch.get(attr.name) !; + const field = propMatch.get(attr.name)!; // Skip the attribute if a previous binding also wrote to it. if (directiveInputs.has(field)) { @@ -1369,7 +1376,7 @@ const enum EventParamType { */ function tcbCreateEventHandler( event: TmplAstBoundEvent, tcb: Context, scope: Scope, - eventType: EventParamType | ts.TypeNode): ts.Expression { + eventType: EventParamType|ts.TypeNode): ts.Expression { const handler = tcbEventHandlerExpression(event.handler, tcb, scope); let eventParamType: ts.TypeNode|undefined; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts index 5df3ab2fb8892..1b5f93f247408 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts @@ -78,7 +78,9 @@ export class TypeCheckFile extends Environment { this.fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); } - getPreludeStatements(): ts.Statement[] { return []; } + getPreludeStatements(): ts.Statement[] { + return []; + } } export function typeCheckFilePath(rootDirs: AbsoluteFsPath[]): AbsoluteFsPath { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_constructor.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_constructor.ts index 55c559f6552ea..de5621bd2bf5f 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_constructor.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_constructor.ts @@ -15,8 +15,7 @@ import {TypeParameterEmitter} from './type_parameter_emitter'; export function generateTypeCtorDeclarationFn( node: ClassDeclaration<ts.ClassDeclaration>, meta: TypeCtorMetadata, nodeTypeRef: ts.EntityName, - typeParams: ts.TypeParameterDeclaration[] | undefined, - reflector: ReflectionHost): ts.Statement { + typeParams: ts.TypeParameterDeclaration[]|undefined, reflector: ReflectionHost): ts.Statement { if (requiresInlineTypeCtor(node, reflector)) { throw new Error(`${node.name.text} requires an inline type constructor`); } @@ -32,7 +31,8 @@ export function generateTypeCtorDeclarationFn( const fnType = ts.createFunctionTypeNode( /* typeParameters */ typeParameters, /* parameters */[initParam], - /* type */ rawType, ); + /* type */ rawType, + ); const decl = ts.createVariableDeclaration( /* name */ meta.fnName, @@ -121,7 +121,8 @@ export function generateInlineTypeCtor( /* typeParameters */ typeParametersWithDefaultTypes(node.typeParameters), /* parameters */[initParam], /* type */ rawType, - /* body */ body, ); + /* body */ body, + ); } function constructTypeCtorParameter( @@ -149,7 +150,8 @@ function constructTypeCtorParameter( /* modifiers */ undefined, /* name */ key, /* questionToken */ undefined, - /* type */ ts.createTypeQueryNode( + /* type */ + ts.createTypeQueryNode( ts.createQualifiedName(rawType.typeName, `ngAcceptInputType_${key}`)), /* initializer */ undefined)); } @@ -243,9 +245,8 @@ function checkIfGenericTypeBoundsAreContextFree( * * This correctly infers `T` as `any`, and therefore `_t3` as `NgFor<any>`. */ -function typeParametersWithDefaultTypes( - params: ReadonlyArray<ts.TypeParameterDeclaration>| undefined): ts.TypeParameterDeclaration[]| - undefined { +function typeParametersWithDefaultTypes(params: ReadonlyArray<ts.TypeParameterDeclaration>| + undefined): ts.TypeParameterDeclaration[]|undefined { if (params === undefined) { return undefined; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_emitter.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_emitter.ts index 877235ef4b160..a1befba5933d6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_emitter.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_emitter.ts @@ -12,7 +12,7 @@ import {Reference} from '../../imports'; * A resolved type reference can either be a `Reference`, the original `ts.TypeReferenceNode` itself * or null to indicate the no reference could be resolved. */ -export type ResolvedTypeReference = Reference | ts.TypeReferenceNode | null; +export type ResolvedTypeReference = Reference|ts.TypeReferenceNode|null; /** * A type reference resolver function is responsible for finding the declaration of the type @@ -111,7 +111,9 @@ export class TypeEmitter { visitTypeReferenceNode: type => this.emitTypeReference(type), visitArrayTypeNode: type => ts.updateArrayTypeNode(type, this.emitType(type.elementType)), visitKeywordType: type => type, - visitOtherType: () => { throw new Error('Unable to emit a complex type'); }, + visitOtherType: () => { + throw new Error('Unable to emit a complex type'); + }, }); } @@ -137,8 +139,8 @@ export class TypeEmitter { const emittedType = this.emitReference(reference); if (!ts.isTypeReferenceNode(emittedType)) { - throw new Error( - `Expected TypeReferenceNode for emitted reference, got ${ts.SyntaxKind[emittedType.kind]}`); + throw new Error(`Expected TypeReferenceNode for emitted reference, got ${ + ts.SyntaxKind[emittedType.kind]}`); } typeName = emittedType.typeName; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_parameter_emitter.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_parameter_emitter.ts index 20d6bf1b18b5d..bf57f7280378f 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_parameter_emitter.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_parameter_emitter.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {OwningModule, Reference} from '../../imports'; import {ReflectionHost} from '../../reflection'; -import {ResolvedTypeReference, TypeEmitter, canEmitType} from './type_emitter'; +import {canEmitType, ResolvedTypeReference, TypeEmitter} from './type_emitter'; /** @@ -92,6 +92,6 @@ export class TypeParameterEmitter { private isLocalTypeParameter(decl: ts.Declaration): boolean { // Checking for local type parameters only occurs during resolution of type parameters, so it is // guaranteed that type parameters are present. - return this.typeParameters !.some(param => param === decl); + return this.typeParameters!.some(param => param === decl); } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts index 1c281971de3d9..c691989bb98c5 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts @@ -8,10 +8,10 @@ import * as ts from 'typescript'; -import {TestFile, runInEachFileSystem} from '../../file_system/testing'; +import {runInEachFileSystem, TestFile} from '../../file_system/testing'; import {TypeCheckingConfig} from '../src/api'; -import {TestDeclaration, ngForDeclaration, ngForDts, typecheck} from './test_utils'; +import {ngForDeclaration, ngForDts, TestDeclaration, typecheck} from './test_utils'; runInEachFileSystem(() => { describe('template diagnostics', () => { @@ -377,8 +377,8 @@ function diagnose( return diagnostics.map(diag => { const text = typeof diag.messageText === 'string' ? diag.messageText : diag.messageText.messageText; - const fileName = diag.file !.fileName; - const {line, character} = ts.getLineAndCharacterOfPosition(diag.file !, diag.start !); + const fileName = diag.file!.fileName; + const {line, character} = ts.getLineAndCharacterOfPosition(diag.file!, diag.start!); return `${fileName}(${line + 1}, ${character + 1}): ${text}`; }); } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts index 1f1a35a0589a6..81279b37bb289 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TestDeclaration, tcb} from './test_utils'; +import {tcb, TestDeclaration} from './test_utils'; describe('type check blocks diagnostics', () => { describe('parse spans', () => { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index da668db467468..89d7d5c0b26a8 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -6,16 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {CssSelector, ParseSourceFile, ParseSourceSpan, R3TargetBinder, SchemaMetadata, SelectorMatcher, TmplAstElement, TmplAstReference, Type, parseTemplate} from '@angular/compiler'; +import {CssSelector, ParseSourceFile, ParseSourceSpan, parseTemplate, R3TargetBinder, SchemaMetadata, SelectorMatcher, TmplAstElement, TmplAstReference, Type} from '@angular/compiler'; import * as ts from 'typescript'; -import {AbsoluteFsPath, LogicalFileSystem, absoluteFrom} from '../../file_system'; +import {absoluteFrom, AbsoluteFsPath, LogicalFileSystem} from '../../file_system'; import {TestFile} from '../../file_system/testing'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; -import {ClassDeclaration, TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {makeProgram} from '../../testing'; import {getRootDirs} from '../../util/src/typescript'; -import {TemplateId, TemplateSourceMapping, TypeCheckBlockMetadata, TypeCheckableDirectiveMeta, TypeCheckingConfig} from '../src/api'; +import {TemplateId, TemplateSourceMapping, TypeCheckableDirectiveMeta, TypeCheckBlockMetadata, TypeCheckingConfig} from '../src/api'; import {TypeCheckContext} from '../src/context'; import {DomSchemaChecker} from '../src/dom'; import {Environment} from '../src/environment'; @@ -168,23 +168,20 @@ export const ALL_ENABLED_CONFIG: TypeCheckingConfig = { }; // Remove 'ref' from TypeCheckableDirectiveMeta and add a 'selector' instead. -export type TestDirective = - Partial<Pick< - TypeCheckableDirectiveMeta, - Exclude<keyof TypeCheckableDirectiveMeta, 'ref'|'coercedInputFields'>>>& - { - selector: string, - name: string, file?: AbsoluteFsPath, - type: 'directive', coercedInputFields?: string[], - }; +export type TestDirective = Partial<Pick< + TypeCheckableDirectiveMeta, + Exclude<keyof TypeCheckableDirectiveMeta, 'ref'|'coercedInputFields'>>>&{ + selector: string, + name: string, + file?: AbsoluteFsPath, type: 'directive', + coercedInputFields?: string[], +}; export type TestPipe = { name: string, - file?: AbsoluteFsPath, - pipeName: string, - type: 'pipe', + file?: AbsoluteFsPath, pipeName: string, type: 'pipe', }; -export type TestDeclaration = TestDirective | TestPipe; +export type TestDeclaration = TestDirective|TestPipe; export function tcb( template: string, declarations: TestDeclaration[] = [], config?: TypeCheckingConfig, @@ -252,7 +249,7 @@ export function typecheck( ]; const {program, host, options} = makeProgram(files, {strictNullChecks: true, noImplicitAny: true, ...opts}, undefined, false); - const sf = program.getSourceFile(absoluteFrom('/main.ts')) !; + const sf = program.getSourceFile(absoluteFrom('/main.ts'))!; const checker = program.getTypeChecker(); const logicalFs = new LogicalFileSystem(getRootDirs(host, options)); const reflectionHost = new TypeScriptReflectionHost(checker); @@ -277,7 +274,7 @@ export function typecheck( const {matcher, pipes} = prepareDeclarations(declarations, decl => { let declFile = sf; if (decl.file !== undefined) { - declFile = program.getSourceFile(decl.file) !; + declFile = program.getSourceFile(decl.file)!; if (declFile === undefined) { throw new Error(`Unable to locate ${decl.file} for ${decl.type} ${decl.name}`); } @@ -356,7 +353,9 @@ class FakeEnvironment /* implements Environment */ { return ts.createParen(ts.createAsExpression(ts.createNull(), this.referenceType(ref))); } - declareOutputHelper(): ts.Expression { return ts.createIdentifier('_outputHelper'); } + declareOutputHelper(): ts.Expression { + return ts.createIdentifier('_outputHelper'); + } reference(ref: Reference<ClassDeclaration<ts.ClassDeclaration>>): ts.Expression { return ref.node.name; @@ -379,7 +378,9 @@ class FakeEnvironment /* implements Environment */ { return ts.createTypeReferenceNode(qName, typeArgs.length > 0 ? typeArgs : undefined); } - getPreludeStatements(): ts.Statement[] { return []; } + getPreludeStatements(): ts.Statement[] { + return []; + } static newFake(config: TypeCheckingConfig): Environment { return new FakeEnvironment(config) as Environment; @@ -387,7 +388,9 @@ class FakeEnvironment /* implements Environment */ { } export class NoopSchemaChecker implements DomSchemaChecker { - get diagnostics(): ReadonlyArray<ts.Diagnostic> { return []; } + get diagnostics(): ReadonlyArray<ts.Diagnostic> { + return []; + } checkElement(id: string, element: TmplAstElement, schemas: SchemaMetadata[]): void {} checkProperty( @@ -396,7 +399,9 @@ export class NoopSchemaChecker implements DomSchemaChecker { } export class NoopOobRecorder implements OutOfBandDiagnosticRecorder { - get diagnostics(): ReadonlyArray<ts.Diagnostic> { return []; } + get diagnostics(): ReadonlyArray<ts.Diagnostic> { + return []; + } missingReferenceTarget(): void {} missingPipe(): void {} illegalAssignmentToTemplateVar(): void {} diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts index 05d9d4cbe4b9d..f69af3763b959 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts @@ -8,12 +8,13 @@ import {TypeCheckingConfig} from '../src/api'; -import {ALL_ENABLED_CONFIG, TestDeclaration, TestDirective, tcb} from './test_utils'; +import {ALL_ENABLED_CONFIG, tcb, TestDeclaration, TestDirective} from './test_utils'; describe('type check blocks', () => { - it('should generate a basic block for a binding', - () => { expect(tcb('{{hello}} {{world}}')).toContain('"" + (ctx).hello + (ctx).world;'); }); + it('should generate a basic block for a binding', () => { + expect(tcb('{{hello}} {{world}}')).toContain('"" + (ctx).hello + (ctx).world;'); + }); it('should generate literal map expressions', () => { const TEMPLATE = '{{ method({foo: a, bar: b}) }}'; @@ -264,7 +265,6 @@ describe('type check blocks', () => { }); describe('outputs', () => { - it('should emit subscribe calls for directive outputs', () => { const DIRECTIVES: TestDeclaration[] = [{ type: 'directive', @@ -305,7 +305,6 @@ describe('type check blocks', () => { expect(block).toContain( '_t3.addEventListener("event", function ($event): any { (_t2 = 3); });'); }); - }); describe('config', () => { @@ -385,7 +384,6 @@ describe('type check blocks', () => { }); describe('config.checkTypeOfBindings', () => { - it('should check types of bindings when enabled', () => { const TEMPLATE = `<div dir [dirInput]="a" [nonDirInput]="b"></div>`; const block = tcb(TEMPLATE, DIRECTIVES); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts index 0dbcd2f9bd238..9f9e1e141c075 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts @@ -6,14 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {LogicalFileSystem, absoluteFrom, getSourceFileOrError} from '../../file_system'; -import {TestFile, runInEachFileSystem} from '../../file_system/testing'; + +import {absoluteFrom, getSourceFileOrError, LogicalFileSystem} from '../../file_system'; +import {runInEachFileSystem, TestFile} from '../../file_system/testing'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; -import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {getRootDirs} from '../../util/src/typescript'; import {TypeCheckContext} from '../src/context'; import {TypeCheckFile} from '../src/type_check_file'; + import {ALL_ENABLED_CONFIG} from './test_utils'; runInEachFileSystem(() => { @@ -42,7 +44,7 @@ runInEachFileSystem(() => { it('should not produce an empty SourceFile when there is nothing to typecheck', () => { const file = new TypeCheckFile( _('/_typecheck_.ts'), ALL_ENABLED_CONFIG, new ReferenceEmitter([]), - /* reflector */ null !); + /* reflector */ null!); const sf = file.render(); expect(sf.statements.length).toBe(1); }); @@ -126,7 +128,7 @@ TestClass.ngTypeCtor({value: 'test'}); const res = ctx.calculateTemplateDiagnostics(program, host, options); const TestClassWithCtor = getDeclaration(res.program, _('/main.ts'), 'TestClass', isNamedClassDeclaration); - const typeCtor = TestClassWithCtor.members.find(isTypeCtor) !; + const typeCtor = TestClassWithCtor.members.find(isTypeCtor)!; expect(typeCtor.getText()).not.toContain('queryField'); }); }); @@ -168,7 +170,7 @@ TestClass.ngTypeCtor({value: 'test'}); const res = ctx.calculateTemplateDiagnostics(program, host, options); const TestClassWithCtor = getDeclaration(res.program, _('/main.ts'), 'TestClass', isNamedClassDeclaration); - const typeCtor = TestClassWithCtor.members.find(isTypeCtor) !; + const typeCtor = TestClassWithCtor.members.find(isTypeCtor)!; const ctorText = typeCtor.getText().replace(/[ \r\n]+/g, ' '); expect(ctorText).toContain( 'init: Pick<TestClass, "foo"> | { bar: typeof TestClass.ngAcceptInputType_bar; }'); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_parameter_emitter_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_parameter_emitter_spec.ts index 099573ebb26ba..5d80d09a01b38 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_parameter_emitter_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_parameter_emitter_spec.ts @@ -8,16 +8,16 @@ import * as ts from 'typescript'; import {absoluteFrom} from '../../file_system'; -import {TestFile, runInEachFileSystem} from '../../file_system/testing'; -import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; +import {runInEachFileSystem, TestFile} from '../../file_system/testing'; +import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {TypeParameterEmitter} from '../src/type_parameter_emitter'; + import {angularCoreDts} from './test_utils'; runInEachFileSystem(() => { describe('type parameter emitter', () => { - function createEmitter(source: string, additionalFiles: TestFile[] = []) { const files: TestFile[] = [ angularCoreDts(), {name: absoluteFrom('/main.ts'), contents: source}, ...additionalFiles @@ -34,7 +34,7 @@ runInEachFileSystem(() => { function emit(emitter: TypeParameterEmitter) { const emitted = emitter.emit(ref => { - const typeName = ts.createQualifiedName(ts.createIdentifier('test'), ref.debugName !); + const typeName = ts.createQualifiedName(ts.createIdentifier('test'), ref.debugName!); return ts.createTypeReferenceNode(typeName, /* typeArguments */ undefined); }); @@ -209,6 +209,5 @@ runInEachFileSystem(() => { expect(emitter.canEmit()).toBe(true); expect(emit(emitter)).toEqual('<T extends test.MyType>'); }); - }); }); diff --git a/packages/compiler-cli/src/ngtsc/util/src/ts_source_map_bug_29300.ts b/packages/compiler-cli/src/ngtsc/util/src/ts_source_map_bug_29300.ts index ede59091e10a2..83148904e72d3 100644 --- a/packages/compiler-cli/src/ngtsc/util/src/ts_source_map_bug_29300.ts +++ b/packages/compiler-cli/src/ngtsc/util/src/ts_source_map_bug_29300.ts @@ -29,16 +29,38 @@ export function tsSourceMapBug29300Fixed() { const sourceFile = ts.createSourceFile('test.ts', 'a;', ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS); const host = { - getSourceFile(): ts.SourceFile | undefined{return sourceFile;}, - fileExists(): boolean{return true;}, - readFile(): string | undefined{return '';}, - writeFile(fileName: string, data: string) { writtenFiles[fileName] = data; }, - getDefaultLibFileName(): string{return '';}, - getCurrentDirectory(): string{return '';}, - getDirectories(): string[]{return [];}, - getCanonicalFileName(): string{return '';}, - useCaseSensitiveFileNames(): boolean{return true;}, - getNewLine(): string{return '\n';}, + getSourceFile(): ts.SourceFile | + undefined { + return sourceFile; + }, + fileExists(): boolean { + return true; + }, + readFile(): string | + undefined { + return ''; + }, + writeFile(fileName: string, data: string) { + writtenFiles[fileName] = data; + }, + getDefaultLibFileName(): string { + return ''; + }, + getCurrentDirectory(): string { + return ''; + }, + getDirectories(): string[] { + return []; + }, + getCanonicalFileName(): string { + return ''; + }, + useCaseSensitiveFileNames(): boolean { + return true; + }, + getNewLine(): string { + return '\n'; + }, }; const transform = (context: ts.TransformationContext) => { diff --git a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts index 68e71b5673748..dc38c2a96c6c2 100644 --- a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts @@ -28,7 +28,7 @@ export function isFromDtsFile(node: ts.Node): boolean { return sf !== undefined && sf.isDeclarationFile; } -export function nodeNameForError(node: ts.Node & {name?: ts.Node}): string { +export function nodeNameForError(node: ts.Node&{name?: ts.Node}): string { if (node.name !== undefined && ts.isIdentifier(node.name)) { return node.name.text; } else { @@ -58,7 +58,7 @@ export function getTokenAtPosition(sf: ts.SourceFile, pos: number): ts.Node { return (ts as any).getTokenAtPosition(sf, pos); } -export function identifierOfNode(decl: ts.Node & {name?: ts.Node}): ts.Identifier|null { +export function identifierOfNode(decl: ts.Node&{name?: ts.Node}): ts.Identifier|null { if (decl.name !== undefined && ts.isIdentifier(decl.name)) { return decl.name; } else { @@ -123,7 +123,7 @@ export function nodeDebugInfo(node: ts.Node): string { export function resolveModuleName( moduleName: string, containingFile: string, compilerOptions: ts.CompilerOptions, compilerHost: ts.CompilerHost, - moduleResolutionCache: ts.ModuleResolutionCache | null): ts.ResolvedModule|undefined { + moduleResolutionCache: ts.ModuleResolutionCache|null): ts.ResolvedModule|undefined { if (compilerHost.resolveModuleNames) { // FIXME: Additional parameters are required in TS3.6, but ignored in 3.5. // Remove the any cast once google3 is fully on TS3.6. diff --git a/packages/compiler-cli/src/ngtsc/util/src/visitor.ts b/packages/compiler-cli/src/ngtsc/util/src/visitor.ts index 2c96f9304c968..fe2ac3377c792 100644 --- a/packages/compiler-cli/src/ngtsc/util/src/visitor.ts +++ b/packages/compiler-cli/src/ngtsc/util/src/visitor.ts @@ -68,7 +68,9 @@ export abstract class Visitor { /** * Visit types of nodes which don't have their own explicit visitor. */ - visitOtherNode<T extends ts.Node>(node: T): T { return node; } + visitOtherNode<T extends ts.Node>(node: T): T { + return node; + } /** * @internal @@ -81,8 +83,9 @@ export abstract class Visitor { node = ts.visitEachChild(node, child => this._visit(child, context), context) as T; if (ts.isClassDeclaration(node)) { - visitedNode = this._visitListEntryNode( - node, (node: ts.ClassDeclaration) => this.visitClassDeclaration(node)) as typeof node; + visitedNode = + this._visitListEntryNode( + node, (node: ts.ClassDeclaration) => this.visitClassDeclaration(node)) as typeof node; } else { visitedNode = this.visitOtherNode(node); } @@ -111,12 +114,12 @@ export abstract class Visitor { const newStatements: ts.Statement[] = []; clone.statements.forEach(stmt => { if (this._before.has(stmt)) { - newStatements.push(...(this._before.get(stmt) !as ts.Statement[])); + newStatements.push(...(this._before.get(stmt)! as ts.Statement[])); this._before.delete(stmt); } newStatements.push(stmt); if (this._after.has(stmt)) { - newStatements.push(...(this._after.get(stmt) !as ts.Statement[])); + newStatements.push(...(this._after.get(stmt)! as ts.Statement[])); this._after.delete(stmt); } }); @@ -126,6 +129,6 @@ export abstract class Visitor { } function hasStatements(node: ts.Node): node is ts.Node&{statements: ts.NodeArray<ts.Statement>} { - const block = node as{statements?: any}; + const block = node as {statements?: any}; return block.statements !== undefined && Array.isArray(block.statements); } diff --git a/packages/compiler-cli/src/ngtsc/util/test/visitor_spec.ts b/packages/compiler-cli/src/ngtsc/util/test/visitor_spec.ts index 7ecbc89aa2f2d..c875cc8ddb200 100644 --- a/packages/compiler-cli/src/ngtsc/util/test/visitor_spec.ts +++ b/packages/compiler-cli/src/ngtsc/util/test/visitor_spec.ts @@ -6,22 +6,22 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; + import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; import {makeProgram} from '../../testing'; -import {VisitListEntryResult, Visitor, visit} from '../src/visitor'; +import {visit, VisitListEntryResult, Visitor} from '../src/visitor'; class TestAstVisitor extends Visitor { visitClassDeclaration(node: ts.ClassDeclaration): VisitListEntryResult<ts.Statement, ts.ClassDeclaration> { - const name = node.name !.text; - const statics = - node.members.filter(member => (member.modifiers as ReadonlyArray<ts.Modifier>|| [ - ]).some(mod => mod.kind === ts.SyntaxKind.StaticKeyword)); - const idStatic = statics - .find( - el => ts.isPropertyDeclaration(el) && ts.isIdentifier(el.name) && - el.name.text === 'id') as ts.PropertyDeclaration | + const name = node.name!.text; + const statics = node.members.filter( + member => (member.modifiers as ReadonlyArray<ts.Modifier>|| + []).some(mod => mod.kind === ts.SyntaxKind.StaticKeyword)); + const idStatic = statics.find( + el => ts.isPropertyDeclaration(el) && ts.isIdentifier(el.name) && + el.name.text === 'id') as ts.PropertyDeclaration | undefined; if (idStatic !== undefined) { return { diff --git a/packages/compiler-cli/src/perform_compile.ts b/packages/compiler-cli/src/perform_compile.ts index b746e6ced5d9d..5456d2f0e4bad 100644 --- a/packages/compiler-cli/src/perform_compile.ts +++ b/packages/compiler-cli/src/perform_compile.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {Position, isSyntaxError} from '@angular/compiler'; +import {isSyntaxError, Position} from '@angular/compiler'; import * as ts from 'typescript'; -import {AbsoluteFsPath, absoluteFrom, getFileSystem, relative, resolve} from '../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, getFileSystem, relative, resolve} from '../src/ngtsc/file_system'; import {replaceTsWithNgInErrors} from './ngtsc/diagnostics'; import * as api from './transformers/api'; @@ -35,7 +35,7 @@ function displayFileName(fileName: string, host: ts.FormatDiagnosticsHost): stri export function formatDiagnosticPosition( position: Position, host: ts.FormatDiagnosticsHost = defaultFormatHost): string { - return `${displayFileName(position.fileName, host)}(${position.line + 1},${position.column+1})`; + return `${displayFileName(position.fileName, host)}(${position.line + 1},${position.column + 1})`; } export function flattenDiagnosticMessageChain( @@ -73,11 +73,10 @@ export function formatDiagnostic( const newLine = host.getNewLine(); const span = diagnostic.span; if (span) { - result += `${formatDiagnosticPosition({ - fileName: span.start.file.url, - line: span.start.line, - column: span.start.col - }, host)}: `; + result += `${ + formatDiagnosticPosition( + {fileName: span.start.file.url, line: span.start.line, column: span.start.col}, + host)}: `; } else if (diagnostic.position) { result += `${formatDiagnosticPosition(diagnostic.position, host)}: `; } @@ -156,8 +155,10 @@ export function readConfiguration( // other options like 'compilerOptions' are merged by TS const baseConfig = existingConfig || config; if (existingConfig) { - baseConfig.angularCompilerOptions = {...config.angularCompilerOptions, - ...baseConfig.angularCompilerOptions}; + baseConfig.angularCompilerOptions = { + ...config.angularCompilerOptions, + ...baseConfig.angularCompilerOptions + }; } if (config.extends) { @@ -223,7 +224,7 @@ export interface PerformCompilationResult { emitResult?: ts.EmitResult; } -export function exitCodeFromResult(diags: Diagnostics | undefined): number { +export function exitCodeFromResult(diags: Diagnostics|undefined): number { if (!diags || filterErrorsAndWarnings(diags).length === 0) { // If we have a result and didn't get any errors, we succeeded. return 0; @@ -233,21 +234,29 @@ export function exitCodeFromResult(diags: Diagnostics | undefined): number { return diags.some(d => d.source === 'angular' && d.code === api.UNKNOWN_ERROR_CODE) ? 2 : 1; } -export function performCompilation( - {rootNames, options, host, oldProgram, emitCallback, mergeEmitResultsCallback, - gatherDiagnostics = defaultGatherDiagnostics, customTransformers, - emitFlags = api.EmitFlags.Default, modifiedResourceFiles = null}: { - rootNames: string[], - options: api.CompilerOptions, - host?: api.CompilerHost, - oldProgram?: api.Program, - emitCallback?: api.TsEmitCallback, - mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback, - gatherDiagnostics?: (program: api.Program) => Diagnostics, - customTransformers?: api.CustomTransformers, - emitFlags?: api.EmitFlags, - modifiedResourceFiles?: Set<string>| null, - }): PerformCompilationResult { +export function performCompilation({ + rootNames, + options, + host, + oldProgram, + emitCallback, + mergeEmitResultsCallback, + gatherDiagnostics = defaultGatherDiagnostics, + customTransformers, + emitFlags = api.EmitFlags.Default, + modifiedResourceFiles = null +}: { + rootNames: string[], + options: api.CompilerOptions, + host?: api.CompilerHost, + oldProgram?: api.Program, + emitCallback?: api.TsEmitCallback, + mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback, + gatherDiagnostics?: (program: api.Program) => Diagnostics, + customTransformers?: api.CustomTransformers, + emitFlags?: api.EmitFlags, + modifiedResourceFiles?: Set<string>| null, +}): PerformCompilationResult { let program: api.Program|undefined; let emitResult: ts.EmitResult|undefined; let allDiagnostics: Array<ts.Diagnostic|api.Diagnostic> = []; @@ -262,7 +271,7 @@ export function performCompilation( program = ng.createProgram({rootNames, host, options, oldProgram}); const beforeDiags = Date.now(); - allDiagnostics.push(...gatherDiagnostics(program !)); + allDiagnostics.push(...gatherDiagnostics(program!)); if (options.diagnostics) { const afterDiags = Date.now(); allDiagnostics.push( @@ -271,7 +280,7 @@ export function performCompilation( if (!hasErrors(allDiagnostics)) { emitResult = - program !.emit({emitCallback, mergeEmitResultsCallback, customTransformers, emitFlags}); + program!.emit({emitCallback, mergeEmitResultsCallback, customTransformers, emitFlags}); allDiagnostics.push(...emitResult.diagnostics); return {diagnostics: allDiagnostics, program, emitResult}; } @@ -297,7 +306,7 @@ export function performCompilation( export function defaultGatherDiagnostics(program: api.Program): Diagnostics { const allDiagnostics: Array<ts.Diagnostic|api.Diagnostic> = []; - function checkDiagnostics(diags: Diagnostics | undefined) { + function checkDiagnostics(diags: Diagnostics|undefined) { if (diags) { allDiagnostics.push(...diags); return !hasErrors(diags); diff --git a/packages/compiler-cli/src/perform_watch.ts b/packages/compiler-cli/src/perform_watch.ts index 2d406686ace5d..36aa3e6a664f8 100644 --- a/packages/compiler-cli/src/perform_watch.ts +++ b/packages/compiler-cli/src/perform_watch.ts @@ -10,7 +10,7 @@ import * as chokidar from 'chokidar'; import * as path from 'path'; import * as ts from 'typescript'; -import {Diagnostics, ParsedConfiguration, PerformCompilationResult, exitCodeFromResult, performCompilation, readConfiguration} from './perform_compile'; +import {Diagnostics, exitCodeFromResult, ParsedConfiguration, performCompilation, PerformCompilationResult, readConfiguration} from './perform_compile'; import * as api from './transformers/api'; import {createCompilerHost} from './transformers/entry_points'; import {createMessageDiagnostic} from './transformers/util'; @@ -50,8 +50,9 @@ export interface PerformWatchHost { export function createPerformWatchHost( configFileName: string, reportDiagnostics: (diagnostics: Diagnostics) => void, - existingOptions?: ts.CompilerOptions, createEmitCallback?: (options: api.CompilerOptions) => - api.TsEmitCallback | undefined): PerformWatchHost { + existingOptions?: ts.CompilerOptions, + createEmitCallback?: (options: api.CompilerOptions) => + api.TsEmitCallback | undefined): PerformWatchHost { return { reportDiagnostics: reportDiagnostics, createCompilerHost: options => createCompilerHost({options}), @@ -130,7 +131,7 @@ export function performWatchCompilation(host: PerformWatchHost): // Note: ! is ok as options are filled after the first compilation // Note: ! is ok as resolvedReadyPromise is filled by the previous call const fileWatcher = - host.onFileChange(cachedOptions !.options, watchedFileChanged, resolveReadyPromise !); + host.onFileChange(cachedOptions!.options, watchedFileChanged, resolveReadyPromise!); return {close, ready: cb => readyPromise.then(cb), firstCompileResult}; @@ -177,7 +178,7 @@ export function performWatchCompilation(host: PerformWatchHost): if (ce.exists == null) { ce.exists = originalFileExists.call(this, fileName); } - return ce.exists !; + return ce.exists!; }; const originalGetSourceFile = cachedCompilerHost.getSourceFile; cachedCompilerHost.getSourceFile = function( @@ -186,7 +187,7 @@ export function performWatchCompilation(host: PerformWatchHost): if (!ce.sf) { ce.sf = originalGetSourceFile.call(this, fileName, languageVersion); } - return ce.sf !; + return ce.sf!; }; const originalReadFile = cachedCompilerHost.readFile; cachedCompilerHost.readFile = function(fileName: string) { @@ -194,7 +195,7 @@ export function performWatchCompilation(host: PerformWatchHost): if (ce.content == null) { ce.content = originalReadFile.call(this, fileName); } - return ce.content !; + return ce.content!; }; // Provide access to the file paths that triggered this rebuild cachedCompilerHost.getModifiedResourceFiles = function() { diff --git a/packages/compiler-cli/src/transformers/api.ts b/packages/compiler-cli/src/transformers/api.ts index 17da6f47d3739..bde02a1a289f4 100644 --- a/packages/compiler-cli/src/transformers/api.ts +++ b/packages/compiler-cli/src/transformers/api.ts @@ -190,8 +190,12 @@ export interface TsEmitArguments { customTransformers?: ts.CustomTransformers; } -export interface TsEmitCallback { (args: TsEmitArguments): ts.EmitResult; } -export interface TsMergeEmitResultsCallback { (results: ts.EmitResult[]): ts.EmitResult; } +export interface TsEmitCallback { + (args: TsEmitArguments): ts.EmitResult; +} +export interface TsMergeEmitResultsCallback { + (results: ts.EmitResult[]): ts.EmitResult; +} export interface LibrarySummary { fileName: string; @@ -283,14 +287,14 @@ export interface Program { * * Angular structural information is required to emit files. */ - emit({emitFlags, cancellationToken, customTransformers, emitCallback, - mergeEmitResultsCallback}?: { - emitFlags?: EmitFlags, - cancellationToken?: ts.CancellationToken, - customTransformers?: CustomTransformers, - emitCallback?: TsEmitCallback, - mergeEmitResultsCallback?: TsMergeEmitResultsCallback - }): ts.EmitResult; + emit({emitFlags, cancellationToken, customTransformers, emitCallback, mergeEmitResultsCallback}?: + { + emitFlags?: EmitFlags, + cancellationToken?: ts.CancellationToken, + customTransformers?: CustomTransformers, + emitCallback?: TsEmitCallback, + mergeEmitResultsCallback?: TsMergeEmitResultsCallback + }): ts.EmitResult; /** * Returns the .d.ts / .ngsummary.json / .ngfactory.d.ts files of libraries that have been emitted diff --git a/packages/compiler-cli/src/transformers/compiler_host.ts b/packages/compiler-cli/src/transformers/compiler_host.ts index 353b1884d8d59..5efb487d2d258 100644 --- a/packages/compiler-cli/src/transformers/compiler_host.ts +++ b/packages/compiler-cli/src/transformers/compiler_host.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotCompilerHost, EmitterVisitorContext, GeneratedFile, ParseSourceSpan, TypeScriptEmitter, collectExternalReferences, syntaxError} from '@angular/compiler'; +import {AotCompilerHost, collectExternalReferences, EmitterVisitorContext, GeneratedFile, ParseSourceSpan, syntaxError, TypeScriptEmitter} from '@angular/compiler'; import * as path from 'path'; import * as ts from 'typescript'; @@ -15,7 +15,7 @@ import {ModuleMetadata} from '../metadata/index'; import {join} from '../ngtsc/file_system'; import {CompilerHost, CompilerOptions, LibrarySummary} from './api'; -import {MetadataReaderHost, createMetadataReaderCache, readMetadata} from './metadata_reader'; +import {createMetadataReaderCache, MetadataReaderHost, readMetadata} from './metadata_reader'; import {DTS, GENERATED_FILES, isInRootDir, relativeToRootDirs} from './util'; const NODE_MODULES_PACKAGE_NAME = /node_modules\/((\w|-|\.)+|(@(\w|-|\.)+\/(\w|-|\.)+))/; @@ -24,8 +24,8 @@ const CSS_PREPROCESSOR_EXT = /(\.scss|\.sass|\.less|\.styl)$/; let wrapHostForTest: ((host: ts.CompilerHost) => ts.CompilerHost)|null = null; -export function setWrapHostForTest(wrapFn: ((host: ts.CompilerHost) => ts.CompilerHost) | null): - void { +export function setWrapHostForTest(wrapFn: ((host: ts.CompilerHost) => ts.CompilerHost)| + null): void { wrapHostForTest = wrapFn; } @@ -53,11 +53,11 @@ export interface CodeGenerator { findGeneratedFileNames(fileName: string): string[]; } -function assert<T>(condition: T | null | undefined) { +function assert<T>(condition: T|null|undefined) { if (!condition) { // TODO(chuckjaz): do the right thing } - return condition !; + return condition!; } /** @@ -67,7 +67,7 @@ function assert<T>(condition: T | null | undefined) { * - TypeCheckHost for mapping ts errors to ng errors (via translateDiagnostics) */ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHost, AotCompilerHost, - TypeCheckHost { + TypeCheckHost { private metadataReaderCache = createMetadataReaderCache(); private fileNameToModuleNameCache = new Map<string, string>(); private flatModuleIndexCache = new Map<string, boolean>(); @@ -83,13 +83,13 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos private metadataReaderHost: MetadataReaderHost; // TODO(issue/24571): remove '!'. - getCancellationToken !: () => ts.CancellationToken; + getCancellationToken!: () => ts.CancellationToken; // TODO(issue/24571): remove '!'. - getDefaultLibLocation !: () => string; + getDefaultLibLocation!: () => string; // TODO(issue/24571): remove '!'. - trace !: (s: string) => void; + trace!: (s: string) => void; // TODO(issue/24571): remove '!'. - getDirectories !: (path: string) => string[]; + getDirectories!: (path: string) => string[]; resolveTypeReferenceDirectives?: (names: string[], containingFile: string) => ts.ResolvedTypeReferenceDirective[]; directoryExists?: (directoryName: string) => boolean; @@ -100,21 +100,21 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos private codeGenerator: CodeGenerator, private librarySummaries = new Map<string, LibrarySummary>()) { this.moduleResolutionCache = ts.createModuleResolutionCache( - this.context.getCurrentDirectory !(), this.context.getCanonicalFileName.bind(this.context)); - const basePath = this.options.basePath !; + this.context.getCurrentDirectory!(), this.context.getCanonicalFileName.bind(this.context)); + const basePath = this.options.basePath!; this.rootDirs = - (this.options.rootDirs || [this.options.basePath !]).map(p => path.resolve(basePath, p)); + (this.options.rootDirs || [this.options.basePath!]).map(p => path.resolve(basePath, p)); if (context.getDirectories) { - this.getDirectories = path => context.getDirectories !(path); + this.getDirectories = path => context.getDirectories!(path); } if (context.directoryExists) { - this.directoryExists = directoryName => context.directoryExists !(directoryName); + this.directoryExists = directoryName => context.directoryExists!(directoryName); } if (context.getCancellationToken) { - this.getCancellationToken = () => context.getCancellationToken !(); + this.getCancellationToken = () => context.getCancellationToken!(); } if (context.getDefaultLibLocation) { - this.getDefaultLibLocation = () => context.getDefaultLibLocation !(); + this.getDefaultLibLocation = () => context.getDefaultLibLocation!(); } if (context.resolveTypeReferenceDirectives) { // Backward compatibility with TypeScript 2.9 and older since return @@ -123,11 +123,11 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos type ts3ResolveTypeReferenceDirectives = (names: string[], containingFile: string) => ts.ResolvedTypeReferenceDirective[]; this.resolveTypeReferenceDirectives = (names: string[], containingFile: string) => - (context.resolveTypeReferenceDirectives as ts3ResolveTypeReferenceDirectives) !( - names, containingFile); + (context.resolveTypeReferenceDirectives as ts3ResolveTypeReferenceDirectives)! + (names, containingFile); } if (context.trace) { - this.trace = s => context.trace !(s); + this.trace = s => context.trace!(s); } if (context.fileNameToModuleName) { this.fileNameToModuleName = context.fileNameToModuleName.bind(context); @@ -265,8 +265,8 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos } } } else { - throw new Error( - `Trying to import a source file from a node_modules package: import ${originalImportedFile} from ${containingFile}`); + throw new Error(`Trying to import a source file from a node_modules package: import ${ + originalImportedFile} from ${containingFile}`); } this.fileNameToModuleNameCache.set(cacheKey, moduleName); @@ -325,7 +325,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos // Note: we need the explicit check via `has` as we also cache results // that were null / undefined. if (this.originalSourceFiles.has(filePath)) { - return this.originalSourceFiles.get(filePath) !; + return this.originalSourceFiles.get(filePath)!; } if (!languageVersion) { languageVersion = this.options.target || ts.ScriptTarget.Latest; @@ -353,8 +353,8 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos newRefs.forEach(r => refsAreEqual = refsAreEqual && oldRefs.has(r)); } if (!refsAreEqual) { - throw new Error( - `Illegal State: external references changed in ${genFile.genFileUrl}.\nOld: ${Array.from(oldRefs)}.\nNew: ${Array.from(newRefs)}`); + throw new Error(`Illegal State: external references changed in ${genFile.genFileUrl}.\nOld: ${ + Array.from(oldRefs)}.\nNew: ${Array.from(newRefs)}`); } return this.addGeneratedFile(genFile, newRefs); } @@ -381,7 +381,8 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos } this.generatedSourceFiles.set(genFile.genFileUrl, { sourceFile: sf, - emitCtx: context, externalReferences, + emitCtx: context, + externalReferences, }); return sf; } @@ -468,7 +469,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos } // TODO(tbosch): TypeScript's typings for getSourceFile are incorrect, // as it can very well return undefined. - return sf !; + return sf!; } private getGeneratedFile(fileName: string): ts.SourceFile|null { @@ -638,7 +639,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos } function genFileExternalReferences(genFile: GeneratedFile): Set<string> { - return new Set(collectExternalReferences(genFile.stmts !).map(er => er.moduleName !)); + return new Set(collectExternalReferences(genFile.stmts!).map(er => er.moduleName!)); } function addReferencesToSourceFile(sf: ts.SourceFile, genFileNames: string[]) { diff --git a/packages/compiler-cli/src/transformers/inline_resources.ts b/packages/compiler-cli/src/transformers/inline_resources.ts index 25e4d6108f1d2..015fad0353800 100644 --- a/packages/compiler-cli/src/transformers/inline_resources.ts +++ b/packages/compiler-cli/src/transformers/inline_resources.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {MetadataObject, MetadataValue, isClassMetadata, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicCallExpression} from '../metadata/index'; +import {isClassMetadata, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicCallExpression, MetadataObject, MetadataValue} from '../metadata/index'; import {MetadataTransformer, ValueTransform} from './metadata_cache'; @@ -17,27 +17,29 @@ const PRECONDITIONS_TEXT = /** A subset of members from AotCompilerHost */ export type ResourcesHost = { - resourceNameToFileName(resourceName: string, containingFileName: string): string | null; + resourceNameToFileName(resourceName: string, containingFileName: string): string|null; loadResource(path: string): Promise<string>| string; }; export type StaticResourceLoader = { - get(url: string | MetadataValue): string; + get(url: string|MetadataValue): string; }; function getResourceLoader(host: ResourcesHost, containingFileName: string): StaticResourceLoader { return { - get(url: string | MetadataValue): string{ + get(url: string|MetadataValue): string { if (typeof url !== 'string') { throw new Error('templateUrl and stylesUrl must be string literals. ' + PRECONDITIONS_TEXT); - } const fileName = host.resourceNameToFileName(url, containingFileName); + } + const fileName = host.resourceNameToFileName(url, containingFileName); if (fileName) { const content = host.loadResource(fileName); if (typeof content !== 'string') { throw new Error('Cannot handle async resource. ' + PRECONDITIONS_TEXT); } return content; - } throw new Error(`Failed to resolve ${url} from ${containingFileName}. ${PRECONDITIONS_TEXT}`); + } + throw new Error(`Failed to resolve ${url} from ${containingFileName}. ${PRECONDITIONS_TEXT}`); } }; } @@ -249,8 +251,7 @@ function isComponentSymbol(identifier: ts.Node, typeChecker: ts.TypeChecker) { const name = (declaration.propertyName || declaration.name).text; // We know that parent pointers are set because we created the SourceFile ourselves. // The number of parent references here match the recursion depth at this point. - const moduleId = - (declaration.parent !.parent !.parent !.moduleSpecifier as ts.StringLiteral).text; + const moduleId = (declaration.parent!.parent!.parent!.moduleSpecifier as ts.StringLiteral).text; return moduleId === '@angular/core' && name === 'Component'; } diff --git a/packages/compiler-cli/src/transformers/lower_expressions.ts b/packages/compiler-cli/src/transformers/lower_expressions.ts index 3f2bc92c7830b..7ded3d5198ad2 100644 --- a/packages/compiler-cli/src/transformers/lower_expressions.ts +++ b/packages/compiler-cli/src/transformers/lower_expressions.ts @@ -9,7 +9,8 @@ import {createLoweredSymbol, isLoweredSymbol} from '@angular/compiler'; import * as ts from 'typescript'; -import {CollectorOptions, MetadataCollector, MetadataValue, ModuleMetadata, isMetadataGlobalReferenceExpression} from '../metadata/index'; +import {CollectorOptions, isMetadataGlobalReferenceExpression, MetadataCollector, MetadataValue, ModuleMetadata} from '../metadata/index'; + import {MetadataCache, MetadataTransformer, ValueTransform} from './metadata_cache'; export interface LoweringRequest { @@ -21,7 +22,10 @@ export interface LoweringRequest { export type RequestLocationMap = Map<number, LoweringRequest>; -const enum DeclarationOrder { BeforeStmt, AfterStmt } +const enum DeclarationOrder { + BeforeStmt, + AfterStmt +} interface Declaration { name: string; @@ -201,14 +205,16 @@ export function getExpressionLoweringTransformFactory( }; } -export interface RequestsMap { getRequests(sourceFile: ts.SourceFile): RequestLocationMap; } +export interface RequestsMap { + getRequests(sourceFile: ts.SourceFile): RequestLocationMap; +} interface MetadataAndLoweringRequests { metadata: ModuleMetadata|undefined; requests: RequestLocationMap; } -function isEligibleForLowering(node: ts.Node | undefined): boolean { +function isEligibleForLowering(node: ts.Node|undefined): boolean { if (node) { switch (node.kind) { case ts.SyntaxKind.SourceFile: @@ -232,10 +238,11 @@ function isEligibleForLowering(node: ts.Node | undefined): boolean { // example) might also require lowering even if the top-level declaration is already // properly exported. const varNode = node as ts.VariableDeclaration; - return isExported || (varNode.initializer !== undefined && - (ts.isObjectLiteralExpression(varNode.initializer) || - ts.isArrayLiteralExpression(varNode.initializer) || - ts.isCallExpression(varNode.initializer))); + return isExported || + (varNode.initializer !== undefined && + (ts.isObjectLiteralExpression(varNode.initializer) || + ts.isArrayLiteralExpression(varNode.initializer) || + ts.isCallExpression(varNode.initializer))); } return isEligibleForLowering(node.parent); } @@ -264,7 +271,7 @@ function isLiteralFieldNamed(node: ts.Node, names: Set<string>): boolean { export class LowerMetadataTransform implements RequestsMap, MetadataTransformer { // TODO(issue/24571): remove '!'. - private cache !: MetadataCache; + private cache!: MetadataCache; private requests = new Map<string, RequestLocationMap>(); private lowerableFieldNames: Set<string>; @@ -288,7 +295,9 @@ export class LowerMetadataTransform implements RequestsMap, MetadataTransformer } // MetadataTransformer - connect(cache: MetadataCache): void { this.cache = cache; } + connect(cache: MetadataCache): void { + this.cache = cache; + } start(sourceFile: ts.SourceFile): ValueTransform|undefined { let identNumber = 0; @@ -329,7 +338,7 @@ export class LowerMetadataTransform implements RequestsMap, MetadataTransformer const hasLowerableParentCache = new Map<ts.Node, boolean>(); - const shouldBeLowered = (node: ts.Node | undefined): boolean => { + const shouldBeLowered = (node: ts.Node|undefined): boolean => { if (node === undefined) { return false; } @@ -346,7 +355,7 @@ export class LowerMetadataTransform implements RequestsMap, MetadataTransformer return lowerable; }; - const hasLowerableParent = (node: ts.Node | undefined): boolean => { + const hasLowerableParent = (node: ts.Node|undefined): boolean => { if (node === undefined) { return false; } @@ -354,10 +363,10 @@ export class LowerMetadataTransform implements RequestsMap, MetadataTransformer hasLowerableParentCache.set( node, shouldBeLowered(node.parent) || hasLowerableParent(node.parent)); } - return hasLowerableParentCache.get(node) !; + return hasLowerableParentCache.get(node)!; }; - const isLowerable = (node: ts.Node | undefined): boolean => { + const isLowerable = (node: ts.Node|undefined): boolean => { if (node === undefined) { return false; } @@ -383,7 +392,7 @@ function createExportTableFor(sourceFile: ts.SourceFile): Set<string> { case ts.SyntaxKind.InterfaceDeclaration: if ((ts.getCombinedModifierFlags(node as ts.Declaration) & ts.ModifierFlags.Export) != 0) { const classDeclaration = - node as(ts.ClassDeclaration | ts.FunctionDeclaration | ts.InterfaceDeclaration); + node as (ts.ClassDeclaration | ts.FunctionDeclaration | ts.InterfaceDeclaration); const name = classDeclaration.name; if (name) exportTable.add(name.text); } @@ -406,7 +415,9 @@ function createExportTableFor(sourceFile: ts.SourceFile): Set<string> { const exportDeclaration = node as ts.ExportDeclaration; const {moduleSpecifier, exportClause} = exportDeclaration; if (!moduleSpecifier && exportClause && ts.isNamedExports(exportClause)) { - exportClause.elements.forEach(spec => { exportTable.add(spec.name.text); }); + exportClause.elements.forEach(spec => { + exportTable.add(spec.name.text); + }); } } }); diff --git a/packages/compiler-cli/src/transformers/nocollapse_hack.ts b/packages/compiler-cli/src/transformers/nocollapse_hack.ts deleted file mode 100644 index 59cd5de80efe0..0000000000000 --- a/packages/compiler-cli/src/transformers/nocollapse_hack.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @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 - */ - -// Closure compiler transforms the form `Service.ɵprov = X` into -// `Service$ɵprov = X`. To prevent this transformation, such assignments need to be -// annotated with @nocollapse. Unfortunately, a bug in Typescript where comments aren't propagated -// through the TS transformations precludes adding the comment via the AST. This workaround detects -// the static assignments to R3 properties such as ɵprov using a regex, as output files -// are written, and applies the annotation through regex replacement. -// -// TODO(alxhub): clean up once fix for TS transformers lands in upstream -// -// Typescript reference issue: https://github.com/Microsoft/TypeScript/issues/22497 - -// Pattern matching all Render3 property names. -const R3_DEF_NAME_PATTERN = [ - 'ɵcmp', - 'ɵdir', - 'ɵprov', - 'ɵinj', - 'ɵmod', - 'ɵpipe', - 'ɵfac', -].join('|'); - -// Pattern matching `Identifier.property` where property is a Render3 property. -const R3_DEF_ACCESS_PATTERN = `[^\\s\\.()[\\]]+\.(${R3_DEF_NAME_PATTERN})`; - -// Pattern matching a source line that contains a Render3 static property assignment. -// It declares two matching groups - one for the preceding whitespace, the second for the rest -// of the assignment expression. -const R3_DEF_LINE_PATTERN = `^(\\s*)(${R3_DEF_ACCESS_PATTERN} = .*)$`; - -// Regex compilation of R3_DEF_LINE_PATTERN. Matching group 1 yields the whitespace preceding the -// assignment, matching group 2 gives the rest of the assignment expressions. -const R3_MATCH_DEFS = new RegExp(R3_DEF_LINE_PATTERN, 'gmu'); - -const R3_TSICKLE_DECL_PATTERN = - `(\\/\\*\\*[*\\s]*)(@[^*]+\\*\\/\\s+[^.]+\\.(?:${R3_DEF_NAME_PATTERN});)`; - -const R3_MATCH_TSICKLE_DECL = new RegExp(R3_TSICKLE_DECL_PATTERN, 'gmu'); - -// Replacement string that complements R3_MATCH_DEFS. It inserts `/** @nocollapse */` before the -// assignment but after any indentation. Note that this will mess up any sourcemaps on this line -// (though there shouldn't be any, since Render3 properties are synthetic). -const R3_NOCOLLAPSE_DEFS = '$1\/** @nocollapse *\/ $2'; - -const R3_NOCOLLAPSE_TSICKLE_DECL = '$1@nocollapse $2'; - -export function nocollapseHack(contents: string): string { - return contents.replace(R3_MATCH_DEFS, R3_NOCOLLAPSE_DEFS) - .replace(R3_MATCH_TSICKLE_DECL, R3_NOCOLLAPSE_TSICKLE_DECL); -} diff --git a/packages/compiler-cli/src/transformers/node_emitter.ts b/packages/compiler-cli/src/transformers/node_emitter.ts index 1cd37b45e8e9d..a557669d453ea 100644 --- a/packages/compiler-cli/src/transformers/node_emitter.ts +++ b/packages/compiler-cli/src/transformers/node_emitter.ts @@ -12,7 +12,9 @@ import * as ts from 'typescript'; import {error} from './util'; -export interface Node { sourceSpan: ParseSourceSpan|null; } +export interface Node { + sourceSpan: ParseSourceSpan|null; +} const METHOD_THIS_NAME = 'this'; const CATCH_ERROR_NAME = 'error'; @@ -20,9 +22,11 @@ const CATCH_STACK_NAME = 'stack'; const _VALID_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i; export class TypeScriptNodeEmitter { + constructor(private annotateForClosureCompiler: boolean) {} + updateSourceFile(sourceFile: ts.SourceFile, stmts: Statement[], preamble?: string): [ts.SourceFile, Map<ts.Node, Node>] { - const converter = new NodeEmitterVisitor(); + const converter = new NodeEmitterVisitor(this.annotateForClosureCompiler); // [].concat flattens the result so that each `visit...` method can also return an array of // stmts. const statements: any[] = [].concat( @@ -63,8 +67,8 @@ export class TypeScriptNodeEmitter { */ export function updateSourceFile( sourceFile: ts.SourceFile, module: PartialModule, - context: ts.TransformationContext): [ts.SourceFile, Map<ts.Node, Node>] { - const converter = new NodeEmitterVisitor(); + annotateForClosureCompiler: boolean): [ts.SourceFile, Map<ts.Node, Node>] { + const converter = new NodeEmitterVisitor(annotateForClosureCompiler); converter.loadExportedVariableIdentifiers(sourceFile); const prefixStatements = module.statements.filter(statement => !(statement instanceof ClassStmt)); @@ -108,15 +112,16 @@ export function updateSourceFile( // Validate that all the classes have been generated classNames.size == 0 || - error( - `${classNames.size == 1 ? 'Class' : 'Classes'} "${Array.from(classNames.keys()).join(', ')}" not generated`); + error(`${classNames.size == 1 ? 'Class' : 'Classes'} "${ + Array.from(classNames.keys()).join(', ')}" not generated`); // Add imports to the module required by the new methods const imports = converter.getImports(); if (imports && imports.length) { // Find where the new imports should go const index = firstAfter( - newStatements, statement => statement.kind === ts.SyntaxKind.ImportDeclaration || + newStatements, + statement => statement.kind === ts.SyntaxKind.ImportDeclaration || statement.kind === ts.SyntaxKind.ImportEqualsDeclaration); newStatements = [...newStatements.slice(0, index), ...imports, ...prefix, ...newStatements.slice(index)]; @@ -150,7 +155,9 @@ function firstAfter<T>(a: T[], predicate: (value: T) => boolean) { // A recorded node is a subtype of the node that is marked as being recorded. This is used // to ensure that NodeEmitterVisitor.record has been called on all nodes returned by the // NodeEmitterVisitor -export type RecordedNode<T extends ts.Node = ts.Node> = (T & { __recorded: any;}) | null; +export type RecordedNode<T extends ts.Node = ts.Node> = (T&{ + __recorded: any; +})|null; function escapeLiteral(value: string): string { return value.replace(/(\"|\\)/g, '\\$1').replace(/(\n)|(\r)/g, function(v, n, r) { @@ -192,6 +199,8 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { private _templateSources = new Map<ParseSourceFile, ts.SourceMapSource>(); private _exportedVariableIdentifiers = new Map<string, ts.Identifier>(); + constructor(private annotateForClosureCompiler: boolean) {} + /** * Process the source file and collect exported identifiers that refer to variables. * @@ -216,8 +225,9 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { .map( ([exportedFilePath, reexports]) => ts.createExportDeclaration( /* decorators */ undefined, - /* modifiers */ undefined, ts.createNamedExports(reexports.map( - ({name, as}) => ts.createExportSpecifier(name, as))), + /* modifiers */ undefined, + ts.createNamedExports( + reexports.map(({name, as}) => ts.createExportSpecifier(name, as))), /* moduleSpecifier */ createLiteral(exportedFilePath))); } @@ -227,13 +237,16 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { ([namespace, prefix]) => ts.createImportDeclaration( /* decorators */ undefined, /* modifiers */ undefined, - /* importClause */ ts.createImportClause( + /* importClause */ + ts.createImportClause( /* name */<ts.Identifier>(undefined as any), ts.createNamespaceImport(ts.createIdentifier(prefix))), /* moduleSpecifier */ createLiteral(namespace))); } - getNodeMap() { return this._nodeMap; } + getNodeMap() { + return this._nodeMap; + } updateSourceMap(statements: ts.Statement[]) { let lastRangeStartNode: ts.Node|undefined = undefined; @@ -320,7 +333,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { reexports = []; this._reexports.set(moduleName, reexports); } - reexports.push({name: name !, as: stmt.name}); + reexports.push({name: name!, as: stmt.name}); return null; } } @@ -336,9 +349,10 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { const tsVarStmt = this.record(stmt, ts.createVariableStatement(/* modifiers */[], varDeclList)); const exportStmt = this.record( - stmt, ts.createExportDeclaration( - /*decorators*/ undefined, /*modifiers*/ undefined, - ts.createNamedExports([ts.createExportSpecifier(stmt.name, stmt.name)]))); + stmt, + ts.createExportDeclaration( + /*decorators*/ undefined, /*modifiers*/ undefined, + ts.createNamedExports([ts.createExportSpecifier(stmt.name, stmt.name)]))); return [tsVarStmt, exportStmt]; } return this.record(stmt, ts.createVariableStatement(this.getModifiers(stmt), varDeclList)); @@ -346,14 +360,15 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitDeclareFunctionStmt(stmt: DeclareFunctionStmt) { return this.record( - stmt, ts.createFunctionDeclaration( - /* decorators */ undefined, this.getModifiers(stmt), - /* asteriskToken */ undefined, stmt.name, /* typeParameters */ undefined, - stmt.params.map( - p => ts.createParameter( - /* decorators */ undefined, /* modifiers */ undefined, - /* dotDotDotToken */ undefined, p.name)), - /* type */ undefined, this._visitStatements(stmt.statements))); + stmt, + ts.createFunctionDeclaration( + /* decorators */ undefined, this.getModifiers(stmt), + /* asteriskToken */ undefined, stmt.name, /* typeParameters */ undefined, + stmt.params.map( + p => ts.createParameter( + /* decorators */ undefined, /* modifiers */ undefined, + /* dotDotDotToken */ undefined, p.name)), + /* type */ undefined, this._visitStatements(stmt.statements))); } visitExpressionStmt(stmt: ExpressionStatement) { @@ -367,14 +382,27 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitDeclareClassStmt(stmt: ClassStmt) { const modifiers = this.getModifiers(stmt); - const fields = stmt.fields.map( - field => ts.createProperty( - /* decorators */ undefined, /* modifiers */ translateModifiers(field.modifiers), - field.name, - /* questionToken */ undefined, - /* type */ undefined, - field.initializer == null ? ts.createNull() : - field.initializer.visitExpression(this, null))); + const fields = stmt.fields.map(field => { + const property = ts.createProperty( + /* decorators */ undefined, /* modifiers */ translateModifiers(field.modifiers), + field.name, + /* questionToken */ undefined, + /* type */ undefined, + field.initializer == null ? ts.createNull() : + field.initializer.visitExpression(this, null)); + + if (this.annotateForClosureCompiler) { + // Closure compiler transforms the form `Service.ɵprov = X` into `Service$ɵprov = X`. To + // prevent this transformation, such assignments need to be annotated with @nocollapse. + // Note that tsickle is typically responsible for adding such annotations, however it + // doesn't yet handle synthetic fields added during other transformations. + ts.addSyntheticLeadingComment( + property, ts.SyntaxKind.MultiLineCommentTrivia, '* @nocollapse ', + /* hasTrailingNewLine */ false); + } + + return property; + }); const getters = stmt.getters.map( getter => ts.createGetAccessor( /* decorators */ undefined, /* modifiers */ undefined, getter.name, /* parameters */[], @@ -384,7 +412,8 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { (stmt.constructorMethod && [ts.createConstructor( /* decorators */ undefined, /* modifiers */ undefined, - /* parameters */ stmt.constructorMethod.params.map( + /* parameters */ + stmt.constructorMethod.params.map( p => ts.createParameter( /* decorators */ undefined, /* modifiers */ undefined, @@ -398,7 +427,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { method => ts.createMethod( /* decorators */ undefined, /* modifiers */ translateModifiers(method.modifiers), - /* astriskToken */ undefined, method.name !/* guarded by filter */, + /* astriskToken */ undefined, method.name!/* guarded by filter */, /* questionToken */ undefined, /* typeParameters */ undefined, method.params.map( p => ts.createParameter( @@ -406,13 +435,14 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { /* dotDotDotToken */ undefined, p.name)), /* type */ undefined, this._visitStatements(method.body))); return this.record( - stmt, ts.createClassDeclaration( - /* decorators */ undefined, modifiers, stmt.name, /* typeParameters*/ undefined, - stmt.parent && [ts.createHeritageClause( - ts.SyntaxKind.ExtendsKeyword, - [stmt.parent.visitExpression(this, null)])] || - [], - [...fields, ...getters, ...constructor, ...methods])); + stmt, + ts.createClassDeclaration( + /* decorators */ undefined, modifiers, stmt.name, /* typeParameters*/ undefined, + stmt.parent && + [ts.createHeritageClause( + ts.SyntaxKind.ExtendsKeyword, [stmt.parent.visitExpression(this, null)])] || + [], + [...fields, ...getters, ...constructor, ...methods])); } visitIfStmt(stmt: IfStmt) { @@ -426,19 +456,21 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitTryCatchStmt(stmt: TryCatchStmt): RecordedNode<ts.TryStatement> { return this.record( - stmt, ts.createTry( - this._visitStatements(stmt.bodyStmts), - ts.createCatchClause( - CATCH_ERROR_NAME, this._visitStatementsPrefix( - [ts.createVariableStatement( - /* modifiers */ undefined, - [ts.createVariableDeclaration( - CATCH_STACK_NAME, /* type */ undefined, - ts.createPropertyAccess( - ts.createIdentifier(CATCH_ERROR_NAME), - ts.createIdentifier(CATCH_STACK_NAME)))])], - stmt.catchStmts)), - /* finallyBlock */ undefined)); + stmt, + ts.createTry( + this._visitStatements(stmt.bodyStmts), + ts.createCatchClause( + CATCH_ERROR_NAME, + this._visitStatementsPrefix( + [ts.createVariableStatement( + /* modifiers */ undefined, + [ts.createVariableDeclaration( + CATCH_STACK_NAME, /* type */ undefined, + ts.createPropertyAccess( + ts.createIdentifier(CATCH_ERROR_NAME), + ts.createIdentifier(CATCH_STACK_NAME)))])], + stmt.catchStmts)), + /* finallyBlock */ undefined)); } visitThrowStmt(stmt: ThrowStmt) { @@ -464,7 +496,9 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } // ExpressionVisitor - visitWrappedNodeExpr(expr: WrappedNodeExpr<any>) { return this.record(expr, expr.node); } + visitWrappedNodeExpr(expr: WrappedNodeExpr<any>) { + return this.record(expr, expr.node); + } visitTypeofExpr(expr: TypeofExpr) { const typeOf = ts.createTypeOf(expr.expr.visitExpression(this, null)); @@ -491,8 +525,9 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitWriteVarExpr(expr: WriteVarExpr): RecordedNode<ts.BinaryExpression> { return this.record( - expr, ts.createAssignment( - ts.createIdentifier(expr.name), expr.value.visitExpression(this, null))); + expr, + ts.createAssignment( + ts.createIdentifier(expr.name), expr.value.visitExpression(this, null))); } visitWriteKeyExpr(expr: WriteKeyExpr): RecordedNode<ts.BinaryExpression> { @@ -506,9 +541,10 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitWritePropExpr(expr: WritePropExpr): RecordedNode<ts.BinaryExpression> { return this.record( - expr, ts.createAssignment( - ts.createPropertyAccess(expr.receiver.visitExpression(this, null), expr.name), - expr.value.visitExpression(this, null))); + expr, + ts.createAssignment( + ts.createPropertyAccess(expr.receiver.visitExpression(this, null), expr.name), + expr.value.visitExpression(this, null))); } visitInvokeMethodExpr(expr: InvokeMethodExpr): RecordedNode<ts.CallExpression> { @@ -522,19 +558,23 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitInvokeFunctionExpr(expr: InvokeFunctionExpr): RecordedNode<ts.CallExpression> { return this.record( - expr, ts.createCall( - expr.fn.visitExpression(this, null), /* typeArguments */ undefined, - expr.args.map(arg => arg.visitExpression(this, null)))); + expr, + ts.createCall( + expr.fn.visitExpression(this, null), /* typeArguments */ undefined, + expr.args.map(arg => arg.visitExpression(this, null)))); } visitInstantiateExpr(expr: InstantiateExpr): RecordedNode<ts.NewExpression> { return this.record( - expr, ts.createNew( - expr.classExpr.visitExpression(this, null), /* typeArguments */ undefined, - expr.args.map(arg => arg.visitExpression(this, null)))); + expr, + ts.createNew( + expr.classExpr.visitExpression(this, null), /* typeArguments */ undefined, + expr.args.map(arg => arg.visitExpression(this, null)))); } - visitLiteralExpr(expr: LiteralExpr) { return this.record(expr, createLiteral(expr.value)); } + visitLiteralExpr(expr: LiteralExpr) { + return this.record(expr, createLiteral(expr.value)); + } visitLocalizedString(expr: LocalizedString, context: any) { throw new Error('localized strings are not supported in pre-ivy mode.'); @@ -550,13 +590,14 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { expr, ts.createParen(ts.createConditional( expr.condition.visitExpression(this, null), expr.trueCase.visitExpression(this, null), - expr.falseCase !.visitExpression(this, null)))); + expr.falseCase!.visitExpression(this, null)))); } visitNotExpr(expr: NotExpr): RecordedNode<ts.PrefixUnaryExpression> { return this.record( - expr, ts.createPrefix( - ts.SyntaxKind.ExclamationToken, expr.condition.visitExpression(this, null))); + expr, + ts.createPrefix( + ts.SyntaxKind.ExclamationToken, expr.condition.visitExpression(this, null))); } visitAssertNotNullExpr(expr: AssertNotNull): RecordedNode<ts.Expression> { @@ -569,15 +610,16 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitFunctionExpr(expr: FunctionExpr) { return this.record( - expr, ts.createFunctionExpression( - /* modifiers */ undefined, /* astriskToken */ undefined, - /* name */ expr.name || undefined, - /* typeParameters */ undefined, - expr.params.map( - p => ts.createParameter( - /* decorators */ undefined, /* modifiers */ undefined, - /* dotDotDotToken */ undefined, p.name)), - /* type */ undefined, this._visitStatements(expr.statements))); + expr, + ts.createFunctionExpression( + /* modifiers */ undefined, /* astriskToken */ undefined, + /* name */ expr.name || undefined, + /* typeParameters */ undefined, + expr.params.map( + p => ts.createParameter( + /* decorators */ undefined, /* modifiers */ undefined, + /* dotDotDotToken */ undefined, p.name)), + /* type */ undefined, this._visitStatements(expr.statements))); } visitBinaryOperatorExpr(expr: BinaryOperatorExpr): @@ -659,21 +701,23 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { visitLiteralMapExpr(expr: LiteralMapExpr): RecordedNode<ts.ObjectLiteralExpression> { return this.record( - expr, ts.createObjectLiteral(expr.entries.map( - entry => ts.createPropertyAssignment( - entry.quoted || !_VALID_IDENTIFIER_RE.test(entry.key) ? - ts.createLiteral(entry.key) : - entry.key, - entry.value.visitExpression(this, null))))); + expr, + ts.createObjectLiteral(expr.entries.map( + entry => ts.createPropertyAssignment( + entry.quoted || !_VALID_IDENTIFIER_RE.test(entry.key) ? + ts.createLiteral(entry.key) : + entry.key, + entry.value.visitExpression(this, null))))); } visitCommaExpr(expr: CommaExpr): RecordedNode<ts.Expression> { return this.record( - expr, expr.parts.map(e => e.visitExpression(this, null)) - .reduce<ts.Expression|null>( - (left, right) => - left ? ts.createBinary(left, ts.SyntaxKind.CommaToken, right) : right, - null)); + expr, + expr.parts.map(e => e.visitExpression(this, null)) + .reduce<ts.Expression|null>( + (left, right) => + left ? ts.createBinary(left, ts.SyntaxKind.CommaToken, right) : right, + null)); } private _visitStatements(statements: Statement[]): ts.Block { @@ -688,7 +732,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { private _visitIdentifier(value: ExternalReference): ts.Expression { // name can only be null during JIT which never executes this code. - const moduleName = value.moduleName, name = value.name !; + const moduleName = value.moduleName, name = value.name!; let prefixIdent: ts.Identifier|null = null; if (moduleName) { let prefix = this._importsWithPrefixes.get(moduleName); @@ -713,7 +757,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor { } -function getMethodName(methodRef: {name: string | null; builtin: BuiltinMethod | null}): string { +function getMethodName(methodRef: {name: string|null; builtin: BuiltinMethod | null}): string { if (methodRef.name) { return methodRef.name; } else { @@ -743,6 +787,6 @@ function modifierFromModifier(modifier: StmtModifier): ts.Modifier { return error(`unknown statement modifier`); } -function translateModifiers(modifiers: StmtModifier[] | null): ts.Modifier[]|undefined { - return modifiers == null ? undefined : modifiers !.map(modifierFromModifier); +function translateModifiers(modifiers: StmtModifier[]|null): ts.Modifier[]|undefined { + return modifiers == null ? undefined : modifiers!.map(modifierFromModifier); } diff --git a/packages/compiler-cli/src/transformers/node_emitter_transform.ts b/packages/compiler-cli/src/transformers/node_emitter_transform.ts index fffdc8e13e32a..bd72bd06e269e 100644 --- a/packages/compiler-cli/src/transformers/node_emitter_transform.ts +++ b/packages/compiler-cli/src/transformers/node_emitter_transform.ts @@ -30,10 +30,10 @@ function getPreamble(original: string) { * list of statements as their body. */ export function getAngularEmitterTransformFactory( - generatedFiles: Map<string, GeneratedFile>, program: ts.Program): () => - (sourceFile: ts.SourceFile) => ts.SourceFile { + generatedFiles: Map<string, GeneratedFile>, program: ts.Program, + annotateForClosureCompiler: boolean): () => (sourceFile: ts.SourceFile) => ts.SourceFile { return function() { - const emitter = new TypeScriptNodeEmitter(); + const emitter = new TypeScriptNodeEmitter(annotateForClosureCompiler); return function(sourceFile: ts.SourceFile): ts.SourceFile { const g = generatedFiles.get(sourceFile.fileName); const orig = g && program.getSourceFile(g.srcFileUrl); diff --git a/packages/compiler-cli/src/transformers/program.ts b/packages/compiler-cli/src/transformers/program.ts index 3855bddf1bc76..89d483b6ff226 100644 --- a/packages/compiler-cli/src/transformers/program.ts +++ b/packages/compiler-cli/src/transformers/program.ts @@ -7,27 +7,26 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotCompiler, AotCompilerHost, AotCompilerOptions, EmitterVisitorContext, FormattedMessageChain, GeneratedFile, MessageBundle, NgAnalyzedFile, NgAnalyzedFileWithInjectables, NgAnalyzedModules, ParseSourceSpan, PartialModule, Position, Serializer, StaticSymbol, TypeScriptEmitter, Xliff, Xliff2, Xmb, core, createAotCompiler, getParseErrors, isFormattedError, isSyntaxError} from '@angular/compiler'; +import {AotCompiler, AotCompilerHost, AotCompilerOptions, core, createAotCompiler, EmitterVisitorContext, FormattedMessageChain, GeneratedFile, getParseErrors, isFormattedError, isSyntaxError, MessageBundle, NgAnalyzedFile, NgAnalyzedFileWithInjectables, NgAnalyzedModules, ParseSourceSpan, PartialModule, Position, Serializer, StaticSymbol, TypeScriptEmitter, Xliff, Xliff2, Xmb} from '@angular/compiler'; import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; -import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics'; -import {MetadataCollector, ModuleMetadata, createBundleIndexHost} from '../metadata'; +import {translateDiagnostics, TypeCheckHost} from '../diagnostics/translate_diagnostics'; +import {createBundleIndexHost, MetadataCollector, ModuleMetadata} from '../metadata'; import {NgtscProgram} from '../ngtsc/program'; import {verifySupportedTypeScriptVersion} from '../typescript_support'; import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, DiagnosticMessageChain, EmitFlags, LazyRoute, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback, TsMergeEmitResultsCallback} from './api'; -import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host'; -import {InlineResourcesMetadataTransformer, getInlineResourcesTransformFactory} from './inline_resources'; -import {LowerMetadataTransform, getExpressionLoweringTransformFactory} from './lower_expressions'; +import {CodeGenerator, getOriginalReferences, TsCompilerAotCompilerTypeCheckHostAdapter} from './compiler_host'; +import {getInlineResourcesTransformFactory, InlineResourcesMetadataTransformer} from './inline_resources'; +import {getExpressionLoweringTransformFactory, LowerMetadataTransform} from './lower_expressions'; import {MetadataCache, MetadataTransformer} from './metadata_cache'; -import {nocollapseHack} from './nocollapse_hack'; import {getAngularEmitterTransformFactory} from './node_emitter_transform'; import {PartialModuleMetadataTransformer} from './r3_metadata_transform'; -import {StripDecoratorsMetadataTransformer, getDecoratorStripTransformerFactory} from './r3_strip_decorators'; +import {getDecoratorStripTransformerFactory, StripDecoratorsMetadataTransformer} from './r3_strip_decorators'; import {getAngularClassTransformerFactory} from './r3_transform'; -import {DTS, GENERATED_FILES, StructureIsReused, TS, createMessageDiagnostic, isInRootDir, ngToTsDiagnostic, tsStructureIsReused, userError} from './util'; +import {createMessageDiagnostic, DTS, GENERATED_FILES, isInRootDir, ngToTsDiagnostic, StructureIsReused, TS, tsStructureIsReused, userError} from './util'; /** @@ -61,18 +60,23 @@ const emptyModules: NgAnalyzedModules = { files: [] }; -const defaultEmitCallback: TsEmitCallback = - ({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, - customTransformers}) => - program.emit( - targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); +const defaultEmitCallback: TsEmitCallback = ({ + program, + targetSourceFile, + writeFile, + cancellationToken, + emitOnlyDtsFiles, + customTransformers +}) => + program.emit( + targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); class AngularCompilerProgram implements Program { private rootNames: string[]; private metadataCache: MetadataCache; // Metadata cache used exclusively for the flat module index // TODO(issue/24571): remove '!'. - private flatModuleMetadataCache !: MetadataCache; + private flatModuleMetadataCache!: MetadataCache; private loweringMetadataTransform: LowerMetadataTransform; private oldProgramLibrarySummaries: Map<string, LibrarySummary>|undefined; private oldProgramEmittedGeneratedFiles: Map<string, GeneratedFile>|undefined; @@ -85,18 +89,18 @@ class AngularCompilerProgram implements Program { // Lazily initialized fields // TODO(issue/24571): remove '!'. - private _compiler !: AotCompiler; + private _compiler!: AotCompiler; // TODO(issue/24571): remove '!'. - private _hostAdapter !: TsCompilerAotCompilerTypeCheckHostAdapter; + private _hostAdapter!: TsCompilerAotCompilerTypeCheckHostAdapter; // TODO(issue/24571): remove '!'. - private _tsProgram !: ts.Program; + private _tsProgram!: ts.Program; private _analyzedModules: NgAnalyzedModules|undefined; private _analyzedInjectables: NgAnalyzedFileWithInjectables[]|undefined; private _structuralDiagnostics: Diagnostic[]|undefined; private _programWithStubs: ts.Program|undefined; private _optionsDiagnostics: Diagnostic[] = []; // TODO(issue/24571): remove '!'. - private _reifiedDecorators !: Set<StaticSymbol>; + private _reifiedDecorators!: Set<StaticSymbol>; constructor( rootNames: ReadonlyArray<string>, private options: CompilerOptions, @@ -125,7 +129,7 @@ class AngularCompilerProgram implements Program { code: DEFAULT_ERROR_CODE }))); } else { - this.rootNames.push(indexName !); + this.rootNames.push(indexName!); this.host = bundleHost; } } @@ -176,7 +180,9 @@ class AngularCompilerProgram implements Program { return result; } - getTsProgram(): ts.Program { return this.tsProgram; } + getTsProgram(): ts.Program { + return this.tsProgram; + } getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken) { return this.tsProgram.getOptionsDiagnostics(cancellationToken); @@ -257,17 +263,19 @@ class AngularCompilerProgram implements Program { return this._emitRender2(parameters); } - private _emitRender3( - { - emitFlags = EmitFlags.Default, cancellationToken, customTransformers, - emitCallback = defaultEmitCallback, mergeEmitResultsCallback = mergeEmitResults, - }: { - emitFlags?: EmitFlags, - cancellationToken?: ts.CancellationToken, - customTransformers?: CustomTransformers, - emitCallback?: TsEmitCallback, - mergeEmitResultsCallback?: TsMergeEmitResultsCallback, - } = {}): ts.EmitResult { + private _emitRender3({ + emitFlags = EmitFlags.Default, + cancellationToken, + customTransformers, + emitCallback = defaultEmitCallback, + mergeEmitResultsCallback = mergeEmitResults, + }: { + emitFlags?: EmitFlags, + cancellationToken?: ts.CancellationToken, + customTransformers?: CustomTransformers, + emitCallback?: TsEmitCallback, + mergeEmitResultsCallback?: TsMergeEmitResultsCallback, + } = {}): ts.EmitResult { const emitStart = Date.now(); if ((emitFlags & (EmitFlags.JS | EmitFlags.DTS | EmitFlags.Metadata | EmitFlags.Codegen)) === 0) { @@ -277,16 +285,10 @@ class AngularCompilerProgram implements Program { // analyzedModules and analyzedInjectables are created together. If one exists, so does the // other. const modules = - this.compiler.emitAllPartialModules(this.analyzedModules, this._analyzedInjectables !); + this.compiler.emitAllPartialModules(this.analyzedModules, this._analyzedInjectables!); const writeTsFile: ts.WriteFileCallback = (outFileName, outData, writeByteOrderMark, onError?, sourceFiles?) => { - const sourceFile = sourceFiles && sourceFiles.length == 1 ? sourceFiles[0] : null; - let genFile: GeneratedFile|undefined; - if (this.options.annotateForClosureCompiler && sourceFile && - TS.test(sourceFile.fileName)) { - outData = nocollapseHack(outData); - } this.writeFile(outFileName, outData, writeByteOrderMark, onError, undefined, sourceFiles); }; @@ -313,7 +315,8 @@ class AngularCompilerProgram implements Program { program: this.tsProgram, host: this.host, options: this.options, - writeFile: writeTsFile, emitOnlyDtsFiles, + writeFile: writeTsFile, + emitOnlyDtsFiles, customTransformers: tsCustomTransformers }); } finally { @@ -326,17 +329,19 @@ class AngularCompilerProgram implements Program { } } - private _emitRender2( - { - emitFlags = EmitFlags.Default, cancellationToken, customTransformers, - emitCallback = defaultEmitCallback, mergeEmitResultsCallback = mergeEmitResults, - }: { - emitFlags?: EmitFlags, - cancellationToken?: ts.CancellationToken, - customTransformers?: CustomTransformers, - emitCallback?: TsEmitCallback, - mergeEmitResultsCallback?: TsMergeEmitResultsCallback, - } = {}): ts.EmitResult { + private _emitRender2({ + emitFlags = EmitFlags.Default, + cancellationToken, + customTransformers, + emitCallback = defaultEmitCallback, + mergeEmitResultsCallback = mergeEmitResults, + }: { + emitFlags?: EmitFlags, + cancellationToken?: ts.CancellationToken, + customTransformers?: CustomTransformers, + emitCallback?: TsEmitCallback, + mergeEmitResultsCallback?: TsMergeEmitResultsCallback, + } = {}): ts.EmitResult { const emitStart = Date.now(); if (emitFlags & EmitFlags.I18nBundle) { const locale = this.options.i18nOutLocale || null; @@ -377,9 +382,6 @@ class AngularCompilerProgram implements Program { emittedSourceFiles.push(originalFile); } } - if (this.options.annotateForClosureCompiler && TS.test(sourceFile.fileName)) { - outData = nocollapseHack(outData); - } } this.writeFile(outFileName, outData, writeByteOrderMark, onError, genFile, sourceFiles); }; @@ -423,7 +425,8 @@ class AngularCompilerProgram implements Program { program: this.tsProgram, host: this.host, options: this.options, - writeFile: writeTsFile, emitOnlyDtsFiles, + writeFile: writeTsFile, + emitOnlyDtsFiles, customTransformers: tsCustomTransformers, targetSourceFile: this.tsProgram.getSourceFile(fileName), }))); @@ -433,7 +436,8 @@ class AngularCompilerProgram implements Program { program: this.tsProgram, host: this.host, options: this.options, - writeFile: writeTsFile, emitOnlyDtsFiles, + writeFile: writeTsFile, + emitOnlyDtsFiles, customTransformers: tsCustomTransformers }); emittedUserTsCount = this.tsProgram.getSourceFiles().length - genTsFiles.length; @@ -476,7 +480,7 @@ class AngularCompilerProgram implements Program { if (emitFlags & EmitFlags.Codegen) { genJsonFiles.forEach(gf => { const outFileName = srcToOutPath(gf.genFileUrl); - this.writeFile(outFileName, gf.source !, false, undefined, gf); + this.writeFile(outFileName, gf.source!, false, undefined, gf); }); } let metadataJsonCount = 0; @@ -511,21 +515,21 @@ class AngularCompilerProgram implements Program { if (!this._compiler) { this._createCompiler(); } - return this._compiler !; + return this._compiler!; } private get hostAdapter(): TsCompilerAotCompilerTypeCheckHostAdapter { if (!this._hostAdapter) { this._createCompiler(); } - return this._hostAdapter !; + return this._hostAdapter!; } private get analyzedModules(): NgAnalyzedModules { if (!this._analyzedModules) { this.initSync(); } - return this._analyzedModules !; + return this._analyzedModules!; } private get structuralDiagnostics(): ReadonlyArray<Diagnostic> { @@ -541,7 +545,7 @@ class AngularCompilerProgram implements Program { if (!this._tsProgram) { this.initSync(); } - return this._tsProgram !; + return this._tsProgram!; } private get reifiedDecorators(): Set<StaticSymbol> { @@ -572,11 +576,13 @@ class AngularCompilerProgram implements Program { getExpressionLoweringTransformFactory(this.loweringMetadataTransform, this.tsProgram)); metadataTransforms.push(this.loweringMetadataTransform); } + const annotateForClosureCompiler = this.options.annotateForClosureCompiler || false; if (genFiles) { - beforeTs.push(getAngularEmitterTransformFactory(genFiles, this.getTsProgram())); + beforeTs.push(getAngularEmitterTransformFactory( + genFiles, this.getTsProgram(), annotateForClosureCompiler)); } if (partialModules) { - beforeTs.push(getAngularClassTransformerFactory(partialModules)); + beforeTs.push(getAngularClassTransformerFactory(partialModules, annotateForClosureCompiler)); // If we have partial modules, the cached metadata might be incorrect as it doesn't reflect // the partial module transforms. @@ -625,7 +631,7 @@ class AngularCompilerProgram implements Program { private _createCompiler() { const codegen: CodeGenerator = { generateFile: (genFileName, baseFileName) => - this._compiler.emitBasicStub(genFileName, baseFileName), + this._compiler.emitBasicStub(genFileName, baseFileName), findGeneratedFileNames: (fileName) => this._compiler.findGeneratedFileNames(fileName), }; @@ -654,7 +660,7 @@ class AngularCompilerProgram implements Program { const codegen: CodeGenerator = { generateFile: (genFileName, baseFileName) => - this.compiler.emitBasicStub(genFileName, baseFileName), + this.compiler.emitBasicStub(genFileName, baseFileName), findGeneratedFileNames: (fileName) => this.compiler.findGeneratedFileNames(fileName), }; @@ -700,7 +706,7 @@ class AngularCompilerProgram implements Program { if (generate) { // Note: ! is ok as hostAdapter.shouldGenerateFile will always return a baseFileName // for .ngfactory.ts files. - const genFile = this.compiler.emitTypeCheckStub(sf.fileName, baseFileName !); + const genFile = this.compiler.emitTypeCheckStub(sf.fileName, baseFileName!); if (genFile) { this.hostAdapter.updateGeneratedFile(genFile); } @@ -790,11 +796,12 @@ class AngularCompilerProgram implements Program { private getSourceFilesForEmit(): ts.SourceFile[]|undefined { // TODO(tbosch): if one of the files contains a `const enum` // always emit all files -> return undefined! - let sourceFilesToEmit = this.tsProgram.getSourceFiles().filter( - sf => { return !sf.isDeclarationFile && !GENERATED_FILES.test(sf.fileName); }); + let sourceFilesToEmit = this.tsProgram.getSourceFiles().filter(sf => { + return !sf.isDeclarationFile && !GENERATED_FILES.test(sf.fileName); + }); if (this.oldProgramEmittedSourceFiles) { sourceFilesToEmit = sourceFilesToEmit.filter(sf => { - const oldFile = this.oldProgramEmittedSourceFiles !.get(sf.fileName); + const oldFile = this.oldProgramEmittedSourceFiles!.get(sf.fileName); return sf !== oldFile; }); } @@ -857,7 +864,8 @@ class AngularCompilerProgram implements Program { export function createProgram({rootNames, options, host, oldProgram}: { rootNames: ReadonlyArray<string>, options: CompilerOptions, - host: CompilerHost, oldProgram?: Program + host: CompilerHost, + oldProgram?: Program }): Program { if (options.enableIvy !== false) { return new NgtscProgram(rootNames, options, host, oldProgram as NgtscProgram | undefined); @@ -895,7 +903,9 @@ function getAotCompilerOptions(options: CompilerOptions): AotCompilerOptions { return { locale: options.i18nInLocale, i18nFormat: options.i18nInFormat || options.i18nOutFormat, - i18nUseExternalIds: options.i18nUseExternalIds, translations, missingTranslation, + i18nUseExternalIds: options.i18nUseExternalIds, + translations, + missingTranslation, enableSummariesForJit: options.enableSummariesForJit, preserveWhitespaces: options.preserveWhitespaces, fullTemplateTypeCheck: options.fullTemplateTypeCheck, @@ -939,8 +949,8 @@ function normalizeSeparators(path: string): string { * POSIX normalized paths for output paths. */ export function createSrcToOutPathMapper( - outDir: string | undefined, sampleSrcFileName: string | undefined, - sampleOutFileName: string | undefined, host: { + outDir: string|undefined, sampleSrcFileName: string|undefined, + sampleOutFileName: string|undefined, host: { dirname: typeof path.dirname, resolve: typeof path.resolve, relative: typeof path.relative @@ -979,14 +989,14 @@ export function createSrcToOutPathMapper( } export function i18nExtract( - formatName: string | null, outFile: string | null, host: ts.CompilerHost, - options: CompilerOptions, bundle: MessageBundle): string[] { + formatName: string|null, outFile: string|null, host: ts.CompilerHost, options: CompilerOptions, + bundle: MessageBundle): string[] { formatName = formatName || 'xlf'; // Checks the format and returns the extension const ext = i18nGetExtension(formatName); const content = i18nSerialize(bundle, formatName, options); const dstFile = outFile || `messages.${ext}`; - const dstPath = path.resolve(options.outDir || options.basePath !, dstFile); + const dstPath = path.resolve(options.outDir || options.basePath!, dstFile); host.writeFile(dstPath, content, false, undefined, []); return [dstPath]; } @@ -1053,7 +1063,7 @@ function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult { function diagnosticSourceOfSpan(span: ParseSourceSpan): ts.SourceFile { // For diagnostics, TypeScript only uses the fileName and text properties. // The redundant '()' are here is to avoid having clang-format breaking the line incorrectly. - return ({ fileName: span.start.file.url, text: span.start.file.content } as any); + return ({fileName: span.start.file.url, text: span.start.file.content} as any); } function diagnosticSourceOfFileName(fileName: string, program: ts.Program): ts.SourceFile { @@ -1063,7 +1073,7 @@ function diagnosticSourceOfFileName(fileName: string, program: ts.Program): ts.S // If we are reporting diagnostics for a source file that is not in the project then we need // to fake a source file so the diagnostic formatting routines can emit the file name. // The redundant '()' are here is to avoid having clang-format breaking the line incorrectly. - return ({ fileName, text: '' } as any); + return ({fileName, text: ''} as any); } diff --git a/packages/compiler-cli/src/transformers/r3_metadata_transform.ts b/packages/compiler-cli/src/transformers/r3_metadata_transform.ts index ace19fea1103a..d0aefbf5531ad 100644 --- a/packages/compiler-cli/src/transformers/r3_metadata_transform.ts +++ b/packages/compiler-cli/src/transformers/r3_metadata_transform.ts @@ -9,7 +9,7 @@ import {ClassStmt, PartialModule, Statement, StmtModifier} from '@angular/compiler'; import * as ts from 'typescript'; -import {MetadataCollector, MetadataValue, ModuleMetadata, isClassMetadata} from '../metadata/index'; +import {isClassMetadata, MetadataCollector, MetadataValue, ModuleMetadata} from '../metadata/index'; import {MetadataTransformer, ValueTransform} from './metadata_cache'; diff --git a/packages/compiler-cli/src/transformers/r3_strip_decorators.ts b/packages/compiler-cli/src/transformers/r3_strip_decorators.ts index c5feabee50aa9..1d2bba7cdb136 100644 --- a/packages/compiler-cli/src/transformers/r3_strip_decorators.ts +++ b/packages/compiler-cli/src/transformers/r3_strip_decorators.ts @@ -9,7 +9,7 @@ import {StaticReflector, StaticSymbol} from '@angular/compiler'; import * as ts from 'typescript'; -import {MetadataValue, isClassMetadata, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicCallExpression} from '../metadata'; +import {isClassMetadata, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicCallExpression, MetadataValue} from '../metadata'; import {MetadataTransformer, ValueTransform} from './metadata_cache'; @@ -39,8 +39,14 @@ export function getDecoratorStripTransformerFactory( }); if (decorators.length !== node.decorators.length) { return ts.updateClassDeclaration( - node, decorators, node.modifiers, node.name, node.typeParameters, - node.heritageClauses || [], node.members, ); + node, + decorators, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses || [], + node.members, + ); } return node; }; @@ -130,7 +136,7 @@ function resolveToStaticSymbol( if (!ts.isImportSpecifier(decl)) { return null; } - const moduleSpecifier = decl.parent !.parent !.parent !.moduleSpecifier; + const moduleSpecifier = decl.parent!.parent!.parent!.moduleSpecifier; if (!ts.isStringLiteral(moduleSpecifier)) { return null; } diff --git a/packages/compiler-cli/src/transformers/r3_transform.ts b/packages/compiler-cli/src/transformers/r3_transform.ts index 5c21b8bc82e22..cf61b8ad2f312 100644 --- a/packages/compiler-cli/src/transformers/r3_transform.ts +++ b/packages/compiler-cli/src/transformers/r3_transform.ts @@ -17,7 +17,8 @@ export type TransformerFactory = (context: ts.TransformationContext) => Transfor /** * Returns a transformer that adds the requested static methods specified by modules. */ -export function getAngularClassTransformerFactory(modules: PartialModule[]): TransformerFactory { +export function getAngularClassTransformerFactory( + modules: PartialModule[], annotateForClosureCompiler: boolean): TransformerFactory { if (modules.length === 0) { // If no modules are specified, just return an identity transform. return () => sf => sf; @@ -27,7 +28,7 @@ export function getAngularClassTransformerFactory(modules: PartialModule[]): Tra return function(sourceFile: ts.SourceFile): ts.SourceFile { const module = moduleMap.get(sourceFile.fileName); if (module && module.statements.length > 0) { - const [newSourceFile] = updateSourceFile(sourceFile, module, context); + const [newSourceFile] = updateSourceFile(sourceFile, module, annotateForClosureCompiler); return newSourceFile; } return sourceFile; diff --git a/packages/compiler-cli/src/transformers/util.ts b/packages/compiler-cli/src/transformers/util.ts index 8d35326e27a29..9df0d763657fc 100644 --- a/packages/compiler-cli/src/transformers/util.ts +++ b/packages/compiler-cli/src/transformers/util.ts @@ -16,7 +16,11 @@ export const GENERATED_FILES = /(.*?)\.(ngfactory|shim\.ngstyle|ngstyle|ngsummar export const DTS = /\.d\.ts$/; export const TS = /^(?!.*\.d\.ts$).*\.ts$/; -export const enum StructureIsReused {Not = 0, SafeModules = 1, Completely = 2} +export const enum StructureIsReused { + Not = 0, + SafeModules = 1, + Completely = 2 +} // Note: This is an internal property in TypeScript. Use it only for assertions and tests. export function tsStructureIsReused(program: ts.Program): StructureIsReused { @@ -36,7 +40,8 @@ export function createMessageDiagnostic(messageText: string): ts.Diagnostic&Diag file: undefined, start: undefined, length: undefined, - category: ts.DiagnosticCategory.Message, messageText, + category: ts.DiagnosticCategory.Message, + messageText, code: DEFAULT_ERROR_CODE, source: SOURCE, }; @@ -76,7 +81,7 @@ export function ngToTsDiagnostic(ng: Diagnostic): ts.Diagnostic { // Note: We can't use a real ts.SourceFile, // but we can at least mirror the properties `fileName` and `text`, which // are mostly used for error reporting. - file = { fileName: ng.span.start.file.url, text: ng.span.start.file.content } as ts.SourceFile; + file = {fileName: ng.span.start.file.url, text: ng.span.start.file.content} as ts.SourceFile; start = ng.span.start.offset; length = ng.span.end.offset - start; } @@ -84,6 +89,8 @@ export function ngToTsDiagnostic(ng: Diagnostic): ts.Diagnostic { file, messageText: ng.messageText, category: ng.category, - code: ng.code, start, length, + code: ng.code, + start, + length, }; } diff --git a/packages/compiler-cli/src/typescript_support.ts b/packages/compiler-cli/src/typescript_support.ts index 3a3e3e6476ed1..98945def78e8b 100644 --- a/packages/compiler-cli/src/typescript_support.ts +++ b/packages/compiler-cli/src/typescript_support.ts @@ -49,8 +49,8 @@ export function restoreTypeScriptVersionForTesting(): void { */ export function checkVersion(version: string, minVersion: string, maxVersion: string) { if ((compareVersions(version, minVersion) < 0 || compareVersions(version, maxVersion) >= 0)) { - throw new Error( - `The Angular Compiler requires TypeScript >=${minVersion} and <${maxVersion} but ${version} was found instead.`); + throw new Error(`The Angular Compiler requires TypeScript >=${minVersion} and <${ + maxVersion} but ${version} was found instead.`); } } diff --git a/packages/compiler-cli/test/compliance/mock_compile.ts b/packages/compiler-cli/test/compliance/mock_compile.ts index e0c905ded73ff..bae4cc8632d94 100644 --- a/packages/compiler-cli/test/compliance/mock_compile.ts +++ b/packages/compiler-cli/test/compliance/mock_compile.ts @@ -7,8 +7,9 @@ */ import {AotCompilerOptions} from '@angular/compiler'; import {escapeRegExp} from '@angular/compiler/src/util'; -import {MockCompilerHost, MockData, MockDirectory, arrayToMockDir, toMockFileArray} from '@angular/compiler/test/aot/test_util'; +import {arrayToMockDir, MockCompilerHost, MockData, MockDirectory, toMockFileArray} from '@angular/compiler/test/aot/test_util'; import * as ts from 'typescript'; + import {NodeJSFileSystem, setFileSystem} from '../../src/ngtsc/file_system'; import {NgtscProgram} from '../../src/ngtsc/program'; @@ -22,10 +23,11 @@ const NUMBER = /\d+/; const ELLIPSIS = '…'; const TOKEN = new RegExp( - `\\s*((${IDENTIFIER.source})|(${BACKTICK_STRING.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source}|${ELLIPSIS})\\s*`, + `\\s*((${IDENTIFIER.source})|(${BACKTICK_STRING.source})|(${OPERATOR.source})|(${ + STRING.source})|${NUMBER.source}|${ELLIPSIS})\\s*`, 'y'); -type Piece = string | RegExp; +type Piece = string|RegExp; const SKIP = /(?:.|\n|\r)*/; @@ -116,15 +118,16 @@ export function expectEmit( const context = fullContext.length > contextLength ? `...${fullContext.substr(-contextLength)}` : fullContext; - fail( - `${description}: Failed to find "${expectedPiece}" after "${context}" in:\n'${source.substr(0,last)}[<---HERE expected "${expectedPiece}"]${source.substr(last)}'`); + fail(`${description}: Failed to find "${expectedPiece}" after "${context}" in:\n'${ + source.substr(0, last)}[<---HERE expected "${expectedPiece}"]${source.substr(last)}'`); return; } else { last = (m.index || 0) + m[0].length; } } fail( - `Test helper failure: Expected expression failed but the reporting logic could not find where it failed in: ${source}`); + `Test helper failure: Expected expression failed but the reporting logic could not find where it failed in: ${ + source}`); } else { if (assertIdentifiers) { // It might be possible to add the constraints in the original regexp (see `buildMatcher`) @@ -141,8 +144,8 @@ export function expectEmit( const name = matches[groups.get(id) as number]; const regexp = assertIdentifiers[id]; if (!regexp.test(name)) { - throw Error( - `${description}: The matching identifier "${id}" is "${name}" which doesn't match ${regexp}`); + throw Error(`${description}: The matching identifier "${id}" is "${ + name}" which doesn't match ${regexp}`); } } } @@ -160,7 +163,7 @@ const MATCHING_IDENT = /^\$.*\$$/; * - the `regexp` to be used to match the generated code, * - the `groups` which maps `$...$` identifier to their position in the regexp matches. */ -function buildMatcher(pieces: (string | RegExp)[]): {regexp: RegExp, groups: Map<string, number>} { +function buildMatcher(pieces: (string|RegExp)[]): {regexp: RegExp, groups: Map<string, number>} { const results: string[] = []; let first = true; let group = 0; @@ -196,7 +199,9 @@ function buildMatcher(pieces: (string | RegExp)[]): {regexp: RegExp, groups: Map export function compile( data: MockDirectory, angularFiles: MockData, options: AotCompilerOptions = {}, - errorCollector: (error: any, fileName?: string) => void = error => { throw error;}): { + errorCollector: (error: any, fileName?: string) => void = error => { + throw error; + }): { source: string, } { setFileSystem(new NodeJSFileSystem()); @@ -211,7 +216,8 @@ export function compile( target: ts.ScriptTarget.ES2015, module: ts.ModuleKind.ES2015, moduleResolution: ts.ModuleResolutionKind.NodeJs, - enableI18nLegacyMessageIdFormat: false, ...options, + enableI18nLegacyMessageIdFormat: false, + ...options, }, mockCompilerHost); program.emit(); diff --git a/packages/compiler-cli/test/compliance/mock_compiler_spec.ts b/packages/compiler-cli/test/compliance/mock_compiler_spec.ts index a1422aa4cc832..fe6166022ce3c 100644 --- a/packages/compiler-cli/test/compliance/mock_compiler_spec.ts +++ b/packages/compiler-cli/test/compliance/mock_compiler_spec.ts @@ -100,14 +100,14 @@ describe('mock_compiler', () => { it('should be able to properly handle string literals with escaped quote', () => { const files = { app: { - 'test.ts': String.raw `const identifier = "\"quoted\"";`, + 'test.ts': String.raw`const identifier = "\"quoted\"";`, } }; const result = compile(files, angularFiles); expect(() => { - expectEmit(result.source, String.raw `const $a$ = "\"quoted\"";`, 'Output does not match.'); + expectEmit(result.source, String.raw`const $a$ = "\"quoted\"";`, 'Output does not match.'); }).not.toThrow(); }); }); diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index 1df894c1c0a52..00f1155b5876b 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -16,7 +16,6 @@ import {compile, expectEmit} from './mock_compile'; * test in compiler_canonical_spec.ts should have a corresponding test here. */ describe('compiler compliance', () => { - const angularFiles = setup({ compileAngular: false, compileAnimations: false, @@ -49,7 +48,8 @@ describe('compiler compliance', () => { // The template should look like this (where IDENT is a wild card for an identifier): const template = ` … - consts: [["title", "Hello", ${AttributeMarker.Classes}, "my-app"], ["cx", "20", "cy", "30", "r", "50"]], + consts: [["title", "Hello", ${ + AttributeMarker.Classes}, "my-app"], ["cx", "20", "cy", "30", "r", "50"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); @@ -536,7 +536,6 @@ describe('compiler compliance', () => { const result = compile(files, angularFiles); expectEmit(result.source, template, 'Incorrect template'); }); - }); describe('components & directives', () => { @@ -867,7 +866,6 @@ describe('compiler compliance', () => { }); describe('value composition', () => { - it('should support array literals', () => { const files = { app: { @@ -1143,7 +1141,6 @@ describe('compiler compliance', () => { }); describe('content projection', () => { - it('should support content projection in root template', () => { const files = { app: { @@ -1319,7 +1316,8 @@ describe('compiler compliance', () => { } const $_c4$ = [[["span", "title", "tofirst"]], "*"]; … - consts: [["id", "second", ${AttributeMarker.Template}, "ngIf"], ["id", "third", ${AttributeMarker.Template}, "ngIf"], ["id", "second"], ["id", "third"]], + consts: [["id", "second", ${AttributeMarker.Template}, "ngIf"], ["id", "third", ${ + AttributeMarker.Template}, "ngIf"], ["id", "second"], ["id", "third"]], template: function Cmp_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵprojectionDef($_c4$); @@ -1534,7 +1532,8 @@ describe('compiler compliance', () => { decls: 1, vars: 1, consts: [ - ["ngProjectAs", ".someclass", ${AttributeMarker.ProjectAs}, ["", 8, "someclass"], ${AttributeMarker.Template}, "ngIf"], + ["ngProjectAs", ".someclass", ${AttributeMarker.ProjectAs}, ["", 8, "someclass"], ${ + AttributeMarker.Template}, "ngIf"], ["ngProjectAs", ".someclass", ${AttributeMarker.ProjectAs}, ["", 8, "someclass"]] ], template: function MyApp_Template(rf, ctx) { @@ -1552,7 +1551,6 @@ describe('compiler compliance', () => { const result = compile(files, angularFiles); expectEmit(result.source, SimpleComponentDefinition, 'Incorrect MyApp definition'); }); - }); describe('queries', () => { @@ -2044,9 +2042,7 @@ describe('compiler compliance', () => { }); describe('pipes', () => { - it('should render pipes', () => { - const files = { app: { 'spec.ts': ` @@ -2217,7 +2213,6 @@ describe('compiler compliance', () => { it('should generate the proper instruction when injecting ChangeDetectorRef into a pipe', () => { - const files = { app: { 'spec.ts': ` @@ -2282,7 +2277,6 @@ describe('compiler compliance', () => { expectEmit(source, MyOtherPipeDefinition, 'Invalid alternate pipe definition'); expectEmit(source, MyOtherPipeFactory, 'Invalid alternate pipe factory function'); }); - }); it('local reference', () => { @@ -2477,7 +2471,8 @@ describe('compiler compliance', () => { } // ... - consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], ["foo", ""], [${AttributeMarker.Template}, "ngIf"]], + consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], ["foo", ""], [${ + AttributeMarker.Template}, "ngIf"]], template:function MyComponent_Template(rf, ctx){ if (rf & 1) { $i0$.ɵɵtemplate(0, MyComponent_div_0_Template, 4, 1, "div", 0); @@ -3280,7 +3275,6 @@ describe('compiler compliance', () => { const result = compile(files, angularFiles); expectEmit(result.source, MyAppDeclaration, 'Invalid component definition'); }); - }); describe('inherited base classes', () => { @@ -3849,7 +3843,7 @@ describe('compiler compliance', () => { } }; const result = compile(files, angularFiles); - expect(result.source.match(/ɵdir/g) !.length).toBe(1); + expect(result.source.match(/ɵdir/g)!.length).toBe(1); }); }); }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts index 98cdd94271e88..1a962dc5ee17a 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts @@ -405,7 +405,6 @@ describe('compiler compliance: bindings', () => { expectEmit(result.source, template, 'Incorrect template'); }); - }); describe('attribute bindings', () => { @@ -640,13 +639,13 @@ describe('compiler compliance: bindings', () => { }; const template = ` - consts: [["target", "_blank", "aria-label", "link", ${AttributeMarker.Bindings}, "title", "id", "customEvent"]], + consts: [["target", "_blank", "aria-label", "link", ${ + AttributeMarker.Bindings}, "title", "id", "customEvent"]], … `; const result = compile(files, angularFiles); expectEmit(result.source, template, 'Incorrect attribute array'); }); - }); describe('host bindings', () => { @@ -850,12 +849,15 @@ describe('compiler compliance: bindings', () => { HostAttributeComp.ɵcmp = $r3$.ɵɵdefineComponent({ type: HostAttributeComp, selectors: [["my-host-attribute-component"]], - hostAttrs: ["title", "hello there from component", ${AttributeMarker.Styles}, "opacity", "1"], + hostAttrs: ["title", "hello there from component", ${ + AttributeMarker.Styles}, "opacity", "1"], … HostAttributeDir.ɵdir = $r3$.ɵɵdefineDirective({ type: HostAttributeDir, selectors: [["", "hostAttributeDir", ""]], - hostAttrs: ["title", "hello there from directive", ${AttributeMarker.Classes}, "one", "two", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"], + hostAttrs: ["title", "hello there from directive", ${ + AttributeMarker.Classes}, "one", "two", ${ + AttributeMarker.Styles}, "width", "200px", "height", "500px"], hostVars: 4, hostBindings: function HostAttributeDir_HostBindings(rf, ctx) { … @@ -1216,7 +1218,6 @@ describe('compiler compliance: bindings', () => { `; expectEmit(result.source, template, 'Incorrect template'); }); - }); describe('non bindable behavior', () => { @@ -1420,7 +1421,5 @@ describe('compiler compliance: bindings', () => { const result = compile(files, angularFiles); expectEmit(result.source, template, 'Incorrect handling of elements with no children'); }); - }); - }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_di_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_di_spec.ts index 935d4e447af6a..91f70e9d03c87 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_di_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_di_spec.ts @@ -386,8 +386,7 @@ describe('compiler compliance: dependency injection', () => { expectEmit(source, MyPipeDefs, 'Invalid pipe factory function'); expectEmit(source, MyOtherPipeDefs, 'Invalid pipe factory function'); - expect(source.match(/MyPipe\.ɵfac =/g) !.length).toBe(1); - expect(source.match(/MyOtherPipe\.ɵfac =/g) !.length).toBe(1); + expect(source.match(/MyPipe\.ɵfac =/g)!.length).toBe(1); + expect(source.match(/MyOtherPipe\.ɵfac =/g)!.length).toBe(1); }); - }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts index 918ef6701ed61..b71ff345c949b 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts @@ -10,7 +10,6 @@ import {setup} from '@angular/compiler/test/aot/test_util'; import {compile, expectEmit} from './mock_compile'; describe('compiler compliance: directives', () => { - const angularFiles = setup({ compileAngular: false, compileAnimations: false, @@ -18,7 +17,6 @@ describe('compiler compliance: directives', () => { }); describe('matching', () => { - it('should not match directives on i18n attribute', () => { const files = { app: { @@ -114,7 +112,6 @@ describe('compiler compliance: directives', () => { }); it('should match directives on element bindings', () => { - const files = { app: { 'spec.ts': ` @@ -273,7 +270,6 @@ describe('compiler compliance: directives', () => { }); it('should match directives on ng-template bindings', () => { - const files = { app: { 'spec.ts': ` @@ -321,7 +317,6 @@ describe('compiler compliance: directives', () => { }); it('should match structural directives', () => { - const files = { app: { 'spec.ts': ` @@ -362,11 +357,9 @@ describe('compiler compliance: directives', () => { const source = result.source; expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp'); - }); it('should match directives on element outputs', () => { - const files = { app: { 'spec.ts': ` @@ -413,6 +406,5 @@ describe('compiler compliance: directives', () => { expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ɵcmp'); }); - }); }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts index 935859f2dfad6..d193d489705cf 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts @@ -58,7 +58,9 @@ const verifyTranslationIds = }); const regexp = /const\s*MSG_EXTERNAL_(.+?)\s*=\s*goog\.getMsg/g; const ids = extract(output, regexp, v => v[1]); - ids.forEach(id => { generatedIds.add(id.split('$$')[0]); }); + ids.forEach(id => { + generatedIds.add(id.split('$$')[0]); + }); const delta = diff(extractedIds, generatedIds); if (delta.size) { // check if we have ids in exception list @@ -179,7 +181,6 @@ const verify = (input: string, output: string, extra: any = {}): void => { }; describe('i18n support in the template compiler', () => { - describe('element attributes', () => { it('should add the meaning and description as JsDoc comments and metadata blocks', () => { const input = ` @@ -193,7 +194,7 @@ describe('i18n support in the template compiler', () => { <div i18n="Some text \\' [BACKUP_MESSAGE_ID: xxx]">Content H</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** @@ -269,8 +270,8 @@ describe('i18n support in the template compiler', () => { if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** * @desc [BACKUP_$` + - String.raw `{MESSAGE}_ID:idH]` + - '`' + String.raw `desc + String.raw`{MESSAGE}_ID:idH]` + + '`' + String.raw`desc */ const $MSG_EXTERNAL_idG$$APP_SPEC_TS_24$ = goog.getMsg("Title G"); $I18N_23$ = $MSG_EXTERNAL_idG$$APP_SPEC_TS_24$; @@ -336,9 +337,7 @@ describe('i18n support in the template compiler', () => { <ng-template i18n-title title="Hello"></ng-template> `; - // TODO (FW-1942): update the code to avoid adding `title` attribute in plain form - // into the `consts` array on Component def. - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_6616505470450179563$$APP_SPEC_TS_1$ = goog.getMsg("Hello"); @@ -349,7 +348,7 @@ describe('i18n support in the template compiler', () => { } const $_c2$ = ["title", $I18N_0$]; … - consts: [["title", "Hello"]], + consts: [[${AttributeMarker.I18n}, "title"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", 0); @@ -366,9 +365,7 @@ describe('i18n support in the template compiler', () => { <ng-template *ngIf="visible" i18n-title title="Hello">Test</ng-template> `; - // TODO (FW-1942): update the code to avoid adding `title` attribute in plain form - // into the `consts` array on Component def. - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_6616505470450179563$$APP_SPEC_TS_1$ = goog.getMsg("Hello"); @@ -390,7 +387,7 @@ describe('i18n support in the template compiler', () => { } } … - consts: [[${AttributeMarker.Template}, "ngIf"], ["title", "Hello"]], + consts: [[${AttributeMarker.Template}, "ngIf"], [${AttributeMarker.I18n}, "title"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_0_Template, 2, 0, undefined, 0); @@ -409,7 +406,7 @@ describe('i18n support in the template compiler', () => { <ng-template i18n-title title="Hello {{ name }}"></ng-template> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_3771704108176831903$$APP_SPEC_TS_1$ = goog.getMsg("Hello {$interpolation}", { @@ -419,7 +416,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`Hello $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c2$ = ["title", $I18N_0$]; … @@ -444,7 +441,7 @@ describe('i18n support in the template compiler', () => { <ng-template *ngIf="true" i18n-title title="Hello {{ name }}"></ng-template> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_3771704108176831903$$APP_SPEC_TS__1$ = goog.getMsg("Hello {$interpolation}", { @@ -454,7 +451,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`Hello $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c2$ = ["title", $I18N_0$]; … @@ -531,7 +528,7 @@ describe('i18n support in the template compiler', () => { <div id="static" i18n-title="m|d" title="introduction"></div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** @@ -572,7 +569,7 @@ describe('i18n support in the template compiler', () => { ></div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_5526535577705876535$$APP_SPEC_TS_1$ = goog.getMsg("static text"); @@ -594,7 +591,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`:m|d:intro $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_3$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -609,7 +606,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_3$ = $localize \`:m1|d1:$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c1$ = [ "aria-roledescription", $I18N_1$, @@ -629,9 +626,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_6$ = $localize \`:m2|d2:$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + - String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: and again $` + - String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + + String.raw`{"\uFFFD1\uFFFD"}:INTERPOLATION_1: and again $` + + String.raw`{"\uFFFD2\uFFFD"}:INTERPOLATION_2:\`; } var $I18N_7$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -642,7 +639,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_7$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = [ "title", $I18N_6$, @@ -651,7 +648,10 @@ describe('i18n support in the template compiler', () => { … decls: 5, vars: 8, - consts: [["id", "dynamic-1", ${AttributeMarker.I18n}, "aria-roledescription", "title", "aria-label"], ["id", "dynamic-2", ${AttributeMarker.I18n}, "title", "aria-roledescription"]], + consts: [["id", "dynamic-1", ${ + AttributeMarker + .I18n}, "aria-roledescription", "title", "aria-label"], ["id", "dynamic-2", ${ + AttributeMarker.I18n}, "title", "aria-roledescription"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); @@ -680,7 +680,7 @@ describe('i18n support in the template compiler', () => { <div i18n-title="m|d" title="intro {% valueA | uppercase %}"></div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** @@ -694,7 +694,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`:m|d:intro $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = ["title", $I18N_1$]; … @@ -722,7 +722,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** @@ -736,7 +736,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`:m|d:different scope $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c2$ = ["title", $I18N_1$]; function MyComponent_div_0_Template(rf, ctx) { @@ -758,7 +758,8 @@ describe('i18n support in the template compiler', () => { … decls: 1, vars: 1, - consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${AttributeMarker.I18n}, "title"]], + consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${ + AttributeMarker.I18n}, "title"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", 0); @@ -777,7 +778,7 @@ describe('i18n support in the template compiler', () => { <div i18n-title title="{{valueA.getRawValue()?.getTitle()}} title"></div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_3462388422673575127$$APP_SPEC_TS_2$ = goog.getMsg("{$interpolation} title", { @@ -787,7 +788,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: title\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: title\`; } const $_c3$ = ["title", $I18N_1$]; … @@ -825,7 +826,7 @@ describe('i18n support in the template compiler', () => { ></div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_5526535577705876535$$APP_SPEC_TS_1$ = goog.getMsg("static text"); @@ -847,7 +848,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`:m|d:intro $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_3$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -862,7 +863,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_3$ = $localize \`:m1|d1:$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c1$ = [ "aria-roledescription", $I18N_1$, @@ -882,9 +883,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_6$ = $localize \`:m2|d2:$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + - String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: and again $` + - String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + + String.raw`{"\uFFFD1\uFFFD"}:INTERPOLATION_1: and again $` + + String.raw`{"\uFFFD2\uFFFD"}:INTERPOLATION_2:\`; } var $I18N_7$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -895,7 +896,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_7$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = [ "title", $I18N_6$, @@ -938,7 +939,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_2$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** @@ -952,7 +953,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`:m|d:different scope $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c4$ = ["title", $I18N_2$]; function MyComponent_div_0_Template(rf, ctx) { @@ -974,7 +975,8 @@ describe('i18n support in the template compiler', () => { … decls: 1, vars: 1, - consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${AttributeMarker.I18n}, "title"]], + consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${ + AttributeMarker.I18n}, "title"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", 0); @@ -993,7 +995,7 @@ describe('i18n support in the template compiler', () => { <div i18n i18n-title="m|d" title="Element title">Some content</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** @@ -1036,7 +1038,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_ID_WITH_INVALID_CHARS$$APP_SPEC_TS_1$ = goog.getMsg("Element title"); @@ -1075,7 +1077,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelement(0, "div"); @@ -1097,7 +1099,7 @@ describe('i18n support in the template compiler', () => { <div i18n>Some <!-- comments --> text</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$ = goog.getMsg("Some text"); @@ -1115,11 +1117,11 @@ describe('i18n support in the template compiler', () => { <div i18n>Some text 'with single quotes', "with double quotes", \`with backticks\` and without quotes.</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_4924931801512133405$$APP_SPEC_TS_0$ = goog.getMsg("Some text 'with single quotes', \"with double quotes\", ` + - '`with backticks`' + String.raw ` and without quotes."); + '`with backticks`' + String.raw` and without quotes."); $I18N_0$ = $MSG_EXTERNAL_4924931801512133405$$APP_SPEC_TS_0$; } else { @@ -1132,16 +1134,16 @@ describe('i18n support in the template compiler', () => { it('should handle interpolations wrapped in backticks', () => { const input = '<div i18n>`{{ count }}`</div>'; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$ = goog.getMsg("` + - '`{$interpolation}`' + String.raw `", { "interpolation": "\uFFFD0\uFFFD" }); + '`{$interpolation}`' + String.raw`", { "interpolation": "\uFFFD0\uFFFD" }); $I18N_0$ = $MSG_APP_SPEC_TS_1$; } else { $I18N_0$ = $localize \`\\\`$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\\\`\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\\\`\`; }`; verify(input, output); }); @@ -1155,7 +1157,7 @@ describe('i18n support in the template compiler', () => { <div i18n>My i18n block #3</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS_0$ = goog.getMsg("My i18n block #1"); @@ -1213,7 +1215,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7597881511811528589$$APP_SPEC_TS_0$ = goog.getMsg(" Named interpolation: {$phA} Named interpolation with spaces: {$phB} ", { @@ -1224,8 +1226,8 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` Named interpolation: $` + - String.raw `{"\uFFFD0\uFFFD"}:PH_A: Named interpolation with spaces: $` + - String.raw `{"\uFFFD1\uFFFD"}:PH_B: \`; + String.raw`{"\uFFFD0\uFFFD"}:PH_A: Named interpolation with spaces: $` + + String.raw`{"\uFFFD1\uFFFD"}:PH_B: \`; } … decls: 2, @@ -1252,7 +1254,7 @@ describe('i18n support in the template compiler', () => { <div i18n>{% valueA %}</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_6749967533321674787$$APP_SPEC_TS_0$ = goog.getMsg("{$interpolation}", { @@ -1262,7 +1264,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … template: function MyComponent_Template(rf, ctx) { @@ -1290,7 +1292,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$$APP_SPEC_TS_1$ = goog.getMsg(" {$interpolation} {$interpolation_1} {$interpolation_2} ", { @@ -1302,9 +1304,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + - String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + - String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2: \`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + + String.raw`{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + + String.raw`{"\uFFFD2\uFFFD"}:INTERPOLATION_2: \`; } … template: function MyComponent_Template(rf, ctx) { @@ -1333,7 +1335,7 @@ describe('i18n support in the template compiler', () => { <div i18n>My i18n block #{{ three + four + five }}</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_572579892698764378$$APP_SPEC_TS_0$ = goog.getMsg("My i18n block #{$interpolation}", { @@ -1343,7 +1345,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -1354,7 +1356,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_2$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -1365,7 +1367,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … decls: 7, @@ -1418,7 +1420,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7905233330103651696$$APP_SPEC_TS_0$ = goog.getMsg(" My i18n block #{$interpolation} {$startTagSpan}Plain text in nested element{$closeTagSpan}", { @@ -1430,9 +1432,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + - String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_SPAN:Plain text in nested element$` + - String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + + String.raw`{"\uFFFD#2\uFFFD"}:START_TAG_SPAN:Plain text in nested element$` + + String.raw`{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; } var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -1448,14 +1450,14 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \` My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + - String.raw `{"[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]"}:START_TAG_DIV:$` + - String.raw `{"[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]"}:START_TAG_DIV:$` + String.raw + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + + String.raw`{"[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]"}:START_TAG_DIV:$` + + String.raw`{"[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]"}:START_TAG_DIV:$` + String.raw `{"\uFFFD#8\uFFFD"}:START_TAG_SPAN: More bindings in more nested element: $` + - String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + - String.raw `{"\uFFFD/#8\uFFFD"}:CLOSE_TAG_SPAN:$` + - String.raw `{"[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]"}:CLOSE_TAG_DIV:$` + - String.raw `{"[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]"}:CLOSE_TAG_DIV:\`; + String.raw`{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + + String.raw`{"\uFFFD/#8\uFFFD"}:CLOSE_TAG_SPAN:$` + + String.raw`{"[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]"}:CLOSE_TAG_DIV:$` + + String.raw`{"[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]"}:CLOSE_TAG_DIV:\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$); … @@ -1509,7 +1511,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_2$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_4782264005467235841$$APP_SPEC_TS_3$ = goog.getMsg("Span title {$interpolation} and {$interpolation_1}", { @@ -1520,8 +1522,8 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`Span title $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + - String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + + String.raw`{"\uFFFD1\uFFFD"}:INTERPOLATION_1:\`; } const $_c4$ = ["title", $I18N_2$]; var $I18N_0$; @@ -1535,9 +1537,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` My i18n block #1 with value: $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + String.raw + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_SPAN: Plain text in nested element (block #1) $` + - String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; + String.raw`{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; } var $I18N_7$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -1548,7 +1550,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_7$ = $localize \`Span title $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c9$ = ["title", $I18N_7$]; var $I18N_6$; @@ -1562,9 +1564,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_6$ = $localize \` My i18n block #2 with value $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + String.raw + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + String.raw `{"\uFFFD#7\uFFFD"}:START_TAG_SPAN: Plain text in nested element (block #2) $` + - String.raw `{"\uFFFD/#7\uFFFD"}:CLOSE_TAG_SPAN:\`; + String.raw`{"\uFFFD/#7\uFFFD"}:CLOSE_TAG_SPAN:\`; } … decls: 9, @@ -1623,7 +1625,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7679414751795588050$$APP_SPEC_TS__1$ = goog.getMsg(" Some other content {$interpolation} {$startTagDiv} More nested levels with bindings {$interpolation_1} {$closeTagDiv}", { @@ -1636,10 +1638,10 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \` Some other content $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + - String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_DIV: More nested levels with bindings $` + - String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + - String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_DIV:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + + String.raw`{"\uFFFD#3\uFFFD"}:START_TAG_DIV: More nested levels with bindings $` + + String.raw`{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + + String.raw`{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_DIV:\`; } … function MyComponent_div_2_Template(rf, ctx) { @@ -1688,7 +1690,7 @@ describe('i18n support in the template compiler', () => { <img src="logo.png" i18n *ngIf="visible" i18n-title title="App logo #{{ id }}" /> `; - const output = String.raw ` + const output = String.raw` function MyComponent_img_1_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelement(0, "img", 0); @@ -1703,7 +1705,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`App logo #$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c4$ = ["title", $I18N_2$]; function MyComponent_img_2_Template(rf, ctx) { @@ -1721,7 +1723,11 @@ describe('i18n support in the template compiler', () => { … decls: 3, vars: 2, - consts: [["src", "logo.png"], ["src", "logo.png", ${AttributeMarker.Template}, "ngIf"], ["src", "logo.png", ${AttributeMarker.Bindings}, "title", ${AttributeMarker.Template}, "ngIf"], ["src", "logo.png", ${AttributeMarker.I18n}, "title"]], + consts: [["src", "logo.png"], ["src", "logo.png", ${ + AttributeMarker.Template}, "ngIf"], ["src", "logo.png", ${ + AttributeMarker.Bindings}, "title", ${ + AttributeMarker.Template}, "ngIf"], ["src", "logo.png", ${ + AttributeMarker.I18n}, "title"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelement(0, "img", 0); @@ -1765,7 +1771,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` function MyComponent_div_2_div_4_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵi18nStart(0, $I18N_0$, 2); @@ -1821,13 +1827,13 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $localize \` Some content $` + String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_DIV_2: Some other content $` + - String.raw `{"\uFFFD0:1\uFFFD"}:INTERPOLATION: $` + String.raw + String.raw`{"\uFFFD0:1\uFFFD"}:INTERPOLATION: $` + String.raw `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:START_TAG_DIV: More nested levels with bindings $` + - String.raw `{"\uFFFD1:1\uFFFD"}:INTERPOLATION_1: $` + String.raw + String.raw`{"\uFFFD1:1\uFFFD"}:INTERPOLATION_1: $` + String.raw `{"\uFFFD*4:2\uFFFD\uFFFD#1:2\uFFFD"}:START_TAG_DIV_1: Content inside sub-template $` + - String.raw `{"\uFFFD0:2\uFFFD"}:INTERPOLATION_2: $` + String.raw + String.raw`{"\uFFFD0:2\uFFFD"}:INTERPOLATION_2: $` + String.raw `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:START_TAG_DIV: Bottom level element $` + - String.raw `{"\uFFFD1:2\uFFFD"}:INTERPOLATION_3: $` + String.raw + String.raw`{"\uFFFD1:2\uFFFD"}:INTERPOLATION_3: $` + String.raw `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + String.raw `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + @@ -1837,9 +1843,9 @@ describe('i18n support in the template compiler', () => { `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + String.raw `{"\uFFFD*3:3\uFFFD\uFFFD#1:3\uFFFD"}:START_TAG_DIV_3: Some other content $` + - String.raw `{"\uFFFD0:3\uFFFD"}:INTERPOLATION_4: $` + String.raw + String.raw`{"\uFFFD0:3\uFFFD"}:INTERPOLATION_4: $` + String.raw `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:START_TAG_DIV: More nested levels with bindings $` + - String.raw `{"\uFFFD1:3\uFFFD"}:INTERPOLATION_5: $` + String.raw + String.raw`{"\uFFFD1:3\uFFFD"}:INTERPOLATION_5: $` + String.raw `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + String.raw `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:\`; @@ -1891,7 +1897,7 @@ describe('i18n support in the template compiler', () => { <div i18n *ngIf="visible">Some other content <span>{{ valueA }}</span></div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$ = goog.getMsg("Some other content {$startTagSpan}{$interpolation}{$closeTagSpan}", { @@ -1903,9 +1909,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`Some other content $` + - String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_SPAN:$` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:$` + - String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; + String.raw`{"\uFFFD#2\uFFFD"}:START_TAG_SPAN:$` + + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:$` + + String.raw`{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; } … function MyComponent_div_0_Template(rf, ctx) { @@ -1945,7 +1951,7 @@ describe('i18n support in the template compiler', () => { <div i18n (click)="onClick()">Hello</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_2$ = goog.getMsg("Hello"); @@ -1976,7 +1982,7 @@ describe('i18n support in the template compiler', () => { <div i18n>My i18n block #1</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS_0$ = goog.getMsg("My i18n block #1"); @@ -2003,7 +2009,7 @@ describe('i18n support in the template compiler', () => { <div i18n>{age, select, 10 {ten} 20 {twenty} other {other}}</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}"); @@ -2041,7 +2047,7 @@ describe('i18n support in the template compiler', () => { <ng-container i18n>My i18n block #2</ng-container> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_2413150872298537152$$APP_SPEC_TS_0$ = goog.getMsg("My i18n block #2"); @@ -2083,7 +2089,7 @@ describe('i18n support in the template compiler', () => { <span i18n style="padding: 10px;">Text #2</span> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_1$ = goog.getMsg("Text #1"); @@ -2103,7 +2109,8 @@ describe('i18n support in the template compiler', () => { … decls: 4, vars: 0, - consts: [[${AttributeMarker.Classes}, "myClass"], [${AttributeMarker.Styles}, "padding", "10px"]], + consts: [[${AttributeMarker.Classes}, "myClass"], [${ + AttributeMarker.Styles}, "padding", "10px"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "span", 0); @@ -2126,7 +2133,7 @@ describe('i18n support in the template compiler', () => { <ng-container i18n>Some content: {{ valueA | uppercase }}</ng-container> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS_0$ = goog.getMsg("Some content: {$interpolation}", { @@ -2136,7 +2143,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`Some content: $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … decls: 3, @@ -2164,7 +2171,7 @@ describe('i18n support in the template compiler', () => { <ng-template i18n>Some content: {{ valueA | uppercase }}</ng-template> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS__0$ = goog.getMsg("Some content: {$interpolation}", { @@ -2174,7 +2181,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`Some content: $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } function MyComponent_ng_template_0_Template(rf, ctx) { if (rf & 1) { @@ -2208,7 +2215,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_702706566400598764$$APP_SPEC_TS_0$ = goog.getMsg("{$startTagNgTemplate}Template content: {$interpolation}{$closeTagNgTemplate}{$startTagNgContainer}Container content: {$interpolation_1}{$closeTagNgContainer}", { @@ -2223,12 +2230,12 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`$` + - String.raw `{"\uFFFD*2:1\uFFFD"}:START_TAG_NG_TEMPLATE:Template content: $` + - String.raw `{"\uFFFD0:1\uFFFD"}:INTERPOLATION:$` + - String.raw `{"\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_NG_TEMPLATE:$` + - String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_NG_CONTAINER:Container content: $` + - String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION_1:$` + - String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; + String.raw`{"\uFFFD*2:1\uFFFD"}:START_TAG_NG_TEMPLATE:Template content: $` + + String.raw`{"\uFFFD0:1\uFFFD"}:INTERPOLATION:$` + + String.raw`{"\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_NG_TEMPLATE:$` + + String.raw`{"\uFFFD#3\uFFFD"}:START_TAG_NG_CONTAINER:Container content: $` + + String.raw`{"\uFFFD0\uFFFD"}:INTERPOLATION_1:$` + + String.raw`{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } function MyComponent_ng_template_2_Template(rf, ctx) { if (rf & 1) { @@ -2272,7 +2279,7 @@ describe('i18n support in the template compiler', () => { <ng-container>{age, select, 10 {ten} 20 {twenty} other {other}}</ng-container> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}"); @@ -2341,7 +2348,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` function MyComponent_ng_template_2_ng_template_2_ng_template_1_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵi18n(0, $I18N_0$, 3); @@ -2380,11 +2387,11 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $localize \`$` + String.raw `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:START_TAG_NG_TEMPLATE: Template A: $` + - String.raw `{"\uFFFD0:1\uFFFD"}:INTERPOLATION: $` + String.raw + String.raw`{"\uFFFD0:1\uFFFD"}:INTERPOLATION: $` + String.raw `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:START_TAG_NG_TEMPLATE: Template B: $` + - String.raw `{"\uFFFD0:2\uFFFD"}:INTERPOLATION_1: $` + String.raw + String.raw`{"\uFFFD0:2\uFFFD"}:INTERPOLATION_1: $` + String.raw `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:START_TAG_NG_TEMPLATE: Template C: $` + - String.raw `{"\uFFFD0:3\uFFFD"}:INTERPOLATION_2: $` + String.raw + String.raw`{"\uFFFD0:3\uFFFD"}:INTERPOLATION_2: $` + String.raw `{"[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]"}:CLOSE_TAG_NG_TEMPLATE:$` + String.raw `{"[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]"}:CLOSE_TAG_NG_TEMPLATE:$` + @@ -2429,7 +2436,7 @@ describe('i18n support in the template compiler', () => { <ng-template i18n>{age, select, 10 {ten} 20 {twenty} other {other}}</ng-template> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); @@ -2493,7 +2500,7 @@ describe('i18n support in the template compiler', () => { </ng-template> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_4891196282781544695$$APP_SPEC_TS_0$ = goog.getMsg("{$tagImg} is my logo #1 ", { @@ -2503,7 +2510,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`$` + - String.raw `{"\uFFFD#2\uFFFD\uFFFD/#2\uFFFD"}:TAG_IMG: is my logo #1 \`; + String.raw`{"\uFFFD#2\uFFFD\uFFFD/#2\uFFFD"}:TAG_IMG: is my logo #1 \`; } var $I18N_2$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { @@ -2514,7 +2521,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`$` + - String.raw `{"\uFFFD#1\uFFFD\uFFFD/#1\uFFFD"}:TAG_IMG: is my logo #2 \`; + String.raw`{"\uFFFD#1\uFFFD\uFFFD/#1\uFFFD"}:TAG_IMG: is my logo #2 \`; } function MyComponent_ng_template_3_Template(rf, ctx) { if (rf & 1) { @@ -2550,7 +2557,7 @@ describe('i18n support in the template compiler', () => { </ng-template> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_8537814667662432133$$APP_SPEC_TS__0$ = goog.getMsg(" Root content {$startTagNgContainer} Nested content {$closeTagNgContainer}", { @@ -2563,7 +2570,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $localize \` Root content $` + String.raw `{"\uFFFD*1:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_NG_CONTAINER: Nested content $` + - String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*1:1\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; + String.raw`{"\uFFFD/#1:1\uFFFD\uFFFD/*1:1\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } … `; @@ -2580,7 +2587,7 @@ describe('i18n support in the template compiler', () => { // TODO(FW-635): currently we generate unique consts for each i18n block even though it // might contain the same content. This should be optimized by translation statements caching, // that can be implemented in the future within FW-635. - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_6563391987554512024$$APP_SPEC_TS_0$ = goog.getMsg("Test"); @@ -2610,7 +2617,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$ = goog.getMsg(" Hello {$startTagNgContainer}there{$closeTagNgContainer}", { "startTagNgContainer": "\uFFFD#2\uFFFD", "closeTagNgContainer": "\uFFFD/#2\uFFFD" }); @@ -2618,8 +2625,8 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` Hello $` + - String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_NG_CONTAINER:there$` + - String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; + String.raw`{"\uFFFD#2\uFFFD"}:START_TAG_NG_CONTAINER:there$` + + String.raw`{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } … decls: 3, @@ -2646,7 +2653,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$ = goog.getMsg(" Hello {$startTagNgContainer}there {$startTagStrong}!{$closeTagStrong}{$closeTagNgContainer}", { "startTagNgContainer": "\uFFFD#2\uFFFD", "startTagStrong": "\uFFFD#3\uFFFD", "closeTagStrong": "\uFFFD/#3\uFFFD", "closeTagNgContainer": "\uFFFD/#2\uFFFD" }); @@ -2654,10 +2661,10 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` Hello $` + - String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_NG_CONTAINER:there $` + - String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_STRONG:!$` + - String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_STRONG:$` + - String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; + String.raw`{"\uFFFD#2\uFFFD"}:START_TAG_NG_CONTAINER:there $` + + String.raw`{"\uFFFD#3\uFFFD"}:START_TAG_STRONG:!$` + + String.raw`{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_STRONG:$` + + String.raw`{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } … decls: 4, @@ -2686,7 +2693,7 @@ describe('i18n support in the template compiler', () => { <ng-container *ngIf="someFlag" i18n>Content B</ng-container> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_3308216566145348998$$APP_SPEC_TS___2$ = goog.getMsg("Content A"); @@ -2747,7 +2754,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_963542717423364282$$APP_SPEC_TS_0$ = goog.getMsg("\n Some text\n {$startTagSpan}Text inside span{$closeTagSpan}\n ", { @@ -2760,8 +2767,8 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $localize \` Some text $` + - String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_SPAN:Text inside span$` + - String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_SPAN: + String.raw`{"\uFFFD#3\uFFFD"}:START_TAG_SPAN:Text inside span$` + + String.raw`{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_SPAN: \`; } … @@ -2788,7 +2795,7 @@ describe('i18n support in the template compiler', () => { <div i18n>{gender, select, male {male} female {female} other {other}}</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); @@ -2825,7 +2832,7 @@ describe('i18n support in the template compiler', () => { <div i18n>{gender, select, single {'single quotes'} double {"double quotes"} other {other}}</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_4166854826696768832$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, single {'single quotes'} double {\"double quotes\"} other {other}}"); @@ -2847,7 +2854,7 @@ describe('i18n support in the template compiler', () => { {age, select, 10 {ten} 20 {twenty} other {other}} `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}"); @@ -2887,7 +2894,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); @@ -2955,7 +2962,9 @@ describe('i18n support in the template compiler', () => { … decls: 4, vars: 3, - consts: [["title", "icu only", ${AttributeMarker.Template}, "ngIf"], ["title", "icu and text", ${AttributeMarker.Template}, "ngIf"], ["title", "icu only"], ["title", "icu and text"]], + consts: [["title", "icu only", ${ + AttributeMarker.Template}, "ngIf"], ["title", "icu and text", ${ + AttributeMarker.Template}, "ngIf"], ["title", "icu only"], ["title", "icu and text"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); @@ -2984,7 +2993,7 @@ describe('i18n support in the template compiler', () => { <div i18n>{age, select, 10 {ten} 20 {twenty} other {{% other %}}}</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_2949673783721159566$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} other {{INTERPOLATION}}}"); @@ -3024,7 +3033,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_2417296354340576868$$APP_SPEC_TS_1$ = goog.getMsg("{VAR_SELECT, select, male {male - {START_BOLD_TEXT}male{CLOSE_BOLD_TEXT}} female {female {START_BOLD_TEXT}female{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}{START_ITALIC_TEXT}other{CLOSE_ITALIC_TEXT}{CLOSE_TAG_DIV}}}"); @@ -3057,13 +3066,13 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{$I18N_1$}:ICU: $` + - String.raw `{"\uFFFD#2\uFFFD"}:START_BOLD_TEXT:Other content$` + - String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_BOLD_TEXT:$` + - String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_DIV:$` + - String.raw `{"\uFFFD#4\uFFFD"}:START_ITALIC_TEXT:Another content$` + - String.raw `{"\uFFFD/#4\uFFFD"}:CLOSE_ITALIC_TEXT:$` + - String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_DIV:\`; + String.raw`{$I18N_1$}:ICU: $` + + String.raw`{"\uFFFD#2\uFFFD"}:START_BOLD_TEXT:Other content$` + + String.raw`{"\uFFFD/#2\uFFFD"}:CLOSE_BOLD_TEXT:$` + + String.raw`{"\uFFFD#3\uFFFD"}:START_TAG_DIV:$` + + String.raw`{"\uFFFD#4\uFFFD"}:START_ITALIC_TEXT:Another content$` + + String.raw`{"\uFFFD/#4\uFFFD"}:CLOSE_ITALIC_TEXT:$` + + String.raw`{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_DIV:\`; } … decls: 5, @@ -3096,7 +3105,7 @@ describe('i18n support in the template compiler', () => { <div i18n>{gender, select, male {male of age: {{ ageA + ageB + ageC }}} female {female} other {other}}</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_6879461626778511059$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, male {male of age: {INTERPOLATION}} female {female} other {other}}"); @@ -3137,7 +3146,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); @@ -3170,7 +3179,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{$I18N_1$}:ICU: $` + String.raw `{$I18N_2$}:ICU_1: \`; + String.raw`{$I18N_1$}:ICU: $` + String.raw`{$I18N_2$}:ICU_1: \`; } … decls: 2, @@ -3205,7 +3214,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); @@ -3251,12 +3260,12 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + - String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_DIV: $` + - String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + String.raw + String.raw`{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + + String.raw`{"\uFFFD#2\uFFFD"}:START_TAG_DIV: $` + + String.raw`{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + String.raw `{"[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]"}:CLOSE_TAG_DIV:$` + - String.raw `{"\uFFFD*3:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_DIV_1: $` + - String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + String.raw + String.raw`{"\uFFFD*3:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_DIV_1: $` + + String.raw`{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + String.raw `{"[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]"}:CLOSE_TAG_DIV:\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { @@ -3314,7 +3323,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_343563413083115114$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT_1, select, male {male of age: {VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}} female {female} other {other}}"); @@ -3334,7 +3343,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{$I18N_1$}:ICU: \`; + String.raw`{$I18N_1$}:ICU: \`; } … decls: 2, vars: 2, @@ -3370,7 +3379,7 @@ describe('i18n support in the template compiler', () => { }</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_6870293071705078389$$APP_SPEC_TS_1$ = goog.getMsg("{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}} !} other {other - {INTERPOLATION}}}"); @@ -3414,7 +3423,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); @@ -3449,10 +3458,10 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{$I18N_1$}:ICU: $` + - String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_SPAN: $` + - String.raw `{$I18N_3$}:ICU_1: $` + - String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_SPAN:\`; + String.raw`{$I18N_1$}:ICU: $` + + String.raw`{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_SPAN: $` + + String.raw`{$I18N_3$}:ICU_1: $` + + String.raw`{"\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_SPAN:\`; } function MyComponent_span_2_Template(rf, ctx) { if (rf & 1) { @@ -3501,7 +3510,7 @@ describe('i18n support in the template compiler', () => { </div> `; - const output = String.raw ` + const output = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$ = goog.getMsg("{VAR_SELECT, select, male {male {INTERPOLATION}} female {female {INTERPOLATION_1}} other {other}}"); @@ -3539,10 +3548,10 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{I18N_1}:ICU: $` + - String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_SPAN: $` + - String.raw `{I18N_3}:ICU_1: $` + - String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_SPAN:\`; + String.raw`{I18N_1}:ICU: $` + + String.raw`{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_SPAN: $` + + String.raw`{I18N_3}:ICU_1: $` + + String.raw`{"\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_SPAN:\`; } function MyComponent_span_2_Template(rf, ctx) { if (rf & 1) { @@ -3592,7 +3601,7 @@ describe('i18n support in the template compiler', () => { }</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_6318060397235942326$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, male {male {PH_A}} female {female {PH_B}} other {other {PH_WITH_SPACES}}}"); @@ -3632,7 +3641,7 @@ describe('i18n support in the template compiler', () => { <div i18n="meaningA|descA@@idA">{count, select, 1 {one} other {more than one}}</div> `; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** @@ -3656,7 +3665,7 @@ describe('i18n support in the template compiler', () => { it('should add legacy message ids if `enableI18nLegacyMessageIdFormat` is true', () => { const input = `<div i18n>Some Message</div>`; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { … } else { @@ -3671,7 +3680,7 @@ describe('i18n support in the template compiler', () => { it('should add legacy message ids if `enableI18nLegacyMessageIdFormat` is undefined', () => { const input = `<div i18n>Some Message</div>`; - const output = String.raw ` + const output = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { … } else { diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_input_outputs_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_input_outputs_spec.ts index b77b35449eae3..5d435b8d66326 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_input_outputs_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_input_outputs_spec.ts @@ -85,5 +85,4 @@ describe('compiler compliance: listen()', () => { expectEmit(result.source, componentDef, 'Incorrect component definition'); expectEmit(result.source, directiveDef, 'Incorrect directive definition'); }); - }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts index 9534349ef4111..b43dcafd45f57 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts @@ -10,8 +10,8 @@ import {setup} from '@angular/compiler/test/aot/test_util'; import {compile, expectEmit} from './mock_compile'; /* These tests are codified version of the tests in compiler_canonical_spec.ts. Every - * test in compiler_canonical_spec.ts should have a corresponding test here. - */ + * test in compiler_canonical_spec.ts should have a corresponding test here. + */ describe('compiler compliance: listen()', () => { const angularFiles = setup({ compileAngular: false, @@ -292,7 +292,8 @@ describe('compiler compliance: listen()', () => { const template = ` … - consts: [[${AttributeMarker.Bindings}, "click", "change"], [${AttributeMarker.Bindings}, "update", "delete"]], + consts: [[${AttributeMarker.Bindings}, "click", "change"], [${ + AttributeMarker.Bindings}, "update", "delete"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); @@ -446,5 +447,4 @@ describe('compiler compliance: listen()', () => { const result = compile(files, angularFiles); expectEmit(result.source, template, 'Incorrect host bindings'); }); - }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts index ba798596054b1..685b1ef69c2cd 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts @@ -622,7 +622,6 @@ describe('compiler compliance: styling', () => { const result = compile(files, angularFiles); expect(result.source).not.toContain('styling'); }); - }); describe('[class]', () => { @@ -743,7 +742,8 @@ describe('compiler compliance: styling', () => { selectors:[["my-component"]], decls: 1, vars: 2, - consts: [[${AttributeMarker.Classes}, "foo", ${AttributeMarker.Styles}, "width", "100px"]], + consts: [[${AttributeMarker.Classes}, "foo", ${ + AttributeMarker.Styles}, "width", "100px"]], template: function MyComponent_Template(rf, $ctx$) { if (rf & 1) { $r3$.ɵɵelement(0, "div", 0); @@ -782,7 +782,6 @@ describe('compiler compliance: styling', () => { const result = compile(files, angularFiles); expect(result.source).not.toContain('styling'); }); - }); describe('[style] mixed with [class]', () => { @@ -1005,7 +1004,8 @@ describe('compiler compliance: styling', () => { }; const template = ` - hostAttrs: [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"], + hostAttrs: [${AttributeMarker.Classes}, "foo", "baz", ${ + AttributeMarker.Styles}, "width", "200px", "height", "500px"], hostVars: 8, hostBindings: function MyComponent_HostBindings(rf, ctx) { if (rf & 2) { @@ -1594,7 +1594,6 @@ describe('compiler compliance: styling', () => { expectEmit(result.source, template, 'Incorrect handling of interpolated style properties'); }); - }); describe('instruction chaining', () => { @@ -1981,7 +1980,8 @@ describe('compiler compliance: styling', () => { }; const template = ` - hostAttrs: ["title", "foo title", ${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"], + hostAttrs: ["title", "foo title", ${AttributeMarker.Classes}, "foo", "baz", ${ + AttributeMarker.Styles}, "width", "200px", "height", "500px"], hostVars: 6, hostBindings: function MyComponent_HostBindings(rf, ctx) { if (rf & 2) { diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts index c6e795d9ba3c0..4b103875262ca 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts @@ -103,7 +103,10 @@ describe('compiler compliance: template', () => { } } // ... - consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${AttributeMarker.Bindings}, "title", "click", ${AttributeMarker.Template}, "ngFor", "ngForOf"], [${AttributeMarker.Bindings}, "title", "click"]], + consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${ + AttributeMarker.Bindings}, "title", "click", ${ + AttributeMarker.Template}, "ngFor", "ngForOf"], [${ + AttributeMarker.Bindings}, "title", "click"]], template:function MyComponent_Template(rf, ctx){ if (rf & 1) { $i0$.ɵɵtemplate(0, MyComponent_ul_0_Template, 2, 1, "ul", 0); @@ -157,7 +160,8 @@ describe('compiler compliance: template', () => { } } // ... - consts: [[${AttributeMarker.Bindings}, "click", ${AttributeMarker.Template}, "ngFor", "ngForOf"], [${AttributeMarker.Bindings}, "click"]], + consts: [[${AttributeMarker.Bindings}, "click", ${ + AttributeMarker.Template}, "ngFor", "ngForOf"], [${AttributeMarker.Bindings}, "click"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 1, 0, "div", 0); @@ -329,7 +333,8 @@ describe('compiler compliance: template', () => { } // ... - consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${AttributeMarker.Template}, "ngIf"]], + consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${ + AttributeMarker.Template}, "ngIf"]], template:function MyComponent_Template(rf, ctx){ if (rf & 1) { $i0$.ɵɵtemplate(0, MyComponent_div_0_Template, 2, 1, "div", 0); @@ -472,7 +477,6 @@ describe('compiler compliance: template', () => { }); it('should support local refs on <ng-template>', () => { - const files = { app: { 'spec.ts': ` @@ -511,7 +515,6 @@ describe('compiler compliance: template', () => { }); it('should support directive outputs on <ng-template>', () => { - const files = { app: { 'spec.ts': ` @@ -545,7 +548,6 @@ describe('compiler compliance: template', () => { const result = compile(files, angularFiles); expectEmit(result.source, template, 'Incorrect template'); - }); it('should allow directive inputs as an interpolated prop on <ng-template>', () => { diff --git a/packages/compiler-cli/test/diagnostics/check_types_spec.ts b/packages/compiler-cli/test/diagnostics/check_types_spec.ts index de0810397ba85..d2155d93d0843 100644 --- a/packages/compiler-cli/test/diagnostics/check_types_spec.ts +++ b/packages/compiler-cli/test/diagnostics/check_types_spec.ts @@ -11,7 +11,8 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import * as ts from 'typescript'; -import {TestSupport, expectNoDiagnostics, setup} from '../test_support'; + +import {expectNoDiagnostics, setup, TestSupport} from '../test_support'; type MockFiles = { [fileName: string]: string @@ -47,19 +48,19 @@ describe('ng type checker', () => { } function reject( - message: string | RegExp, location: RegExp | null, files: MockFiles, + message: string|RegExp, location: RegExp|null, files: MockFiles, overrideOptions: ng.CompilerOptions = {}) { const diagnostics = compileAndCheck([QUICKSTART, files], overrideOptions); if (!diagnostics || !diagnostics.length) { throw new Error('Expected a diagnostic error message'); } else { - const matches: (d: ng.Diagnostic | ts.Diagnostic) => boolean = typeof message === 'string' ? + const matches: (d: ng.Diagnostic|ts.Diagnostic) => boolean = typeof message === 'string' ? d => ng.isNgDiagnostic(d)&& d.messageText == message : d => ng.isNgDiagnostic(d) && message.test(d.messageText); const matchingDiagnostics = diagnostics.filter(matches) as ng.Diagnostic[]; if (!matchingDiagnostics || !matchingDiagnostics.length) { - throw new Error( - `Expected a diagnostics matching ${message}, received\n ${diagnostics.map(d => d.messageText).join('\n ')}`); + throw new Error(`Expected a diagnostics matching ${message}, received\n ${ + diagnostics.map(d => d.messageText).join('\n ')}`); } if (location) { @@ -72,7 +73,9 @@ describe('ng type checker', () => { } } - it('should accept unmodified QuickStart', () => { accept(); }); + it('should accept unmodified QuickStart', () => { + accept(); + }); it('should accept unmodified QuickStart with tests for unused variables', () => { accept({}, { @@ -523,7 +526,7 @@ describe('ng type checker', () => { }; const r = - (message: string | RegExp, location: RegExp | null, files: MockFiles, + (message: string|RegExp, location: RegExp|null, files: MockFiles, options: ng.AngularCompilerOptions = {}) => { reject( message, location, {'src/app.component.ts': '', 'src/lib.ts': '', ...files}, @@ -712,16 +715,18 @@ describe('ng type checker', () => { }); function addTests(config: {fullTemplateTypeCheck: boolean}) { - function a(template: string) { accept({'src/app.component.html': template}, config); } + function a(template: string) { + accept({'src/app.component.html': template}, config); + } - function r(template: string, message: string | RegExp, location: string) { + function r(template: string, message: string|RegExp, location: string) { reject( message, new RegExp(`app\.component\.html\@${location}$`), {'src/app.component.html': template}, config); } function rejectOnlyWithFullTemplateTypeCheck( - template: string, message: string | RegExp, location: string) { + template: string, message: string|RegExp, location: string) { if (config.fullTemplateTypeCheck) { r(template, message, location); } else { @@ -732,23 +737,33 @@ describe('ng type checker', () => { it('should report an invalid field access', () => { r('<div>{{fame}}<div>', `Property 'fame' does not exist on type 'AppComponent'.`, '0:5'); }); - it('should reject a reference to a field of a nullable', - () => { r('<div>{{maybePerson.name}}</div>', `Object is possibly 'undefined'.`, '0:5'); }); - it('should accept a reference to a field of a nullable using using non-null-assert', - () => { a('{{maybePerson!.name}}'); }); - it('should accept a safe property access of a nullable person', - () => { a('{{maybePerson?.name}}'); }); + it('should reject a reference to a field of a nullable', () => { + r('<div>{{maybePerson.name}}</div>', `Object is possibly 'undefined'.`, '0:5'); + }); + it('should accept a reference to a field of a nullable using using non-null-assert', () => { + a('{{maybePerson!.name}}'); + }); + it('should accept a safe property access of a nullable person', () => { + a('{{maybePerson?.name}}'); + }); - it('should accept using a library pipe', () => { a('{{1 | libPipe}}'); }); - it('should accept using a library directive', - () => { a('<div libDir #libDir="libDir">{{libDir.name}}</div>'); }); + it('should accept using a library pipe', () => { + a('{{1 | libPipe}}'); + }); + it('should accept using a library directive', () => { + a('<div libDir #libDir="libDir">{{libDir.name}}</div>'); + }); - it('should accept a function call', () => { a('{{getName()}}'); }); + it('should accept a function call', () => { + a('{{getName()}}'); + }); it('should reject an invalid method', () => { r('<div>{{getFame()}}</div>', `Property 'getFame' does not exist on type 'AppComponent'. Did you mean 'getName'?`, '0:5'); }); - it('should accept a field access of a method result', () => { a('{{getPerson().name}}'); }); + it('should accept a field access of a method result', () => { + a('{{getPerson().name}}'); + }); it('should reject an invalid field reference of a method result', () => { r('<div>{{getPerson().fame}}</div>', `Property 'fame' does not exist on type 'Person'.`, '0:5'); @@ -756,10 +771,13 @@ describe('ng type checker', () => { it('should reject an access to a nullable field of a method result', () => { r('<div>{{getMaybePerson().name}}</div>', `Object is possibly 'undefined'.`, '0:5'); }); - it('should accept a nullable assert of a nullable field references of a method result', - () => { a('{{getMaybePerson()!.name}}'); }); + it('should accept a nullable assert of a nullable field references of a method result', () => { + a('{{getMaybePerson()!.name}}'); + }); it('should accept a safe property access of a nullable field reference of a method result', - () => { a('{{getMaybePerson()?.name}}'); }); + () => { + a('{{getMaybePerson()?.name}}'); + }); it('should report an invalid field access inside of an ng-template', () => { rejectOnlyWithFullTemplateTypeCheck( @@ -779,8 +797,9 @@ describe('ng type checker', () => { } describe('with lowered expressions', () => { - it('should not report lowered expressions as errors', - () => { expectNoDiagnostics({}, compileAndCheck([LOWERING_QUICKSTART])); }); + it('should not report lowered expressions as errors', () => { + expectNoDiagnostics({}, compileAndCheck([LOWERING_QUICKSTART])); + }); }); }); diff --git a/packages/compiler-cli/test/diagnostics/typescript_version_spec.ts b/packages/compiler-cli/test/diagnostics/typescript_version_spec.ts index 605b3e1ed79cf..3e817cced5539 100644 --- a/packages/compiler-cli/test/diagnostics/typescript_version_spec.ts +++ b/packages/compiler-cli/test/diagnostics/typescript_version_spec.ts @@ -18,8 +18,9 @@ describe('toNumbers', () => { }); describe('compareNumbers', () => { - - it('should handle empty arrays', () => { expect(compareNumbers([], [])).toEqual(0); }); + it('should handle empty arrays', () => { + expect(compareNumbers([], [])).toEqual(0); + }); it('should handle arrays of same length', () => { expect(compareNumbers([1], [3])).toEqual(-1); diff --git a/packages/compiler-cli/test/extract_i18n_spec.ts b/packages/compiler-cli/test/extract_i18n_spec.ts index f2d4aad0468dc..39569ac55472a 100644 --- a/packages/compiler-cli/test/extract_i18n_spec.ts +++ b/packages/compiler-cli/test/extract_i18n_spec.ts @@ -207,7 +207,9 @@ describe('extract_i18n command line', () => { beforeEach(() => { errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error); const support = setup(); - write = (fileName: string, content: string) => { support.write(fileName, content); }; + write = (fileName: string, content: string) => { + support.write(fileName, content); + }; basePath = support.basePath; outDir = path.join(basePath, 'built'); write('tsconfig-base.json', `{ diff --git a/packages/compiler-cli/test/helpers/src/mock_file_loading.ts b/packages/compiler-cli/test/helpers/src/mock_file_loading.ts index 8cfdf54f3ff73..baf20e7ed42c2 100644 --- a/packages/compiler-cli/test/helpers/src/mock_file_loading.ts +++ b/packages/compiler-cli/test/helpers/src/mock_file_loading.ts @@ -7,7 +7,7 @@ */ /// <reference types="node" /> -import {readFileSync, readdirSync, statSync} from 'fs'; +import {readdirSync, readFileSync, statSync} from 'fs'; import {resolve} from 'path'; import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '..'; diff --git a/packages/compiler-cli/test/helpers/src/runfile_helpers.ts b/packages/compiler-cli/test/helpers/src/runfile_helpers.ts index bb848881dd54f..72ad3c9d7b0bf 100644 --- a/packages/compiler-cli/test/helpers/src/runfile_helpers.ts +++ b/packages/compiler-cli/test/helpers/src/runfile_helpers.ts @@ -22,7 +22,7 @@ export function getAngularPackagesFromRunfiles() { const runfilesManifestPath = process.env.RUNFILES_MANIFEST_FILE; if (!runfilesManifestPath) { - const packageRunfilesDir = path.join(process.env.RUNFILES !, 'angular/packages'); + const packageRunfilesDir = path.join(process.env.RUNFILES!, 'angular/packages'); return fs.readdirSync(packageRunfilesDir) .map(name => ({name, pkgPath: path.join(packageRunfilesDir, name, 'npm_package/')})) diff --git a/packages/compiler-cli/test/metadata/bundler_spec.ts b/packages/compiler-cli/test/metadata/bundler_spec.ts index 8e147e1a98f13..7f8d297a1d8a8 100644 --- a/packages/compiler-cli/test/metadata/bundler_spec.ts +++ b/packages/compiler-cli/test/metadata/bundler_spec.ts @@ -15,7 +15,6 @@ import {ClassMetadata, MetadataEntry, MetadataGlobalReferenceExpression, ModuleM import {Directory, MockAotContext, MockCompilerHost} from '../mocks'; describe('compiler host adapter', () => { - it('should retrieve metadata for an explicit index relative path reference', () => { const context = new MockAotContext('.', SIMPLE_LIBRARY); const host = new MockCompilerHost(context); @@ -28,7 +27,7 @@ describe('compiler host adapter', () => { const metadata = adapter.getMetadataFor('./lib/src/two/index', '.'); expect(metadata).toBeDefined(); - expect(Object.keys(metadata !.metadata).sort()).toEqual([ + expect(Object.keys(metadata!.metadata).sort()).toEqual([ 'PrivateTwo', 'TWO_CLASSES', 'Two', @@ -48,7 +47,7 @@ describe('compiler host adapter', () => { const metadata = adapter.getMetadataFor('./lib/src/two', '.'); expect(metadata).toBeDefined(); - expect(Object.keys(metadata !.metadata).sort()).toEqual([ + expect(Object.keys(metadata!.metadata).sort()).toEqual([ 'PrivateTwo', 'TWO_CLASSES', 'Two', @@ -82,7 +81,7 @@ describe('compiler host adapter', () => { const metadata = adapter.getMetadataFor('./lib/src/index', '.'); expect(metadata).toBeDefined(); - expect(metadata !.exports !.map(e => e.export !) + expect(metadata!.exports!.map(e => e.export !) .reduce((prev, next) => prev.concat(next), []) .sort()) .toEqual([ @@ -127,13 +126,13 @@ describe('compiler host adapter', () => { const metadata = adapter.getMetadataFor('./lib', '.'); expect(metadata).toBeDefined(); - expect(Object.keys(metadata !.metadata).sort()).toEqual([ + expect(Object.keys(metadata!.metadata).sort()).toEqual([ 'ONE_CLASSES', 'One', 'OneMore', 'PrivateOne', ]); - expect(Array.isArray(metadata !.metadata !['ONE_CLASSES'])).toBeTruthy(); + expect(Array.isArray(metadata!.metadata!['ONE_CLASSES'])).toBeTruthy(); }); it('should look for non-declaration file when resolving metadata via a package.json "types" entry', @@ -180,19 +179,17 @@ describe('compiler host adapter', () => { const metadata = adapter.getMetadataFor('./lib', '.'); expect(metadata).toBeDefined(); - expect(Object.keys(metadata !.metadata).sort()).toEqual([ + expect(Object.keys(metadata!.metadata).sort()).toEqual([ 'ONE_CLASSES', 'One', 'OneMore', 'PrivateOne', ]); - expect(Array.isArray(metadata !.metadata !['ONE_CLASSES'])).toBeTruthy(); - + expect(Array.isArray(metadata!.metadata!['ONE_CLASSES'])).toBeTruthy(); }); }); describe('metadata bundler', () => { - it('should be able to bundle a simple library', () => { const host = new MockStringBundlerHost('/', SIMPLE_LIBRARY); const bundler = new MetadataBundler('/lib/index', undefined, host, 'prfx_'); @@ -203,9 +200,9 @@ describe('metadata bundler', () => { const originalOne = './src/one'; const originalTwo = './src/two/index'; - expect(Object.keys(result.metadata.origins !) + expect(Object.keys(result.metadata.origins!) .sort() - .map(name => ({name, value: result.metadata.origins ![name]}))) + .map(name => ({name, value: result.metadata.origins![name]}))) .toEqual([ {name: 'ONE_CLASSES', value: originalOne}, {name: 'One', value: originalOne}, {name: 'OneMore', value: originalOne}, {name: 'TWO_CLASSES', value: originalTwo}, @@ -239,7 +236,7 @@ describe('metadata bundler', () => { }); const bundler = new MetadataBundler('/lib/index', undefined, host); const bundledMetadata = bundler.getMetadataBundle().metadata; - const deepIndexMetadata = host.getMetadataFor('/lib/deep/index') !; + const deepIndexMetadata = host.getMetadataFor('/lib/deep/index')!; // The unbundled metadata should reference symbols using the relative module path. expect(deepIndexMetadata.metadata['MyClass']).toEqual(jasmine.objectContaining<MetadataEntry>({ @@ -419,7 +416,7 @@ describe('metadata bundler', () => { from: 'external_one' } ]); - expect(result.metadata.origins !['E']).toBeUndefined(); + expect(result.metadata.origins!['E']).toBeUndefined(); }); it('should be able to bundle a library with multiple unnamed re-exports', () => { @@ -456,7 +453,7 @@ describe('metadata bundler', () => { const bundler = new MetadataBundler('/public-api', undefined, host); const result = bundler.getMetadataBundle(); - const {A, A2, A3, B1, B2} = result.metadata.metadata as{ + const {A, A2, A3, B1, B2} = result.metadata.metadata as { A: ClassMetadata, A2: MetadataGlobalReferenceExpression, A3: ClassMetadata, diff --git a/packages/compiler-cli/test/metadata/collector_spec.ts b/packages/compiler-cli/test/metadata/collector_spec.ts index dffe560d32aaa..5b591e0beaf99 100644 --- a/packages/compiler-cli/test/metadata/collector_spec.ts +++ b/packages/compiler-cli/test/metadata/collector_spec.ts @@ -9,9 +9,9 @@ import * as ts from 'typescript'; import {MetadataCollector} from '../../src/metadata/collector'; -import {ClassMetadata, ConstructorMetadata, METADATA_VERSION, MetadataEntry, MetadataMap, MetadataSymbolicExpression, ModuleMetadata, isClassMetadata, isMetadataGlobalReferenceExpression} from '../../src/metadata/schema'; +import {ClassMetadata, ConstructorMetadata, isClassMetadata, isMetadataGlobalReferenceExpression, METADATA_VERSION, MetadataEntry, MetadataMap, MetadataSymbolicExpression, ModuleMetadata} from '../../src/metadata/schema'; -import {Directory, Host, expectValidSources} from './typescript.mocks'; +import {Directory, expectValidSources, Host} from './typescript.mocks'; describe('Collector', () => { const documentRegistry = ts.createDocumentRegistry(); @@ -40,20 +40,22 @@ describe('Collector', () => { 'interface-reference.ts', 'static-type-check-members.ts', ]); service = ts.createLanguageService(host, documentRegistry); - program = service.getProgram() !; + program = service.getProgram()!; collector = new MetadataCollector({quotedNames: true}); }); - it('should not have errors in test data', () => { expectValidSources(service, program); }); + it('should not have errors in test data', () => { + expectValidSources(service, program); + }); it('should return undefined for modules that have no metadata', () => { - const sourceFile = program.getSourceFile('app/empty.ts') !; + const sourceFile = program.getSourceFile('app/empty.ts')!; const metadata = collector.getMetadata(sourceFile); expect(metadata).toBeUndefined(); }); it('should treat all symbols of .d.ts files as exported', () => { - const sourceFile = program.getSourceFile('declarations.d.ts') !; + const sourceFile = program.getSourceFile('declarations.d.ts')!; const metadata = collector.getMetadata(sourceFile); expect(metadata).toEqual({ __symbolic: 'module', @@ -66,7 +68,7 @@ describe('Collector', () => { }); it('should return an interface reference for types', () => { - const sourceFile = program.getSourceFile('/exported-type.ts') !; + const sourceFile = program.getSourceFile('/exported-type.ts')!; const metadata = collector.getMetadata(sourceFile); expect(metadata).toEqual({ __symbolic: 'module', @@ -76,7 +78,7 @@ describe('Collector', () => { }); it('should return an interface reference for interfaces', () => { - const sourceFile = program.getSourceFile('app/hero.ts') !; + const sourceFile = program.getSourceFile('app/hero.ts')!; const metadata = collector.getMetadata(sourceFile); expect(metadata).toEqual({ __symbolic: 'module', @@ -86,13 +88,13 @@ describe('Collector', () => { }); it('should preserve module names from TypeScript sources', () => { - const sourceFile = program.getSourceFile('named-module.d.ts') !; + const sourceFile = program.getSourceFile('named-module.d.ts')!; const metadata = collector.getMetadata(sourceFile); - expect(metadata !['importAs']).toEqual('some-named-module'); + expect(metadata!['importAs']).toEqual('some-named-module'); }); it('should be able to collect a simple component\'s metadata', () => { - const sourceFile = program.getSourceFile('app/hero-detail.component.ts') !; + const sourceFile = program.getSourceFile('app/hero-detail.component.ts')!; const metadata = collector.getMetadata(sourceFile); expect(metadata).toEqual({ __symbolic: 'module', @@ -144,7 +146,7 @@ describe('Collector', () => { }); it('should be able to get a more complicated component\'s metadata', () => { - const sourceFile = program.getSourceFile('/app/app.component.ts') !; + const sourceFile = program.getSourceFile('/app/app.component.ts')!; const metadata = collector.getMetadata(sourceFile); expect(metadata).toEqual({ __symbolic: 'module', @@ -236,7 +238,7 @@ describe('Collector', () => { }); it('should return the values of exported variables', () => { - const sourceFile = program.getSourceFile('/app/mock-heroes.ts') !; + const sourceFile = program.getSourceFile('/app/mock-heroes.ts')!; const metadata = collector.getMetadata(sourceFile); expect(metadata).toEqual({ __symbolic: 'module', @@ -262,14 +264,14 @@ describe('Collector', () => { let casesMetadata: ModuleMetadata; beforeEach(() => { - casesFile = program.getSourceFile('/app/cases-data.ts') !; - casesMetadata = collector.getMetadata(casesFile) !; + casesFile = program.getSourceFile('/app/cases-data.ts')!; + casesMetadata = collector.getMetadata(casesFile)!; }); it('should provide any reference for an any ctor parameter type', () => { const casesAny = <ClassMetadata>casesMetadata.metadata['CaseAny']; expect(casesAny).toBeTruthy(); - const ctorData = casesAny.members !['__ctor__'] as ConstructorMetadata[]; + const ctorData = casesAny.members!['__ctor__'] as ConstructorMetadata[]; expect(ctorData).toEqual([{ __symbolic: 'constructor', parameters: [{__symbolic: 'reference', name: 'any'} as MetadataSymbolicExpression] @@ -300,7 +302,8 @@ describe('Collector', () => { it('should record references to parameterized types', () => { const casesForIn = <ClassMetadata>casesMetadata.metadata['NgFor']; expect(casesForIn).toEqual({ - __symbolic: 'class', decorators: [{ + __symbolic: 'class', + decorators: [{ __symbolic: 'call', expression: { __symbolic: 'reference', @@ -310,21 +313,21 @@ describe('Collector', () => { character: 7 } }], - members: { - __ctor__: [{ - __symbolic: 'constructor', - parameters: [{ - __symbolic: 'reference', - name: 'ClassReference', - arguments: [{__symbolic: 'reference', name: 'NgForRow'}] - }] - }] - } + members: { + __ctor__: [{ + __symbolic: 'constructor', + parameters: [{ + __symbolic: 'reference', + name: 'ClassReference', + arguments: [{__symbolic: 'reference', name: 'NgForRow'}] + }] + }] + } } as any as ClassMetadata); // TODO: Review use of `any` here (#19904) }); it('should report errors for destructured imports', () => { - const unsupported1 = program.getSourceFile('/unsupported-1.ts') !; + const unsupported1 = program.getSourceFile('/unsupported-1.ts')!; const metadata = collector.getMetadata(unsupported1); expect(metadata).toEqual({ __symbolic: 'module', @@ -340,11 +343,11 @@ describe('Collector', () => { }); it('should report an error for references to unexpected types', () => { - const unsupported1 = program.getSourceFile('/unsupported-2.ts') !; - const metadata = collector.getMetadata(unsupported1) !; + const unsupported1 = program.getSourceFile('/unsupported-2.ts')!; + const metadata = collector.getMetadata(unsupported1)!; const barClass = <ClassMetadata>metadata.metadata['Bar']; - const ctor = <ConstructorMetadata>barClass.members !['__ctor__'][0]; - const parameter = ctor.parameters ![0]; + const ctor = <ConstructorMetadata>barClass.members!['__ctor__'][0]; + const parameter = ctor.parameters![0]; expect(parameter).toEqual({ __symbolic: 'error', message: 'Reference to non-exported class', @@ -355,18 +358,19 @@ describe('Collector', () => { }); it('should be able to handle import star type references', () => { - const importStar = program.getSourceFile('/import-star.ts') !; - const metadata = collector.getMetadata(importStar) !; + const importStar = program.getSourceFile('/import-star.ts')!; + const metadata = collector.getMetadata(importStar)!; const someClass = <ClassMetadata>metadata.metadata['SomeClass']; - const ctor = <ConstructorMetadata>someClass.members !['__ctor__'][0]; + const ctor = <ConstructorMetadata>someClass.members!['__ctor__'][0]; const parameters = ctor.parameters; - expect(parameters).toEqual([{ - __symbolic: 'reference', module: 'angular2/common', name: 'NgFor', line: 6, character: 29 - } as MetadataSymbolicExpression]); + expect(parameters).toEqual([ + {__symbolic: 'reference', module: 'angular2/common', name: 'NgFor', line: 6, character: 29} as + MetadataSymbolicExpression + ]); }); it('should record all exported classes', () => { - const sourceFile = program.getSourceFile('/exported-classes.ts') !; + const sourceFile = program.getSourceFile('/exported-classes.ts')!; const metadata = collector.getMetadata(sourceFile); expect(metadata).toEqual({ __symbolic: 'module', @@ -380,7 +384,7 @@ describe('Collector', () => { }); it('should be able to record functions', () => { - const exportedFunctions = program.getSourceFile('/exported-functions.ts') !; + const exportedFunctions = program.getSourceFile('/exported-functions.ts')!; const metadata = collector.getMetadata(exportedFunctions); expect(metadata).toEqual({ __symbolic: 'module', @@ -440,26 +444,27 @@ describe('Collector', () => { }); it('should be able to handle import star type references', () => { - const importStar = program.getSourceFile('/import-star.ts') !; - const metadata = collector.getMetadata(importStar) !; + const importStar = program.getSourceFile('/import-star.ts')!; + const metadata = collector.getMetadata(importStar)!; const someClass = <ClassMetadata>metadata.metadata['SomeClass']; - const ctor = <ConstructorMetadata>someClass.members !['__ctor__'][0]; + const ctor = <ConstructorMetadata>someClass.members!['__ctor__'][0]; const parameters = ctor.parameters; - expect(parameters).toEqual([{ - __symbolic: 'reference', module: 'angular2/common', name: 'NgFor', line: 6, character: 29 - } as MetadataSymbolicExpression]); + expect(parameters).toEqual([ + {__symbolic: 'reference', module: 'angular2/common', name: 'NgFor', line: 6, character: 29} as + MetadataSymbolicExpression + ]); }); it('should be able to collect the value of an enum', () => { - const enumSource = program.getSourceFile('/exported-enum.ts') !; - const metadata = collector.getMetadata(enumSource) !; + const enumSource = program.getSourceFile('/exported-enum.ts')!; + const metadata = collector.getMetadata(enumSource)!; const someEnum: any = metadata.metadata['SomeEnum']; expect(someEnum).toEqual({A: 0, B: 1, C: 100, D: 101}); }); it('should ignore a non-export enum', () => { - const enumSource = program.getSourceFile('/private-enum.ts') !; - const metadata = collector.getMetadata(enumSource) !; + const enumSource = program.getSourceFile('/private-enum.ts')!; + const metadata = collector.getMetadata(enumSource)!; const publicEnum: any = metadata.metadata['PublicEnum']; const privateEnum: any = metadata.metadata['PrivateEnum']; expect(publicEnum).toEqual({a: 0, b: 1, c: 2}); @@ -467,8 +472,8 @@ describe('Collector', () => { }); it('should be able to collect enums initialized from consts', () => { - const enumSource = program.getSourceFile('/exported-enum.ts') !; - const metadata = collector.getMetadata(enumSource) !; + const enumSource = program.getSourceFile('/exported-enum.ts')!; + const metadata = collector.getMetadata(enumSource)!; const complexEnum: any = metadata.metadata['ComplexEnum']; expect(complexEnum).toEqual({ A: 0, @@ -486,8 +491,8 @@ describe('Collector', () => { }); it('should be able to collect a simple static method', () => { - const staticSource = program.getSourceFile('/static-method.ts') !; - const metadata = collector.getMetadata(staticSource) !; + const staticSource = program.getSourceFile('/static-method.ts')!; + const metadata = collector.getMetadata(staticSource)!; expect(metadata).toBeDefined(); const classData = <ClassMetadata>metadata.metadata['MyModule']; expect(classData).toBeDefined(); @@ -504,43 +509,45 @@ describe('Collector', () => { }); it('should be able to collect a call to a static method', () => { - const staticSource = program.getSourceFile('/static-method-call.ts') !; - const metadata = collector.getMetadata(staticSource) !; + const staticSource = program.getSourceFile('/static-method-call.ts')!; + const metadata = collector.getMetadata(staticSource)!; expect(metadata).toBeDefined(); const classData = <ClassMetadata>metadata.metadata['Foo']; expect(classData).toBeDefined(); - expect(classData.decorators).toEqual([{ - __symbolic: 'call', - expression: { - __symbolic: 'reference', - module: 'angular2/core', - name: 'Component', - line: 4, - character: 5 - }, - arguments: [{ - providers: { - __symbolic: 'call', - expression: { - __symbolic: 'select', + expect(classData.decorators).toEqual([ + { + __symbolic: 'call', + expression: { + __symbolic: 'reference', + module: 'angular2/core', + name: 'Component', + line: 4, + character: 5 + }, + arguments: [{ + providers: { + __symbolic: 'call', expression: { - __symbolic: 'reference', - module: './static-method', - name: 'MyModule', - line: 5, - character: 17 + __symbolic: 'select', + expression: { + __symbolic: 'reference', + module: './static-method', + name: 'MyModule', + line: 5, + character: 17 + }, + member: 'with' }, - member: 'with' - }, - arguments: ['a'] - } - }] - }] as any as MetadataSymbolicExpression[]); // TODO: Review use of `any` here (#19904) + arguments: ['a'] + } + }] + } + ] as any as MetadataSymbolicExpression[]); // TODO: Review use of `any` here (#19904) }); it('should be able to collect a static field', () => { - const staticSource = program.getSourceFile('/static-field.ts') !; - const metadata = collector.getMetadata(staticSource) !; + const staticSource = program.getSourceFile('/static-field.ts')!; + const metadata = collector.getMetadata(staticSource)!; expect(metadata).toBeDefined(); const classData = <ClassMetadata>metadata.metadata['MyModule']; expect(classData).toBeDefined(); @@ -548,8 +555,8 @@ describe('Collector', () => { }); it('should ignore static type check members without a value', () => { - const typeCheckMembers = program.getSourceFile('/static-type-check-members.ts') !; - const metadata = collector.getMetadata(typeCheckMembers) !; + const typeCheckMembers = program.getSourceFile('/static-type-check-members.ts')!; + const metadata = collector.getMetadata(typeCheckMembers)!; const classData = <ClassMetadata>metadata.metadata['MyDirective']; expect(classData.statics).toEqual({ foo: 'bar', @@ -560,42 +567,44 @@ describe('Collector', () => { }); it('should be able to collect a reference to a static field', () => { - const staticSource = program.getSourceFile('/static-field-reference.ts') !; - const metadata = collector.getMetadata(staticSource) !; + const staticSource = program.getSourceFile('/static-field-reference.ts')!; + const metadata = collector.getMetadata(staticSource)!; expect(metadata).toBeDefined(); const classData = <ClassMetadata>metadata.metadata['Foo']; expect(classData).toBeDefined(); - expect(classData.decorators).toEqual([{ - __symbolic: 'call', - expression: { - __symbolic: 'reference', - module: 'angular2/core', - name: 'Component', - line: 4, - character: 5 - }, - arguments: [{ - providers: [{ - provide: 'a', - useValue: { - __symbolic: 'select', - expression: { - __symbolic: 'reference', - module: './static-field', - name: 'MyModule', - line: 5, - character: 45 - }, - member: 'VALUE' - } + expect(classData.decorators).toEqual([ + { + __symbolic: 'call', + expression: { + __symbolic: 'reference', + module: 'angular2/core', + name: 'Component', + line: 4, + character: 5 + }, + arguments: [{ + providers: [{ + provide: 'a', + useValue: { + __symbolic: 'select', + expression: { + __symbolic: 'reference', + module: './static-field', + name: 'MyModule', + line: 5, + character: 45 + }, + member: 'VALUE' + } + }] }] - }] - }] as any as MetadataSymbolicExpression[]); // TODO: Review use of `any` here (#19904) + } + ] as any as MetadataSymbolicExpression[]); // TODO: Review use of `any` here (#19904) }); it('should be able to collect a method with a conditional expression', () => { - const source = program.getSourceFile('/static-method-with-if.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('/static-method-with-if.ts')!; + const metadata = collector.getMetadata(source)!; expect(metadata).toBeDefined(); const classData = <ClassMetadata>metadata.metadata['MyModule']; expect(classData).toBeDefined(); @@ -619,8 +628,8 @@ describe('Collector', () => { }); it('should be able to collect a method with a default parameter', () => { - const source = program.getSourceFile('/static-method-with-default.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('/static-method-with-default.ts')!; + const metadata = collector.getMetadata(source)!; expect(metadata).toBeDefined(); const classData = <ClassMetadata>metadata.metadata['MyModule']; expect(classData).toBeDefined(); @@ -648,8 +657,8 @@ describe('Collector', () => { }); it('should be able to collect re-exported symbols', () => { - const source = program.getSourceFile('/re-exports.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('/re-exports.ts')!; + const metadata = collector.getMetadata(source)!; expect(metadata.exports).toEqual([ {from: './static-field', export: ['MyModule']}, {from: './static-field-reference', export: [{name: 'Foo', as: 'OtherModule'}]}, @@ -658,14 +667,14 @@ describe('Collector', () => { }); it('should be able to collect a export as symbol', () => { - const source = program.getSourceFile('export-as.d.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('export-as.d.ts')!; + const metadata = collector.getMetadata(source)!; expect(metadata.metadata).toEqual({SomeFunction: {__symbolic: 'function'}}); }); it('should be able to collect exports with no module specifier', () => { - const source = program.getSourceFile('/re-exports-2.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('/re-exports-2.ts')!; + const metadata = collector.getMetadata(source)!; expect(metadata.metadata).toEqual({ MyClass: Object({__symbolic: 'class'}), OtherModule: { @@ -686,8 +695,8 @@ describe('Collector', () => { }); it('should collect an error symbol if collecting a reference to a non-exported symbol', () => { - const source = program.getSourceFile('/local-symbol-ref.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('/local-symbol-ref.ts')!; + const metadata = collector.getMetadata(source)!; expect(metadata.metadata).toEqual({ REQUIRED_VALIDATOR: { __symbolic: 'error', @@ -714,8 +723,8 @@ describe('Collector', () => { }); it('should collect an error symbol if collecting a reference to a non-exported function', () => { - const source = program.getSourceFile('/local-function-ref.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('/local-function-ref.ts')!; + const metadata = collector.getMetadata(source)!; expect(metadata.metadata).toEqual({ REQUIRED_VALIDATOR: { __symbolic: 'error', @@ -742,8 +751,8 @@ describe('Collector', () => { }); it('should collect an error for a simple function that references a local variable', () => { - const source = program.getSourceFile('/local-symbol-ref-func.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('/local-symbol-ref-func.ts')!; + const metadata = collector.getMetadata(source)!; expect(metadata.metadata).toEqual({ foo: { __symbolic: 'function', @@ -760,8 +769,8 @@ describe('Collector', () => { }); it('should collect any for interface parameter reference', () => { - const source = program.getSourceFile('/interface-reference.ts') !; - const metadata = collector.getMetadata(source) !; + const source = program.getSourceFile('/interface-reference.ts')!; + const metadata = collector.getMetadata(source)!; expect((metadata.metadata['SomeClass'] as ClassMetadata).members).toEqual({ __ctor__: [{ __symbolic: 'constructor', @@ -787,11 +796,13 @@ describe('Collector', () => { return expect(metadata.metadata['value']); } - it('should be able to collect a raw interpolated string', - () => { e('`simple value`').toBe('simple value'); }); + it('should be able to collect a raw interpolated string', () => { + e('`simple value`').toBe('simple value'); + }); - it('should be able to interpolate a single value', - () => { e('`${foo}`', 'const foo = "foo value"').toBe('foo value'); }); + it('should be able to interpolate a single value', () => { + e('`${foo}`', 'const foo = "foo value"').toBe('foo value'); + }); it('should be able to interpolate multiple values', () => { e('`foo:${foo}, bar:${bar}, end`', 'const foo = "foo"; const bar = "bar";') @@ -894,30 +905,30 @@ describe('Collector', () => { toString(): string { return \`InjectionToken \${this._desc}\`; } } as any;`, ts.ScriptTarget.Latest, true); - const metadata = collector.getMetadata(source) !; + const metadata = collector.getMetadata(source)!; expect(metadata.metadata).toEqual({InjectionToken: {__symbolic: 'class'}}); }); describe('in strict mode', () => { it('should throw if an error symbol is collecting a reference to a non-exported symbol', () => { - const source = program.getSourceFile('/local-symbol-ref.ts') !; + const source = program.getSourceFile('/local-symbol-ref.ts')!; expect(() => collector.getMetadata(source, true)).toThrowError(/Reference to a local symbol/); }); it('should throw if an error if collecting a reference to a non-exported function', () => { - const source = program.getSourceFile('/local-function-ref.ts') !; + const source = program.getSourceFile('/local-function-ref.ts')!; expect(() => collector.getMetadata(source, true)) .toThrowError(/Reference to a non-exported function/); }); it('should throw for references to unexpected types', () => { - const unsupported2 = program.getSourceFile('/unsupported-2.ts') !; + const unsupported2 = program.getSourceFile('/unsupported-2.ts')!; expect(() => collector.getMetadata(unsupported2, true)) .toThrowError(/Reference to non-exported class/); }); it('should throw for errors in a static method', () => { - const unsupported3 = program.getSourceFile('/unsupported-3.ts') !; + const unsupported3 = program.getSourceFile('/unsupported-3.ts')!; expect(() => collector.getMetadata(unsupported3, true)) .toThrowError(/Reference to a non-exported class/); }); @@ -927,34 +938,39 @@ describe('Collector', () => { it('should not throw with a class with no name', () => { const fileName = '/invalid-class.ts'; override(fileName, 'export class'); - const invalidClass = program.getSourceFile(fileName) !; + const invalidClass = program.getSourceFile(fileName)!; expect(() => collector.getMetadata(invalidClass)).not.toThrow(); }); it('should not throw with a function with no name', () => { const fileName = '/invalid-function.ts'; override(fileName, 'export function'); - const invalidFunction = program.getSourceFile(fileName) !; + const invalidFunction = program.getSourceFile(fileName)!; expect(() => collector.getMetadata(invalidFunction)).not.toThrow(); }); }); - describe('inheritance', () => { - it('should record `extends` clauses for declared classes', () => { - const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts') !) !; - expect(metadata.metadata['DeclaredChildClass']) + describe( + 'inheritance', () => { + it('should record `extends` clauses for declared classes', + () => { + const metadata = + collector.getMetadata(program.getSourceFile('/class-inheritance.ts')!)!; + expect(metadata.metadata['DeclaredChildClass']) .toEqual({__symbolic: 'class', extends: {__symbolic: 'reference', name: 'ParentClass'}}); - }); + }); - it('should record `extends` clauses for classes in the same file', () => { - const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts') !) !; - expect(metadata.metadata['ChildClassSameFile']) + it('should record `extends` clauses for classes in the same file', + () => { + const metadata = + collector.getMetadata(program.getSourceFile('/class-inheritance.ts')!)!; + expect(metadata.metadata['ChildClassSameFile']) .toEqual({__symbolic: 'class', extends: {__symbolic: 'reference', name: 'ParentClass'}}); - }); + }); - it('should record `extends` clauses for classes in a different file', () => { - const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts') !) !; - expect(metadata.metadata['ChildClassOtherFile']).toEqual({ + it('should record `extends` clauses for classes in a different file', () => { + const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts')!)!; + expect(metadata.metadata['ChildClassOtherFile']).toEqual({ __symbolic: 'class', extends: { __symbolic: 'reference', @@ -964,29 +980,29 @@ describe('Collector', () => { character: 45, } }); - }); + }); - function expectClass(entry: MetadataEntry): entry is ClassMetadata { - const result = isClassMetadata(entry); - expect(result).toBeTruthy(); - return result; - } + function expectClass(entry: MetadataEntry): entry is ClassMetadata { + const result = isClassMetadata(entry); + expect(result).toBeTruthy(); + return result; + } - it('should collect the correct arity for a class', () => { - const metadata = collector.getMetadata(program.getSourceFile('/class-arity.ts') !) !; - - const zero = metadata.metadata['Zero']; - if (expectClass(zero)) expect(zero.arity).toBeUndefined(); - const one = metadata.metadata['One']; - if (expectClass(one)) expect(one.arity).toBe(1); - const two = metadata.metadata['Two']; - if (expectClass(two)) expect(two.arity).toBe(2); - const three = metadata.metadata['Three']; - if (expectClass(three)) expect(three.arity).toBe(3); - const nine = metadata.metadata['Nine']; - if (expectClass(nine)) expect(nine.arity).toBe(9); - }); - }); + it('should collect the correct arity for a class', () => { + const metadata = collector.getMetadata(program.getSourceFile('/class-arity.ts')!)!; + + const zero = metadata.metadata['Zero']; + if (expectClass(zero)) expect(zero.arity).toBeUndefined(); + const one = metadata.metadata['One']; + if (expectClass(one)) expect(one.arity).toBe(1); + const two = metadata.metadata['Two']; + if (expectClass(two)) expect(two.arity).toBe(2); + const three = metadata.metadata['Three']; + if (expectClass(three)) expect(three.arity).toBe(3); + const nine = metadata.metadata['Nine']; + if (expectClass(nine)) expect(nine.arity).toBe(9); + }); + }); describe('regression', () => { it('should be able to collect a short-hand property value', () => { @@ -1053,11 +1069,12 @@ describe('Collector', () => { expect((metadata.metadata.MyIf as any).statics.typeGuard) .not.toBeUndefined('typeGuard was not collected'); }); - }); describe('references', () => { - beforeEach(() => { collector = new MetadataCollector({quotedNames: true}); }); + beforeEach(() => { + collector = new MetadataCollector({quotedNames: true}); + }); it('should record a reference to an exported field of a useValue', () => { const metadata = collectSource(` @@ -1113,13 +1130,13 @@ describe('Collector', () => { } return value; }); - expect(metadata !.metadata['a']).toEqual({__symbolic: 'reference', name: lambdaTemp}); + expect(metadata!.metadata['a']).toEqual({__symbolic: 'reference', name: lambdaTemp}); }); it('should compose substitution functions', () => { const collector = new MetadataCollector({ - substituteExpression: (value, node) => isMetadataGlobalReferenceExpression(value) && - value.name == lambdaTemp ? + substituteExpression: (value, node) => + isMetadataGlobalReferenceExpression(value) && value.name == lambdaTemp ? {__symbolic: 'reference', name: value.name + '2'} : value }); @@ -1133,19 +1150,19 @@ describe('Collector', () => { } return value; }); - expect(metadata !.metadata['a']).toEqual({__symbolic: 'reference', name: lambdaTemp + '2'}); + expect(metadata!.metadata['a']).toEqual({__symbolic: 'reference', name: lambdaTemp + '2'}); }); }); function override(fileName: string, content: string) { host.overrideFile(fileName, content); host.addFile(fileName); - program = service.getProgram() !; + program = service.getProgram()!; } function collectSource(content: string): ModuleMetadata { const sourceFile = createSource(content); - return collector.getMetadata(sourceFile) !; + return collector.getMetadata(sourceFile)!; } }); diff --git a/packages/compiler-cli/test/metadata/evaluator_spec.ts b/packages/compiler-cli/test/metadata/evaluator_spec.ts index 6bd8f5167a0af..405ff7418957d 100644 --- a/packages/compiler-cli/test/metadata/evaluator_spec.ts +++ b/packages/compiler-cli/test/metadata/evaluator_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {Evaluator} from '../../src/metadata/evaluator'; import {Symbols} from '../../src/metadata/symbols'; -import {Directory, Host, expectNoDiagnostics, findVar, findVarInitializer} from './typescript.mocks'; +import {Directory, expectNoDiagnostics, findVar, findVarInitializer, Host} from './typescript.mocks'; describe('Evaluator', () => { const documentRegistry = ts.createDocumentRegistry(); @@ -27,7 +27,7 @@ describe('Evaluator', () => { 'newExpression.ts', 'errors.ts', 'declared.ts' ]); service = ts.createLanguageService(host, documentRegistry); - program = service.getProgram() !; + program = service.getProgram()!; typeChecker = program.getTypeChecker(); symbols = new Symbols(null as any as ts.SourceFile); evaluator = new Evaluator(symbols, new Map()); @@ -45,7 +45,7 @@ describe('Evaluator', () => { }); it('should be able to fold literal expressions', () => { - const consts = program.getSourceFile('consts.ts') !; + const consts = program.getSourceFile('consts.ts')!; expect(evaluator.isFoldable(findVarInitializer(consts, 'someName'))).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(consts, 'someBool'))).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(consts, 'one'))).toBeTruthy(); @@ -53,7 +53,7 @@ describe('Evaluator', () => { }); it('should be able to fold expressions with foldable references', () => { - const expressions = program.getSourceFile('expressions.ts') !; + const expressions = program.getSourceFile('expressions.ts')!; symbols.define('someName', 'some-name'); symbols.define('someBool', true); symbols.define('one', 1); @@ -67,7 +67,7 @@ describe('Evaluator', () => { }); it('should be able to evaluate literal expressions', () => { - const consts = program.getSourceFile('consts.ts') !; + const consts = program.getSourceFile('consts.ts')!; expect(evaluator.evaluateNode(findVarInitializer(consts, 'someName'))).toBe('some-name'); expect(evaluator.evaluateNode(findVarInitializer(consts, 'someBool'))).toBe(true); expect(evaluator.evaluateNode(findVarInitializer(consts, 'one'))).toBe(1); @@ -75,7 +75,7 @@ describe('Evaluator', () => { }); it('should be able to evaluate expressions', () => { - const expressions = program.getSourceFile('expressions.ts') !; + const expressions = program.getSourceFile('expressions.ts')!; symbols.define('someName', 'some-name'); symbols.define('someBool', true); symbols.define('one', 1); @@ -118,11 +118,10 @@ describe('Evaluator', () => { expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftRight'))).toEqual(-1 >> 2); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftRightU'))) .toEqual(-1 >>> 2); - }); it('should report recursive references as symbolic', () => { - const expressions = program.getSourceFile('expressions.ts') !; + const expressions = program.getSourceFile('expressions.ts')!; expect(evaluator.evaluateNode(findVarInitializer(expressions, 'recursiveA'))) .toEqual({__symbolic: 'reference', name: 'recursiveB'}); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'recursiveB'))) @@ -130,13 +129,13 @@ describe('Evaluator', () => { }); it('should correctly handle special cases for CONST_EXPR', () => { - const const_expr = program.getSourceFile('const_expr.ts') !; + const const_expr = program.getSourceFile('const_expr.ts')!; expect(evaluator.evaluateNode(findVarInitializer(const_expr, 'bTrue'))).toEqual(true); expect(evaluator.evaluateNode(findVarInitializer(const_expr, 'bFalse'))).toEqual(false); }); it('should resolve a forwardRef', () => { - const forwardRef = program.getSourceFile('forwardRef.ts') !; + const forwardRef = program.getSourceFile('forwardRef.ts')!; expect(evaluator.evaluateNode(findVarInitializer(forwardRef, 'bTrue'))).toEqual(true); expect(evaluator.evaluateNode(findVarInitializer(forwardRef, 'bFalse'))).toEqual(false); }); @@ -144,7 +143,7 @@ describe('Evaluator', () => { it('should return new expressions', () => { symbols.define('Value', {__symbolic: 'reference', module: './classes', name: 'Value'}); evaluator = new Evaluator(symbols, new Map()); - const newExpression = program.getSourceFile('newExpression.ts') !; + const newExpression = program.getSourceFile('newExpression.ts')!; expect(evaluator.evaluateNode(findVarInitializer(newExpression, 'someValue'))).toEqual({ __symbolic: 'new', expression: @@ -160,9 +159,9 @@ describe('Evaluator', () => { }); it('should support reference to a declared module type', () => { - const declared = program.getSourceFile('declared.ts') !; - const aDecl = findVar(declared, 'a') !; - expect(evaluator.evaluateNode(aDecl.type !)).toEqual({ + const declared = program.getSourceFile('declared.ts')!; + const aDecl = findVar(declared, 'a')!; + expect(evaluator.evaluateNode(aDecl.type!)).toEqual({ __symbolic: 'select', expression: {__symbolic: 'reference', name: 'Foo'}, member: 'A' @@ -170,28 +169,28 @@ describe('Evaluator', () => { }); it('should return errors for unsupported expressions', () => { - const errors = program.getSourceFile('errors.ts') !; - const fDecl = findVar(errors, 'f') !; - expect(evaluator.evaluateNode(fDecl.initializer !)) + const errors = program.getSourceFile('errors.ts')!; + const fDecl = findVar(errors, 'f')!; + expect(evaluator.evaluateNode(fDecl.initializer!)) .toEqual({__symbolic: 'error', message: 'Lambda not supported', line: 1, character: 12}); - const eDecl = findVar(errors, 'e') !; - expect(evaluator.evaluateNode(eDecl.type !)).toEqual({ + const eDecl = findVar(errors, 'e')!; + expect(evaluator.evaluateNode(eDecl.type!)).toEqual({ __symbolic: 'error', message: 'Could not resolve type', line: 2, character: 11, context: {typeName: 'NotFound'} }); - const sDecl = findVar(errors, 's') !; - expect(evaluator.evaluateNode(sDecl.initializer !)).toEqual({ + const sDecl = findVar(errors, 's')!; + expect(evaluator.evaluateNode(sDecl.initializer!)).toEqual({ __symbolic: 'error', message: 'Name expected', line: 3, character: 14, context: {received: '1'} }); - const tDecl = findVar(errors, 't') !; - expect(evaluator.evaluateNode(tDecl.initializer !)).toEqual({ + const tDecl = findVar(errors, 't')!; + expect(evaluator.evaluateNode(tDecl.initializer!)).toEqual({ __symbolic: 'error', message: 'Expression form not supported', line: 4, @@ -200,16 +199,16 @@ describe('Evaluator', () => { }); it('should be able to fold an array spread', () => { - const expressions = program.getSourceFile('expressions.ts') !; + const expressions = program.getSourceFile('expressions.ts')!; symbols.define('arr', [1, 2, 3, 4]); - const arrSpread = findVar(expressions, 'arrSpread') !; - expect(evaluator.evaluateNode(arrSpread.initializer !)).toEqual([0, 1, 2, 3, 4, 5]); + const arrSpread = findVar(expressions, 'arrSpread')!; + expect(evaluator.evaluateNode(arrSpread.initializer!)).toEqual([0, 1, 2, 3, 4, 5]); }); it('should be able to produce a spread expression', () => { - const expressions = program.getSourceFile('expressions.ts') !; - const arrSpreadRef = findVar(expressions, 'arrSpreadRef') !; - expect(evaluator.evaluateNode(arrSpreadRef.initializer !)).toEqual([ + const expressions = program.getSourceFile('expressions.ts')!; + const arrSpreadRef = findVar(expressions, 'arrSpreadRef')!; + expect(evaluator.evaluateNode(arrSpreadRef.initializer!)).toEqual([ 0, {__symbolic: 'spread', expression: {__symbolic: 'reference', name: 'arrImport'}}, 5 ]); }); @@ -218,8 +217,8 @@ describe('Evaluator', () => { const source = sourceFileOf(` export var a = new f; `); - const expr = findVar(source, 'a') !; - expect(evaluator.evaluateNode(expr.initializer !)) + const expr = findVar(source, 'a')!; + expect(evaluator.evaluateNode(expr.initializer!)) .toEqual({__symbolic: 'new', expression: {__symbolic: 'reference', name: 'f'}}); }); @@ -244,7 +243,7 @@ describe('Evaluator', () => { export var a = () => b; `); const expr = findVar(source, 'a'); - expect(evaluator.evaluateNode(expr !.initializer !)) + expect(evaluator.evaluateNode(expr!.initializer!)) .toEqual({__symbolic: 'reference', name: lambdaTemp}); }); @@ -256,7 +255,7 @@ describe('Evaluator', () => { ]; `); const expr = findVar(source, 'a'); - expect(evaluator.evaluateNode(expr !.initializer !)).toEqual([ + expect(evaluator.evaluateNode(expr!.initializer!)).toEqual([ {provide: 'someValue', useFactory: {__symbolic: 'reference', name: lambdaTemp}} ]); }); diff --git a/packages/compiler-cli/test/metadata/symbols_spec.ts b/packages/compiler-cli/test/metadata/symbols_spec.ts index 8ea2e70edb673..37c5a693fdb45 100644 --- a/packages/compiler-cli/test/metadata/symbols_spec.ts +++ b/packages/compiler-cli/test/metadata/symbols_spec.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {isMetadataGlobalReferenceExpression} from '../../src/metadata/schema'; import {Symbols} from '../../src/metadata/symbols'; -import {Directory, Host, expectNoDiagnostics} from './typescript.mocks'; +import {Directory, expectNoDiagnostics, Host} from './typescript.mocks'; describe('Symbols', () => { let symbols: Symbols; @@ -42,9 +42,9 @@ describe('Symbols', () => { beforeEach(() => { host = new Host(FILES, ['consts.ts', 'expressions.ts', 'imports.ts']); service = ts.createLanguageService(host); - program = service.getProgram() !; - expressions = program.getSourceFile('expressions.ts') !; - imports = program.getSourceFile('imports.ts') !; + program = service.getProgram()!; + expressions = program.getSourceFile('expressions.ts')!; + imports = program.getSourceFile('imports.ts')!; }); it('should not have syntax errors in the test sources', () => { @@ -111,7 +111,7 @@ describe('Symbols', () => { } return false; }; - ts.forEachChild(core !, visit); + ts.forEachChild(core!, visit); }); }); diff --git a/packages/compiler-cli/test/metadata/typescript.mocks.ts b/packages/compiler-cli/test/metadata/typescript.mocks.ts index 44d3bb39fbd14..71d214406d2d0 100644 --- a/packages/compiler-cli/test/metadata/typescript.mocks.ts +++ b/packages/compiler-cli/test/metadata/typescript.mocks.ts @@ -10,7 +10,9 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; -export interface Directory { [name: string]: (Directory|string); } +export interface Directory { + [name: string]: (Directory|string); +} export class Host implements ts.LanguageServiceHost { private overrides = new Map<string, string>(); @@ -26,20 +28,30 @@ export class Host implements ts.LanguageServiceHost { }; } - getScriptFileNames(): string[] { return this.scripts; } + getScriptFileNames(): string[] { + return this.scripts; + } - getScriptVersion(fileName: string): string { return this.version.toString(); } + getScriptVersion(fileName: string): string { + return this.version.toString(); + } getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined { const content = this.getFileContent(fileName); if (content) return ts.ScriptSnapshot.fromString(content); } - fileExists(fileName: string): boolean { return this.getFileContent(fileName) != null; } + fileExists(fileName: string): boolean { + return this.getFileContent(fileName) != null; + } - getCurrentDirectory(): string { return '/'; } + getCurrentDirectory(): string { + return '/'; + } - getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'lib.d.ts'; + } overrideFile(fileName: string, content: string) { this.overrides.set(fileName, content); @@ -81,24 +93,52 @@ export function open(directory: Directory, fileName: string): Directory|string|u export class MockNode implements ts.Node { decorators?: ts.NodeArray<ts.Decorator>; modifiers?: ts.NodeArray<ts.Modifier>; - parent !: ts.Node; + parent!: ts.Node; constructor( public kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, public flags: ts.NodeFlags = 0, public pos: number = 0, public end: number = 0) {} - getSourceFile(): ts.SourceFile { return null as any as ts.SourceFile; } - getChildCount(sourceFile?: ts.SourceFile): number { return 0; } - getChildAt(index: number, sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; } - getChildren(sourceFile?: ts.SourceFile): ts.Node[] { return []; } - getStart(sourceFile?: ts.SourceFile): number { return 0; } - getFullStart(): number { return 0; } - getEnd(): number { return 0; } - getWidth(sourceFile?: ts.SourceFile): number { return 0; } - getFullWidth(): number { return 0; } - getLeadingTriviaWidth(sourceFile?: ts.SourceFile): number { return 0; } - getFullText(sourceFile?: ts.SourceFile): string { return ''; } - getText(sourceFile?: ts.SourceFile): string { return ''; } - getFirstToken(sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; } - getLastToken(sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; } + getSourceFile(): ts.SourceFile { + return null as any as ts.SourceFile; + } + getChildCount(sourceFile?: ts.SourceFile): number { + return 0; + } + getChildAt(index: number, sourceFile?: ts.SourceFile): ts.Node { + return null as any as ts.Node; + } + getChildren(sourceFile?: ts.SourceFile): ts.Node[] { + return []; + } + getStart(sourceFile?: ts.SourceFile): number { + return 0; + } + getFullStart(): number { + return 0; + } + getEnd(): number { + return 0; + } + getWidth(sourceFile?: ts.SourceFile): number { + return 0; + } + getFullWidth(): number { + return 0; + } + getLeadingTriviaWidth(sourceFile?: ts.SourceFile): number { + return 0; + } + getFullText(sourceFile?: ts.SourceFile): string { + return ''; + } + getText(sourceFile?: ts.SourceFile): string { + return ''; + } + getFirstToken(sourceFile?: ts.SourceFile): ts.Node { + return null as any as ts.Node; + } + getLastToken(sourceFile?: ts.SourceFile): ts.Node { + return null as any as ts.Node; + } forEachChild<T>( cbNode: (node: ts.Node) => T | undefined, cbNodeArray?: (nodes: ts.NodeArray<ts.Node>) => T | undefined): T|undefined { @@ -111,10 +151,10 @@ export class MockIdentifier extends MockNode implements ts.Identifier { isInJSDocNamespace?: boolean; decorators?: ts.NodeArray<ts.Decorator>; modifiers?: ts.NodeArray<ts.Modifier>; - parent !: ts.Node; + parent!: ts.Node; public text: string; // TODO(issue/24571): remove '!'. - public escapedText !: ts.__String; + public escapedText!: ts.__String; // tslint:disable public _declarationBrand: any; public _primaryExpressionBrand: any; @@ -135,7 +175,7 @@ export class MockIdentifier extends MockNode implements ts.Identifier { } export class MockVariableDeclaration extends MockNode implements ts.VariableDeclaration { - parent !: ts.VariableDeclarationList | ts.CatchClause; + parent!: ts.VariableDeclarationList|ts.CatchClause; exclamationToken?: ts.Token<ts.SyntaxKind.ExclamationToken>; type?: ts.TypeNode; initializer?: ts.Expression; @@ -151,32 +191,46 @@ export class MockVariableDeclaration extends MockNode implements ts.VariableDecl super(kind, flags, pos, end); } - static of (name: string): MockVariableDeclaration { + static of(name: string): MockVariableDeclaration { return new MockVariableDeclaration(new MockIdentifier(name)); } } export class MockSymbol implements ts.Symbol { - declarations !: ts.Declaration[]; - valueDeclaration !: ts.Declaration; + declarations!: ts.Declaration[]; + valueDeclaration!: ts.Declaration; members?: ts.UnderscoreEscapedMap<ts.Symbol>; exports?: ts.UnderscoreEscapedMap<ts.Symbol>; globalExports?: ts.UnderscoreEscapedMap<ts.Symbol>; // TODO(issue/24571): remove '!'. - public escapedName !: ts.__String; + public escapedName!: ts.__String; constructor( public name: string, private node: ts.Declaration = MockVariableDeclaration.of(name), public flags: ts.SymbolFlags = 0) {} - getFlags(): ts.SymbolFlags { return this.flags; } - getName(): string { return this.name; } - getEscapedName(): ts.__String { return this.escapedName; } - getDeclarations(): ts.Declaration[] { return [this.node]; } - getDocumentationComment(): ts.SymbolDisplayPart[] { return []; } + getFlags(): ts.SymbolFlags { + return this.flags; + } + getName(): string { + return this.name; + } + getEscapedName(): ts.__String { + return this.escapedName; + } + getDeclarations(): ts.Declaration[] { + return [this.node]; + } + getDocumentationComment(): ts.SymbolDisplayPart[] { + return []; + } // TODO(vicb): removed in TS 2.2 - getJsDocTags(): any[] { return []; } + getJsDocTags(): any[] { + return []; + } - static of (name: string): MockSymbol { return new MockSymbol(name); } + static of(name: string): MockSymbol { + return new MockSymbol(name); + } } export function expectNoDiagnostics(diagnostics: ts.Diagnostic[]) { @@ -219,14 +273,14 @@ export function findVar(sourceFile: ts.SourceFile, name: string): ts.VariableDec export function findVarInitializer(sourceFile: ts.SourceFile, name: string): ts.Expression { const v = findVar(sourceFile, name); expect(v && v.initializer).toBeDefined(); - return v !.initializer !; + return v!.initializer!; } export function isClass(node: ts.Node): node is ts.ClassDeclaration { return node.kind === ts.SyntaxKind.ClassDeclaration; } -export function isNamed(node: ts.Node | undefined, name: string): node is ts.Identifier { +export function isNamed(node: ts.Node|undefined, name: string): node is ts.Identifier { return !!node && node.kind === ts.SyntaxKind.Identifier && (<ts.Identifier>node).text === name; } diff --git a/packages/compiler-cli/test/mocks.ts b/packages/compiler-cli/test/mocks.ts index a1ac68edd5309..7d8d083d3edfe 100644 --- a/packages/compiler-cli/test/mocks.ts +++ b/packages/compiler-cli/test/mocks.ts @@ -8,16 +8,22 @@ import * as ts from 'typescript'; -export type Entry = string | Directory; +export type Entry = string|Directory; -export interface Directory { [name: string]: Entry; } +export interface Directory { + [name: string]: Entry; +} export class MockAotContext { private files: Entry[]; - constructor(public currentDirectory: string, ...files: Entry[]) { this.files = files; } + constructor(public currentDirectory: string, ...files: Entry[]) { + this.files = files; + } - fileExists(fileName: string): boolean { return typeof this.getEntry(fileName) === 'string'; } + fileExists(fileName: string): boolean { + return typeof this.getEntry(fileName) === 'string'; + } directoryExists(path: string): boolean { return path === this.currentDirectory || typeof this.getEntry(path) === 'object'; @@ -28,7 +34,7 @@ export class MockAotContext { if (typeof data === 'string') { return data; } - return undefined !; + return undefined!; } readResource(fileName: string): Promise<string> { @@ -41,14 +47,16 @@ export class MockAotContext { writeFile(fileName: string, data: string): void { const parts = fileName.split('/'); - const name = parts.pop() !; + const name = parts.pop()!; const entry = this.getEntry(parts); if (entry && typeof entry !== 'string') { entry[name] = data; } } - assumeFileExists(fileName: string): void { this.writeFile(fileName, ''); } + assumeFileExists(fileName: string): void { + this.writeFile(fileName, ''); + } getEntry(fileName: string|string[]): Entry|undefined { let parts = typeof fileName === 'string' ? fileName.split('/') : fileName; @@ -69,7 +77,9 @@ export class MockAotContext { } } - override(files: Entry) { return new MockAotContext(this.currentDirectory, files, ...this.files); } + override(files: Entry) { + return new MockAotContext(this.currentDirectory, files, ...this.files); + } } function first<T>(a: T[], cb: (value: T) => T | undefined): T|undefined { @@ -82,7 +92,7 @@ function first<T>(a: T[], cb: (value: T) => T | undefined): T|undefined { function getEntryFromFiles(parts: string[], files: Entry) { let current = files; while (parts.length) { - const part = parts.shift() !; + const part = parts.shift()!; if (typeof current === 'string') { return undefined; } @@ -98,7 +108,7 @@ function getEntryFromFiles(parts: string[], files: Entry) { function normalize(parts: string[]): string[] { const result: string[] = []; while (parts.length) { - const part = parts.shift() !; + const part = parts.shift()!; switch (part) { case '.': break; @@ -115,9 +125,13 @@ function normalize(parts: string[]): string[] { export class MockCompilerHost implements ts.CompilerHost { constructor(private context: MockAotContext) {} - fileExists(fileName: string): boolean { return this.context.fileExists(fileName); } + fileExists(fileName: string): boolean { + return this.context.fileExists(fileName); + } - readFile(fileName: string): string { return this.context.readFile(fileName); } + readFile(fileName: string): string { + return this.context.readFile(fileName); + } directoryExists(directoryName: string): boolean { return this.context.directoryExists(directoryName); @@ -130,7 +144,7 @@ export class MockCompilerHost implements ts.CompilerHost { if (sourceText != null) { return ts.createSourceFile(fileName, sourceText, languageVersion); } else { - return undefined !; + return undefined!; } } @@ -138,15 +152,28 @@ export class MockCompilerHost implements ts.CompilerHost { return ts.getDefaultLibFileName(options); } - writeFile: ts.WriteFileCallback = (fileName, text) => { this.context.writeFile(fileName, text); }; + writeFile: ts.WriteFileCallback = + (fileName, text) => { + this.context.writeFile(fileName, text); + } - getCurrentDirectory(): string { return this.context.currentDirectory; } + getCurrentDirectory(): string { + return this.context.currentDirectory; + } - getCanonicalFileName(fileName: string): string { return fileName; } + getCanonicalFileName(fileName: string): string { + return fileName; + } - useCaseSensitiveFileNames(): boolean { return false; } + useCaseSensitiveFileNames(): boolean { + return false; + } - getNewLine(): string { return '\n'; } + getNewLine(): string { + return '\n'; + } - getDirectories(path: string): string[] { return this.context.getDirectories(path); } + getDirectories(path: string): string[] { + return this.context.getDirectories(path); + } } diff --git a/packages/compiler-cli/test/ngc_spec.ts b/packages/compiler-cli/test/ngc_spec.ts index d972374208b27..d2c783ad6c152 100644 --- a/packages/compiler-cli/test/ngc_spec.ts +++ b/packages/compiler-cli/test/ngc_spec.ts @@ -41,7 +41,9 @@ describe('ngc transformer command-line', () => { basePath = support.basePath; outDir = path.join(basePath, 'built'); process.chdir(basePath); - write = (fileName: string, content: string) => { support.write(fileName, content); }; + write = (fileName: string, content: string) => { + support.write(fileName, content); + }; write('tsconfig-base.json', `{ "compilerOptions": { @@ -96,8 +98,9 @@ describe('ngc transformer command-line', () => { }); describe('errors', () => { - - beforeEach(() => { errorSpy.and.stub(); }); + beforeEach(() => { + errorSpy.and.stub(); + }); it('should not print the stack trace if user input file does not exist', () => { writeConfig(`{ @@ -231,7 +234,6 @@ describe('ngc transformer command-line', () => { }); describe('compile ngfactory files', () => { - it('should compile ngfactory files that are not referenced by root files', () => { writeConfig(`{ "extends": "./tsconfig-base.json", @@ -1122,7 +1124,6 @@ describe('ngc transformer command-line', () => { }); describe('with external symbol re-exports enabled', () => { - it('should be able to compile multiple libraries with summaries', () => { // Note: we need to emit the generated code for the libraries // into the node_modules, as that is the only way that we @@ -1559,11 +1560,15 @@ describe('ngc transformer command-line', () => { originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; const timerToken = 100; - spyOn(ts.sys, 'setTimeout').and.callFake((callback: () => void) => { + // TODO: @JiaLiPassion, need to wait @types/jasmine to handle optional method case + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486 + spyOn(ts.sys as any, 'setTimeout').and.callFake((callback: () => void) => { timer = callback; return timerToken; }); - spyOn(ts.sys, 'clearTimeout').and.callFake((token: number) => { + // TODO: @JiaLiPassion, need to wait @types/jasmine to handle optional method case + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486 + spyOn(ts.sys as any, 'clearTimeout').and.callFake((token: number) => { if (token == timerToken) { timer = undefined; } @@ -1615,7 +1620,9 @@ describe('ngc transformer command-line', () => { `); }); - afterEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; }); + afterEach(() => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + }); function writeAppConfig(location: string) { writeConfig(`{ @@ -1670,11 +1677,13 @@ describe('ngc transformer command-line', () => { `); })); - it('should recompile when the html file changes', - expectRecompile(() => { write('greet.html', '<p> Hello {{name}} again!</p>'); })); + it('should recompile when the html file changes', expectRecompile(() => { + write('greet.html', '<p> Hello {{name}} again!</p>'); + })); - it('should recompile when the css file changes', - expectRecompile(() => { write('greet.css', `p.greeting { color: blue }`); })); + it('should recompile when the css file changes', expectRecompile(() => { + write('greet.css', `p.greeting { color: blue }`); + })); }); describe('regressions', () => { @@ -2039,8 +2048,8 @@ describe('ngc transformer command-line', () => { expect(exitCode).toBe(1, 'Compile was expected to fail'); const srcPathWithSep = `lib/`; expect(messages[0]) - .toEqual( - `${srcPathWithSep}test.component.ts(6,21): Error during template compile of 'TestComponent' + .toEqual(`${ + srcPathWithSep}test.component.ts(6,21): Error during template compile of 'TestComponent' Tagged template expressions are not supported in metadata in 't1' 't1' references 't2' at ${srcPathWithSep}indirect1.ts(3,27) 't2' contains the error at ${srcPathWithSep}indirect2.ts(4,27). @@ -2049,7 +2058,6 @@ describe('ngc transformer command-line', () => { }); describe('tree shakeable services', () => { - function compileService(source: string): string { write('service.ts', source); @@ -2323,17 +2331,17 @@ describe('ngc transformer command-line', () => { })); write('lib1/index.ts', ` import {Directive} from '@angular/core'; - + @Directive() export class BaseClass {} `); write('index.ts', ` import {NgModule, Directive} from '@angular/core'; import {BaseClass} from 'lib1_built'; - + @Directive({selector: 'my-dir'}) export class MyDirective extends BaseClass {} - + @NgModule({declarations: [MyDirective]}) export class AppModule {} `); diff --git a/packages/compiler-cli/test/ngtsc/component_indexing_spec.ts b/packages/compiler-cli/test/ngtsc/component_indexing_spec.ts index ff05d00593ee9..a1f9e9a989db4 100644 --- a/packages/compiler-cli/test/ngtsc/component_indexing_spec.ts +++ b/packages/compiler-cli/test/ngtsc/component_indexing_spec.ts @@ -14,7 +14,7 @@ import {NgtscTestEnvironment} from './env'; runInEachFileSystem(() => { describe('ngtsc component indexing', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; let testSourceFile: AbsoluteFsPath; let testTemplateFile: AbsoluteFsPath; @@ -177,10 +177,10 @@ runInEachFileSystem(() => { expect(testComp).toBeDefined(); expect(testImportComp).toBeDefined(); - expect(testComp !.template.usedComponents.size).toBe(0); - expect(testImportComp !.template.usedComponents.size).toBe(1); + expect(testComp!.template.usedComponents.size).toBe(0); + expect(testImportComp!.template.usedComponents.size).toBe(1); - const [usedComp] = Array.from(testImportComp !.template.usedComponents); + const [usedComp] = Array.from(testImportComp!.template.usedComponents); expect(indexed.get(usedComp)).toEqual(testComp); }); }); diff --git a/packages/compiler-cli/test/ngtsc/env.ts b/packages/compiler-cli/test/ngtsc/env.ts index d8c63fb1fa389..1d5a2deb6a209 100644 --- a/packages/compiler-cli/test/ngtsc/env.ts +++ b/packages/compiler-cli/test/ngtsc/env.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {CustomTransformers, Program, defaultGatherDiagnostics} from '@angular/compiler-cli'; +import {CustomTransformers, defaultGatherDiagnostics, Program} from '@angular/compiler-cli'; import * as api from '@angular/compiler-cli/src/transformers/api'; import * as ts from 'typescript'; import {createCompilerHost, createProgram} from '../../index'; import {main, mainDiagnosticsForTest, readNgcCommandLineAndConfiguration} from '../../src/main'; -import {AbsoluteFsPath, FileSystem, NgtscCompilerHost, absoluteFrom, getFileSystem} from '../../src/ngtsc/file_system'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, NgtscCompilerHost} from '../../src/ngtsc/file_system'; import {Folder, MockFileSystem} from '../../src/ngtsc/file_system/testing'; import {IndexedComponent} from '../../src/ngtsc/indexer'; import {NgtscProgram} from '../../src/ngtsc/program'; @@ -115,7 +115,7 @@ export class NgtscTestEnvironment { if (this.multiCompileHostExt === null) { throw new Error(`Not tracking written files - call enableMultipleCompilations()`); } - this.changedResources !.clear(); + this.changedResources!.clear(); this.multiCompileHostExt.flushWrittenFileTracking(); } @@ -123,7 +123,9 @@ export class NgtscTestEnvironment { * Older versions of the CLI do not provide the `CompilerHost.getModifiedResourceFiles()` method. * This results in the `changedResources` set being `null`. */ - simulateLegacyCLICompilerHost() { this.changedResources = null; } + simulateLegacyCLICompilerHost() { + this.changedResources = null; + } getFilesWrittenSinceLastFlush(): Set<string> { if (this.multiCompileHostExt === null) { @@ -142,7 +144,7 @@ export class NgtscTestEnvironment { const absFilePath = this.fs.resolve(this.basePath, fileName); if (this.multiCompileHostExt !== null) { this.multiCompileHostExt.invalidate(absFilePath); - this.changedResources !.add(absFilePath); + this.changedResources!.add(absFilePath); } this.fs.ensureDir(this.fs.dirname(absFilePath)); this.fs.writeFile(absFilePath, content); @@ -156,8 +158,7 @@ export class NgtscTestEnvironment { this.multiCompileHostExt.invalidate(absFilePath); } - tsconfig(extraOpts: {[key: string]: string | boolean | null} = {}, extraRootDirs?: string[]): - void { + tsconfig(extraOpts: {[key: string]: string|boolean|null} = {}, extraRootDirs?: string[]): void { const tsconfig: {[key: string]: any} = { extends: './tsconfig-base.json', angularCompilerOptions: {...extraOpts, enableIvy: true}, @@ -179,7 +180,7 @@ export class NgtscTestEnvironment { */ driveMain(customTransformers?: CustomTransformers): void { const errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error); - let reuseProgram: {program: Program | undefined}|undefined = undefined; + let reuseProgram: {program: Program|undefined}|undefined = undefined; if (this.multiCompileHostExt !== null) { reuseProgram = { program: this.oldProgram || undefined, @@ -191,7 +192,7 @@ export class NgtscTestEnvironment { expect(errorSpy).not.toHaveBeenCalled(); expect(exitCode).toBe(0); if (this.multiCompileHostExt !== null) { - this.oldProgram = reuseProgram !.program !; + this.oldProgram = reuseProgram!.program!; } } @@ -200,7 +201,7 @@ export class NgtscTestEnvironment { */ driveDiagnostics(): ReadonlyArray<ts.Diagnostic> { // ngtsc only produces ts.Diagnostic messages. - let reuseProgram: {program: Program | undefined}|undefined = undefined; + let reuseProgram: {program: Program|undefined}|undefined = undefined; if (this.multiCompileHostExt !== null) { reuseProgram = { program: this.oldProgram || undefined, @@ -212,7 +213,7 @@ export class NgtscTestEnvironment { if (this.multiCompileHostExt !== null) { - this.oldProgram = reuseProgram !.program !; + this.oldProgram = reuseProgram!.program!; } // In ngtsc, only `ts.Diagnostic`s are produced. @@ -245,7 +246,7 @@ export class NgtscTestEnvironment { } class AugmentedCompilerHost extends NgtscCompilerHost { - delegate !: ts.CompilerHost; + delegate!: ts.CompilerHost; } const ROOT_PREFIX = 'root/'; @@ -283,7 +284,7 @@ class MultiCompileHostExt extends AugmentedCompilerHost implements Partial<ts.Co fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): ts.SourceFile|undefined { if (this.cache.has(fileName)) { - return this.cache.get(fileName) !; + return this.cache.get(fileName)!; } const sf = super.getSourceFile(fileName, languageVersion); if (sf !== undefined) { @@ -292,7 +293,9 @@ class MultiCompileHostExt extends AugmentedCompilerHost implements Partial<ts.Co return sf; } - flushWrittenFileTracking(): void { this.writtenFiles.clear(); } + flushWrittenFileTracking(): void { + this.writtenFiles.clear(); + } writeFile( fileName: string, data: string, writeByteOrderMark: boolean, @@ -302,9 +305,13 @@ class MultiCompileHostExt extends AugmentedCompilerHost implements Partial<ts.Co this.writtenFiles.add(fileName); } - getFilesWrittenSinceLastFlush(): Set<string> { return this.writtenFiles; } + getFilesWrittenSinceLastFlush(): Set<string> { + return this.writtenFiles; + } - invalidate(fileName: string): void { this.cache.delete(fileName); } + invalidate(fileName: string): void { + this.cache.delete(fileName); + } } class ResourceLoadingCompileHost extends AugmentedCompilerHost implements api.CompilerHost { @@ -323,7 +330,7 @@ function makeWrapHost(wrapped: AugmentedCompilerHost): (host: ts.CompilerHost) = return new Proxy(delegate, { get: (target: ts.CompilerHost, name: string): any => { if ((wrapped as any)[name] !== undefined) { - return (wrapped as any)[name] !.bind(wrapped); + return (wrapped as any)[name]!.bind(wrapped); } return (target as any)[name]; } diff --git a/packages/compiler-cli/test/ngtsc/fake_core/index.ts b/packages/compiler-cli/test/ngtsc/fake_core/index.ts index 6c296d213747b..b1c7451d13f9f 100644 --- a/packages/compiler-cli/test/ngtsc/fake_core/index.ts +++ b/packages/compiler-cli/test/ngtsc/fake_core/index.ts @@ -8,19 +8,19 @@ interface FnWithArg<T> { (...args: any[]): T; - new (...args: any[]): T; + new(...args: any[]): T; } function callableClassDecorator(): FnWithArg<(clazz: any) => any> { - return null !; + return null!; } function callableParamDecorator(): FnWithArg<(a: any, b: any, c: any) => void> { - return null !; + return null!; } function callablePropDecorator(): FnWithArg<(a: any, b: any) => any> { - return null !; + return null!; } export const Component = callableClassDecorator(); @@ -34,6 +34,7 @@ export const Inject = callableParamDecorator(); export const Self = callableParamDecorator(); export const SkipSelf = callableParamDecorator(); export const Optional = callableParamDecorator(); +export const Host = callableParamDecorator(); export const ContentChild = callablePropDecorator(); export const ContentChildren = callablePropDecorator(); @@ -65,10 +66,13 @@ export function forwardRef<T>(fn: () => T): T { return fn(); } -export interface SimpleChanges { [propName: string]: any; } +export interface SimpleChanges { + [propName: string]: any; +} export type ɵɵNgModuleDefWithMeta<ModuleT, DeclarationsT, ImportsT, ExportsT> = any; -export type ɵɵDirectiveDefWithMeta<DirT, SelectorT, ExportAsT, InputsT, OutputsT, QueriesT> = any; +export type ɵɵDirectiveDefWithMeta< + DirT, SelectorT, ExportAsT, InputsT, OutputsT, QueriesT, NgContentSelectorsT> = any; export type ɵɵPipeDefWithMeta<PipeT, NameT> = any; export enum ViewEncapsulation { @@ -87,11 +91,15 @@ export const CUSTOM_ELEMENTS_SCHEMA: any = false; export const NO_ERRORS_SCHEMA: any = false; export class EventEmitter<T> { - subscribe(generatorOrNext?: any, error?: any, complete?: any): unknown { return null; } + subscribe(generatorOrNext?: any, error?: any, complete?: any): unknown { + return null; + } } -export interface QueryList<T>/* implements Iterable<T> */ { [Symbol.iterator]: () => Iterator<T>; } +export interface QueryList<T>/* implements Iterable<T> */ { + [Symbol.iterator]: () => Iterator<T>; +} -export type NgIterable<T> = Array<T>| Iterable<T>; +export type NgIterable<T> = Array<T>|Iterable<T>; export class NgZone {} diff --git a/packages/compiler-cli/test/ngtsc/incremental_error_spec.ts b/packages/compiler-cli/test/ngtsc/incremental_error_spec.ts index 16a51005c3798..c850b74c7f14d 100644 --- a/packages/compiler-cli/test/ngtsc/incremental_error_spec.ts +++ b/packages/compiler-cli/test/ngtsc/incremental_error_spec.ts @@ -16,7 +16,7 @@ const testFiles = loadStandardTestFiles(); runInEachFileSystem(() => { describe('ngtsc incremental compilation with errors', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles); @@ -66,7 +66,7 @@ runInEachFileSystem(() => { `); const diags = env.driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].file !.fileName).toBe(_('/other.ts')); + expect(diags[0].file!.fileName).toBe(_('/other.ts')); expectToHaveWritten([]); // Remove the error. /other.js should now be emitted again. @@ -92,7 +92,7 @@ runInEachFileSystem(() => { const diags = env.driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].file !.fileName).toBe(_('/other.ts')); + expect(diags[0].file!.fileName).toBe(_('/other.ts')); expectToHaveWritten([]); // Remove the error. All files should be emitted. @@ -128,7 +128,7 @@ runInEachFileSystem(() => { const diags = env.driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].file !.fileName).toBe(_('/other.ts')); + expect(diags[0].file!.fileName).toBe(_('/other.ts')); expectToHaveWritten([]); // Remove the error. All files should be emitted. @@ -175,7 +175,7 @@ runInEachFileSystem(() => { const diags = env.driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].file !.fileName).toBe(_('/other.ts')); + expect(diags[0].file!.fileName).toBe(_('/other.ts')); expectToHaveWritten([]); // Remove the error. All files should be emitted. diff --git a/packages/compiler-cli/test/ngtsc/incremental_spec.ts b/packages/compiler-cli/test/ngtsc/incremental_spec.ts index f8842baae6b36..6cf7f993435e8 100644 --- a/packages/compiler-cli/test/ngtsc/incremental_spec.ts +++ b/packages/compiler-cli/test/ngtsc/incremental_spec.ts @@ -15,7 +15,7 @@ const testFiles = loadStandardTestFiles(); runInEachFileSystem(() => { describe('ngtsc incremental compilation', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles); diff --git a/packages/compiler-cli/test/ngtsc/modulewithproviders_spec.ts b/packages/compiler-cli/test/ngtsc/modulewithproviders_spec.ts index 3035661e28fb9..59663f0f5a8be 100644 --- a/packages/compiler-cli/test/ngtsc/modulewithproviders_spec.ts +++ b/packages/compiler-cli/test/ngtsc/modulewithproviders_spec.ts @@ -17,7 +17,7 @@ const testFiles = loadStandardTestFiles(); runInEachFileSystem(() => { describe('ModuleWithProviders generic type transform', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles); diff --git a/packages/compiler-cli/test/ngtsc/monorepo_spec.ts b/packages/compiler-cli/test/ngtsc/monorepo_spec.ts index ac90576641fca..f6b0666a9b85f 100644 --- a/packages/compiler-cli/test/ngtsc/monorepo_spec.ts +++ b/packages/compiler-cli/test/ngtsc/monorepo_spec.ts @@ -16,7 +16,7 @@ const testFiles = loadStandardTestFiles(); runInEachFileSystem(() => { describe('monorepos', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles, absoluteFrom('/app')); diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 9921e67f1ab54..53fff99df4f09 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -37,9 +37,13 @@ const setClassMetadataRegExp = (expectedType: string): RegExp => const testFiles = loadStandardTestFiles(); +function getDiagnosticSourceCode(diag: ts.Diagnostic): string { + return diag.file!.text.substr(diag.start!, diag.length!); +} + runInEachFileSystem(os => { describe('ngtsc behavioral tests', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles); @@ -68,8 +72,8 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Dep>;'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Service>;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Dep>;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Dep, never>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service, never>;'); }); it('should compile Injectables with a generic service', () => { @@ -86,7 +90,7 @@ runInEachFileSystem(os => { const jsContents = env.getContents('test.js'); expect(jsContents).toContain('Store.ɵprov ='); const dtsContents = env.getContents('test.d.ts'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Store<any>>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Store<any>, never>;'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Store<any>>;'); }); @@ -117,8 +121,8 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Dep>;'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Service>;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Dep>;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Dep, never>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service, never>;'); }); it('should compile Injectables with providedIn and factory without errors', () => { @@ -143,7 +147,7 @@ runInEachFileSystem(os => { expect(jsContents).not.toContain('__decorate'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Service>;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service, never>;'); }); it('should compile Injectables with providedIn and factory with deps without errors', () => { @@ -172,7 +176,7 @@ runInEachFileSystem(os => { expect(jsContents).not.toContain('__decorate'); const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Service>;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service, never>;'); }); it('should compile @Injectable with an @Optional dependency', () => { @@ -237,7 +241,7 @@ runInEachFileSystem(os => { expect(dtsContents) .toContain( 'static ɵdir: i0.ɵɵDirectiveDefWithMeta<TestDir, "[dir]", never, {}, {}, never>'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestDir>'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestDir, never>'); }); it('should compile abstract Directives without errors', () => { @@ -259,7 +263,7 @@ runInEachFileSystem(os => { expect(dtsContents) .toContain( 'static ɵdir: i0.ɵɵDirectiveDefWithMeta<TestDir, never, never, {}, {}, never>'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestDir>'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestDir, never>'); }); it('should compile Components (inline template) without errors', () => { @@ -283,8 +287,8 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta<TestCmp, "test-cmp", never, {}, {}, never>'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestCmp>'); + 'static ɵcmp: i0.ɵɵComponentDefWithMeta<TestCmp, "test-cmp", never, {}, {}, never, never>'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestCmp, never>'); }); it('should compile Components (dynamic inline template) without errors', () => { @@ -309,8 +313,9 @@ runInEachFileSystem(os => { expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta<TestCmp, "test-cmp", never, {}, {}, never>'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestCmp>'); + 'static ɵcmp: i0.ɵɵComponentDefWithMeta' + + '<TestCmp, "test-cmp", never, {}, {}, never, never>'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestCmp, never>'); }); it('should compile Components (function call inline template) without errors', () => { @@ -337,8 +342,8 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta<TestCmp, "test-cmp", never, {}, {}, never>'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestCmp>'); + 'static ɵcmp: i0.ɵɵComponentDefWithMeta<TestCmp, "test-cmp", never, {}, {}, never, never>'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestCmp, never>'); }); it('should compile Components (external template) without errors', () => { @@ -364,7 +369,6 @@ runInEachFileSystem(os => { // that start with `C:`. if (os !== 'Windows' || platform() === 'win32') { describe('when closure annotations are requested', () => { - it('should add @nocollapse to static fields', () => { env.tsconfig({ 'annotateForClosureCompiler': true, @@ -526,7 +530,6 @@ runInEachFileSystem(os => { verifyOutput(env.getContents('test.js')); }); }); - }); } @@ -935,7 +938,7 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( - 'static ɵcmp: i0.ɵɵComponentDefWithMeta<TestCmp, "test-cmp", never, {}, {}, never>'); + 'static ɵcmp: i0.ɵɵComponentDefWithMeta<TestCmp, "test-cmp", never, {}, {}, never, never>'); expect(dtsContents) .toContain( 'static ɵmod: i0.ɵɵNgModuleDefWithMeta<TestModule, [typeof TestCmp], never, never>'); @@ -1327,7 +1330,7 @@ runInEachFileSystem(os => { .toContain( 'TestPipe.ɵfac = function TestPipe_Factory(t) { return new (t || TestPipe)(); }'); expect(dtsContents).toContain('static ɵpipe: i0.ɵɵPipeDefWithMeta<TestPipe, "test-pipe">;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestPipe>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestPipe, never>;'); }); it('should compile pure Pipes without errors', () => { @@ -1352,7 +1355,7 @@ runInEachFileSystem(os => { .toContain( 'TestPipe.ɵfac = function TestPipe_Factory(t) { return new (t || TestPipe)(); }'); expect(dtsContents).toContain('static ɵpipe: i0.ɵɵPipeDefWithMeta<TestPipe, "test-pipe">;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestPipe>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestPipe, never>;'); }); it('should compile Pipes with dependencies', () => { @@ -1393,7 +1396,7 @@ runInEachFileSystem(os => { const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain('static ɵpipe: i0.ɵɵPipeDefWithMeta<TestPipe<any>, "test-pipe">;'); - expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestPipe<any>>;'); + expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<TestPipe<any>, never>;'); }); it('should include @Pipes in @NgModule scopes', () => { @@ -1709,7 +1712,8 @@ runInEachFileSystem(os => { }); ['ContentChild', 'ContentChildren'].forEach(decorator => { - it(`should throw if \`descendants\` field of @${decorator}'s options argument has wrong type`, + it(`should throw if \`descendants\` field of @${ + decorator}'s options argument has wrong type`, () => { env.tsconfig({}); env.write('test.ts', ` @@ -2143,7 +2147,6 @@ runInEachFileSystem(os => { expect(jsContents) .toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()'); }); - }); }); @@ -2574,6 +2577,141 @@ runInEachFileSystem(os => { `FooCmp.ɵfac = function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵɵinjectAttribute("test"), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.Injector), i0.ɵɵdirectiveInject(i0.Renderer2), i0.ɵɵdirectiveInject(i0.TemplateRef), i0.ɵɵdirectiveInject(i0.ViewContainerRef)); }`); }); + it('should include constructor dependency metadata for directives/components/pipes', () => { + env.write(`test.ts`, ` + import {Attribute, Component, Directive, Pipe, Self, SkipSelf, Host, Optional} from '@angular/core'; + + export class MyService {} + export function dynamic() {}; + + @Directive() + export class WithDecorators { + constructor( + @Self() withSelf: MyService, + @SkipSelf() withSkipSelf: MyService, + @Host() withHost: MyService, + @Optional() withOptional: MyService, + @Attribute("attr") withAttribute: string, + @Attribute(dynamic()) withAttributeDynamic: string, + @Optional() @SkipSelf() @Host() withMany: MyService, + noDecorators: MyService) {} + } + + @Directive() + export class NoCtor {} + + @Directive() + export class EmptyCtor { + constructor() {} + } + + @Directive() + export class WithoutDecorators { + constructor(noDecorators: MyService) {} + } + + @Component({ template: 'test' }) + export class MyCmp { + constructor(@Host() withHost: MyService) {} + } + + @Pipe({ name: 'test' }) + export class MyPipe { + constructor(@Host() withHost: MyService) {} + } + `); + + env.driveMain(); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents) + .toContain( + 'static ɵfac: i0.ɵɵFactoryDef<WithDecorators, [' + + '{ self: true; }, { skipSelf: true; }, { host: true; }, ' + + '{ optional: true; }, { attribute: "attr"; }, { attribute: unknown; }, ' + + '{ optional: true; host: true; skipSelf: true; }, null]>'); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef<NoCtor, never>`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef<EmptyCtor, never>`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef<WithoutDecorators, never>`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef<MyCmp, [{ host: true; }]>`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef<MyPipe, [{ host: true; }]>`); + }); + + it('should include constructor dependency metadata for @Injectable', () => { + env.write(`test.ts`, ` + import {Injectable, Self, Host} from '@angular/core'; + + export class MyService {} + + @Injectable() + export class Inj { + constructor(@Self() service: MyService) {} + } + + @Injectable({ useExisting: MyService }) + export class InjUseExisting { + constructor(@Self() service: MyService) {} + } + + @Injectable({ useClass: MyService }) + export class InjUseClass { + constructor(@Self() service: MyService) {} + } + + @Injectable({ useClass: MyService, deps: [[new Host(), MyService]] }) + export class InjUseClassWithDeps { + constructor(@Self() service: MyService) {} + } + + @Injectable({ useFactory: () => new Injectable(new MyService()) }) + export class InjUseFactory { + constructor(@Self() service: MyService) {} + } + + @Injectable({ useFactory: (service: MyService) => new Injectable(service), deps: [[new Host(), MyService]] }) + export class InjUseFactoryWithDeps { + constructor(@Self() service: MyService) {} + } + + @Injectable({ useValue: new Injectable(new MyService()) }) + export class InjUseValue { + constructor(@Self() service: MyService) {} + } + `); + + env.driveMain(); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef<Inj, [{ self: true; }]>`); + expect(dtsContents) + .toContain(`static ɵfac: i0.ɵɵFactoryDef<InjUseExisting, [{ self: true; }]>`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef<InjUseClass, [{ self: true; }]>`); + expect(dtsContents) + .toContain(`static ɵfac: i0.ɵɵFactoryDef<InjUseClassWithDeps, [{ self: true; }]>`); + expect(dtsContents) + .toContain(`static ɵfac: i0.ɵɵFactoryDef<InjUseFactory, [{ self: true; }]>`); + expect(dtsContents) + .toContain(`static ɵfac: i0.ɵɵFactoryDef<InjUseFactoryWithDeps, [{ self: true; }]>`); + expect(dtsContents).toContain(`static ɵfac: i0.ɵɵFactoryDef<InjUseValue, [{ self: true; }]>`); + }); + + it('should include ng-content selectors in the metadata', () => { + env.write(`test.ts`, ` + import {Component} from '@angular/core'; + + @Component({ + selector: 'test', + template: '<ng-content></ng-content> <ng-content select=".foo"></ng-content>', + }) + export class TestCmp { + } + `); + + env.driveMain(); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents) + .toContain( + 'static ɵcmp: i0.ɵɵComponentDefWithMeta<TestCmp, "test", never, {}, {}, never, ["*", ".foo"]>'); + }); + it('should generate queries for components', () => { env.write(`test.ts`, ` import {Component, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core'; @@ -2761,6 +2899,27 @@ runInEachFileSystem(os => { `Unexpected global target 'UnknownTarget' defined for 'click' event. Supported list of global targets: window,document,body.`); }); + it('should provide error location for invalid host properties', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + + @Component({ + selector: 'test', + template: '...', + host: { + '(click)': 'act() | pipe', + } + }) + class FooCmp {} + `); + + const errors = env.driveDiagnostics(); + expect(getDiagnosticSourceCode(errors[0])).toBe(`{ + '(click)': 'act() | pipe', + }`); + expect(errors[0].messageText).toContain('/test.ts@7:17'); + }); + it('should throw in case pipes are used in host listeners', () => { env.write(`test.ts`, ` import {Component} from '@angular/core'; @@ -3305,7 +3464,9 @@ runInEachFileSystem(os => { }); describe('ngfactory shims', () => { - beforeEach(() => { env.tsconfig({'generateNgFactoryShims': true}); }); + beforeEach(() => { + env.tsconfig({'generateNgFactoryShims': true}); + }); it('should generate correct type annotation for NgModuleFactory calls in ngfactories', () => { env.write('test.ts', ` @@ -3407,7 +3568,9 @@ runInEachFileSystem(os => { describe('ngsummary shim generation', () => { - beforeEach(() => { env.tsconfig({'generateNgSummaryShims': true}); }); + beforeEach(() => { + env.tsconfig({'generateNgSummaryShims': true}); + }); it('should generate a summary stub for decorated classes in the input file only', () => { env.write('test.ts', ` @@ -3631,7 +3794,6 @@ runInEachFileSystem(os => { }); it('should use imported types in setClassMetadata if they can be represented as values', () => { - env.write(`types.ts`, ` export class MyTypeA {} export class MyTypeB {} @@ -3663,7 +3825,6 @@ runInEachFileSystem(os => { it('should use imported types in setClassMetadata if they can be represented as values and imported as `* as foo`', () => { - env.write(`types.ts`, ` export class MyTypeA {} export class MyTypeB {} @@ -3694,7 +3855,6 @@ runInEachFileSystem(os => { }); it('should use default-imported types if they can be represented as values', () => { - env.write(`types.ts`, ` export default class Default {} export class Other {} @@ -3720,9 +3880,142 @@ runInEachFileSystem(os => { expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.Other')); }); + describe('namespace support', () => { + it('should generate correct imports for type references to namespaced symbols using a namespace import', + () => { + env.write(`/node_modules/ns/index.d.ts`, ` + export declare class Zero {} + export declare namespace one { + export declare class One {} + } + export declare namespace one.two { + export declare class Two {} + } + `); + env.write(`test.ts`, ` + import {Inject, Injectable, InjectionToken} from '@angular/core'; + import * as ns from 'ns'; + + @Injectable() + export class MyService { + constructor( + zero: ns.Zero, + one: ns.one.One, + two: ns.one.two.Two, + ) {} + } + `); + + env.driveMain(); + const jsContents = trim(env.getContents('test.js')); + expect(jsContents).toContain(`import * as i1 from "ns";`); + expect(jsContents).toContain('i0.ɵɵinject(i1.Zero)'); + expect(jsContents).toContain('i0.ɵɵinject(i1.one.One)'); + expect(jsContents).toContain('i0.ɵɵinject(i1.one.two.Two)'); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.Zero')); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.one.One')); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.one.two.Two')); + }); + + it('should generate correct imports for type references to namespaced symbols using named imports', + () => { + env.write(`/node_modules/ns/index.d.ts`, ` + export namespace ns { + export declare class Zero {} + export declare namespace one { + export declare class One {} + } + export declare namespace one.two { + export declare class Two {} + } + } + `); + env.write(`test.ts`, ` + import {Inject, Injectable, InjectionToken} from '@angular/core'; + import {ns} from 'ns'; + import {ns as alias} from 'ns'; + + @Injectable() + export class MyService { + constructor( + zero: ns.Zero, + one: ns.one.One, + two: ns.one.two.Two, + aliasedZero: alias.Zero, + aliasedOne: alias.one.One, + aliasedTwo: alias.one.two.Two, + ) {} + } + `); + + env.driveMain(); + const jsContents = trim(env.getContents('test.js')); + expect(jsContents).toContain(`import * as i1 from "ns";`); + expect(jsContents) + .toContain( + 'i0.ɵɵinject(i1.ns.Zero), ' + + 'i0.ɵɵinject(i1.ns.one.One), ' + + 'i0.ɵɵinject(i1.ns.one.two.Two), ' + + 'i0.ɵɵinject(i1.ns.Zero), ' + + 'i0.ɵɵinject(i1.ns.one.One), ' + + 'i0.ɵɵinject(i1.ns.one.two.Two)'); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.ns.Zero')); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.ns.one.One')); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.ns.one.two.Two')); + }); + + it('should not error for a namespace import as parameter type when @Inject is used', () => { + env.tsconfig({'strictInjectionParameters': true}); + env.write(`/node_modules/foo/index.d.ts`, ` + export = Foo; + declare class Foo {} + declare namespace Foo {} + `); + env.write(`test.ts`, ` + import {Inject, Injectable, InjectionToken} from '@angular/core'; + import * as Foo from 'foo'; + + export const TOKEN = new InjectionToken<Foo>('Foo'); + + @Injectable() + export class MyService { + constructor(@Inject(TOKEN) foo: Foo) {} + } + `); + + env.driveMain(); + const jsContents = trim(env.getContents('test.js')); + expect(jsContents).toContain('i0.ɵɵinject(TOKEN)'); + expect(jsContents).toMatch(setClassMetadataRegExp('type: undefined')); + }); + + it('should error for a namespace import as parameter type used for DI', () => { + env.tsconfig({'strictInjectionParameters': true}); + env.write(`/node_modules/foo/index.d.ts`, ` + export = Foo; + declare class Foo {} + declare namespace Foo {} + `); + env.write(`test.ts`, ` + import {Injectable} from '@angular/core'; + import * as Foo from 'foo'; + + @Injectable() + export class MyService { + constructor(foo: Foo) {} + } + `); + + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(diags[0].messageText) + .toBe( + `No suitable injection token for parameter 'foo' of class 'MyService'.\nFound Foo`); + }); + }); + it('should use `undefined` in setClassMetadata if types can\'t be represented as values', () => { - env.write(`types.ts`, ` export type MyType = Map<any, any>; `); @@ -3898,7 +4191,6 @@ runInEachFileSystem(os => { it('should not generate an error when a local ref is unresolved' + ' (outside of template type-checking)', () => { - env.write('test.ts', ` import {Component} from '@angular/core'; @@ -4186,7 +4478,7 @@ runInEachFileSystem(os => { } }); - it('should throw if @Component is missing a template', async() => { + it('should throw if @Component is missing a template', async () => { env.write('test.ts', ` import {Component} from '@angular/core'; @@ -4198,10 +4490,10 @@ runInEachFileSystem(os => { const diags = await driveDiagnostics(); expect(diags[0].messageText).toBe('component is missing a template'); - expect(diags[0].file !.fileName).toBe(absoluteFrom('/test.ts')); + expect(diags[0].file!.fileName).toBe(absoluteFrom('/test.ts')); }); - it('should throw if `styleUrls` is defined incorrectly in @Component', async() => { + it('should throw if `styleUrls` is defined incorrectly in @Component', async () => { env.write('test.ts', ` import {Component} from '@angular/core'; @@ -4215,7 +4507,7 @@ runInEachFileSystem(os => { const diags = await driveDiagnostics(); expect(diags[0].messageText).toBe('styleUrls must be an array of strings'); - expect(diags[0].file !.fileName).toBe(absoluteFrom('/test.ts')); + expect(diags[0].file!.fileName).toBe(absoluteFrom('/test.ts')); }); }); }); @@ -4336,7 +4628,7 @@ runInEachFileSystem(os => { // Verify that the error is for the correct class. const error = errors[0] as ts.Diagnostic; - const id = expectTokenAtPosition(error.file !, error.start !, ts.isIdentifier); + const id = expectTokenAtPosition(error.file!, error.start!, ts.isIdentifier); expect(id.text).toBe('Dir'); expect(ts.isClassDeclaration(id.parent)).toBe(true); }); @@ -4643,7 +4935,7 @@ runInEachFileSystem(os => { const diag = env.driveDiagnostics(); expect(diag.length).toBe(1); - expect(diag[0] !.code).toEqual(ngErrorCode(ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION)); + expect(diag[0]!.code).toEqual(ngErrorCode(ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION)); }); it('should not error when two directives with the same declared name are exported from the same NgModule, but one is exported from the file directly', @@ -6520,7 +6812,7 @@ export const Foo = Foo__PRE_R3__; export declare class NgZone {} export declare class Testability { - static ɵfac: i0.ɵɵFactoryDef<Testability>; + static ɵfac: i0.ɵɵFactoryDef<Testability, never>; constructor(ngZone: NgZone) {} } `); @@ -6540,9 +6832,7 @@ export const Foo = Foo__PRE_R3__; const diags = env.driveDiagnostics(); expect(diags.length).toBe(0); }); - }); - }); function expectTokenAtPosition<T extends ts.Node>( @@ -6553,5 +6843,7 @@ export const Foo = Foo__PRE_R3__; return node as T; } - function normalize(input: string): string { return input.replace(/\s+/g, ' ').trim(); } + function normalize(input: string): string { + return input.replace(/\s+/g, ' ').trim(); + } }); diff --git a/packages/compiler-cli/test/ngtsc/scope_spec.ts b/packages/compiler-cli/test/ngtsc/scope_spec.ts index 91651047060f6..904bedd75f4e9 100644 --- a/packages/compiler-cli/test/ngtsc/scope_spec.ts +++ b/packages/compiler-cli/test/ngtsc/scope_spec.ts @@ -20,7 +20,7 @@ const testFiles = loadStandardTestFiles(); runInEachFileSystem(() => { describe('ngtsc module scopes', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles); @@ -92,11 +92,11 @@ runInEachFileSystem(() => { const diags = env.driveDiagnostics(); expect(diags.length).toBe(1); const node = findContainingClass(diagnosticToNode(diags[0], ts.isIdentifier)); - expect(node.name !.text).toEqual('TestDir'); + expect(node.name!.text).toEqual('TestDir'); - const relatedNodes = new Set(diags[0].relatedInformation !.map( + const relatedNodes = new Set(diags[0].relatedInformation!.map( related => - findContainingClass(diagnosticToNode(related, ts.isIdentifier)).name !.text)); + findContainingClass(diagnosticToNode(related, ts.isIdentifier)).name!.text)); expect(relatedNodes).toContain('ModuleA'); expect(relatedNodes).toContain('ModuleB'); expect(relatedNodes.size).toBe(2); @@ -141,11 +141,11 @@ runInEachFileSystem(() => { const diags = env.driveDiagnostics(); expect(diags.length).toBe(1); const node = findContainingClass(diagnosticToNode(diags[0], ts.isIdentifier)); - expect(node.name !.text).toEqual('TestDir'); + expect(node.name!.text).toEqual('TestDir'); - const relatedNodes = new Set(diags[0].relatedInformation !.map( + const relatedNodes = new Set(diags[0].relatedInformation!.map( related => - findContainingClass(diagnosticToNode(related, ts.isIdentifier)).name !.text)); + findContainingClass(diagnosticToNode(related, ts.isIdentifier)).name!.text)); expect(relatedNodes).toContain('ModuleA'); expect(relatedNodes).toContain('ModuleB'); expect(relatedNodes.size).toBe(2); @@ -386,13 +386,13 @@ runInEachFileSystem(() => { }); function diagnosticToNode<T extends ts.Node>( - diagnostic: ts.Diagnostic | Diagnostic | ts.DiagnosticRelatedInformation, + diagnostic: ts.Diagnostic|Diagnostic|ts.DiagnosticRelatedInformation, guard: (node: ts.Node) => node is T): T { const diag = diagnostic as ts.Diagnostic | ts.DiagnosticRelatedInformation; if (diag.file === undefined) { throw new Error(`Expected ts.Diagnostic to have a file source`); } - const node = getTokenAtPosition(diag.file, diag.start !); + const node = getTokenAtPosition(diag.file, diag.start!); expect(guard(node)).toBe(true); return node as T; } diff --git a/packages/compiler-cli/test/ngtsc/sourcemap_utils.ts b/packages/compiler-cli/test/ngtsc/sourcemap_utils.ts index 2dbd7df5424f4..dff529f602872 100644 --- a/packages/compiler-cli/test/ngtsc/sourcemap_utils.ts +++ b/packages/compiler-cli/test/ngtsc/sourcemap_utils.ts @@ -85,7 +85,7 @@ export function getMappedSegments( while (currentMapping) { const nextMapping = mappings.shift(); if (nextMapping) { - const source = sources.get(currentMapping.source) !; + const source = sources.get(currentMapping.source)!; const segment = { generated: generated.getSegment('generated', currentMapping, nextMapping), source: source.getSegment('original', currentMapping, nextMapping), diff --git a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts index 5c51defba1735..aed02b33a4098 100644 --- a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts @@ -14,13 +14,13 @@ import {tsSourceMapBug29300Fixed} from '../../src/ngtsc/util/src/ts_source_map_b import {loadStandardTestFiles} from '../helpers/src/mock_file_loading'; import {NgtscTestEnvironment} from './env'; -import {SegmentMapping, getMappedSegments} from './sourcemap_utils'; +import {getMappedSegments, SegmentMapping} from './sourcemap_utils'; const testFiles = loadStandardTestFiles(); runInEachFileSystem((os) => { describe('template source-mapping', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles); @@ -323,7 +323,6 @@ runInEachFileSystem((os) => { expect(mappings).toContain( {source: '</div>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'}); - }); it('should map ng-template [ngFor] scenario', () => { @@ -518,7 +517,7 @@ runInEachFileSystem((os) => { }); } - function compileAndMap(template: string, templateUrl: string | null = null) { + function compileAndMap(template: string, templateUrl: string|null = null) { const templateConfig = templateUrl ? `templateUrl: '${templateUrl}'` : ('template: `' + template.replace(/`/g, '\\`') + '`'); env.tsconfig({sourceMap: true}); diff --git a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts index 4d3aaafd78fb2..e77209c7e03ae 100644 --- a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts @@ -19,7 +19,7 @@ const testFiles = loadStandardTestFiles(); runInEachFileSystem(() => { describe('ngtsc type checking', () => { - let env !: NgtscTestEnvironment; + let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles); @@ -1502,8 +1502,9 @@ export declare class AnimationEvent { }); describe('legacy schema checking with the DOM schema', () => { - beforeEach( - () => { env.tsconfig({ivyTemplateTypeCheck: true, fullTemplateTypeCheck: false}); }); + beforeEach(() => { + env.tsconfig({ivyTemplateTypeCheck: true, fullTemplateTypeCheck: false}); + }); it('should check for unknown elements', () => { env.write('test.ts', ` @@ -1734,7 +1735,7 @@ export declare class AnimationEvent { } }); - it('should be correct for direct templates', async() => { + it('should be correct for direct templates', async () => { env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; @@ -1750,11 +1751,11 @@ export declare class AnimationEvent { const diags = await driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].file !.fileName).toBe(_('/test.ts')); + expect(diags[0].file!.fileName).toBe(_('/test.ts')); expect(getSourceCodeForDiagnostic(diags[0])).toBe('user.does_not_exist'); }); - it('should be correct for indirect templates', async() => { + it('should be correct for indirect templates', async () => { env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; @@ -1772,12 +1773,12 @@ export declare class AnimationEvent { const diags = await driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].file !.fileName).toBe(_('/test.ts') + ' (TestCmp template)'); + expect(diags[0].file!.fileName).toBe(_('/test.ts') + ' (TestCmp template)'); expect(getSourceCodeForDiagnostic(diags[0])).toBe('user.does_not_exist'); - expect(getSourceCodeForDiagnostic(diags[0].relatedInformation ![0])).toBe('TEMPLATE'); + expect(getSourceCodeForDiagnostic(diags[0].relatedInformation![0])).toBe('TEMPLATE'); }); - it('should be correct for external templates', async() => { + it('should be correct for external templates', async () => { env.write('template.html', `<p> {{user.does_not_exist}} </p>`); @@ -1795,9 +1796,9 @@ export declare class AnimationEvent { const diags = await driveDiagnostics(); expect(diags.length).toBe(1); - expect(diags[0].file !.fileName).toBe(_('/template.html')); + expect(diags[0].file!.fileName).toBe(_('/template.html')); expect(getSourceCodeForDiagnostic(diags[0])).toBe('user.does_not_exist'); - expect(getSourceCodeForDiagnostic(diags[0].relatedInformation ![0])) + expect(getSourceCodeForDiagnostic(diags[0].relatedInformation![0])) .toBe(`'./template.html'`); }); }); @@ -1841,6 +1842,6 @@ export declare class AnimationEvent { }); function getSourceCodeForDiagnostic(diag: ts.Diagnostic): string { - const text = diag.file !.text; - return text.substr(diag.start !, diag.length !); + const text = diag.file!.text; + return text.substr(diag.start!, diag.length!); } diff --git a/packages/compiler-cli/test/perform_compile_spec.ts b/packages/compiler-cli/test/perform_compile_spec.ts index 8696ab19550f4..d3e25172165fd 100644 --- a/packages/compiler-cli/test/perform_compile_spec.ts +++ b/packages/compiler-cli/test/perform_compile_spec.ts @@ -10,7 +10,7 @@ import * as path from 'path'; import {readConfiguration} from '../src/perform_compile'; -import {TestSupport, setup} from './test_support'; +import {setup, TestSupport} from './test_support'; describe('perform_compile', () => { let support: TestSupport; diff --git a/packages/compiler-cli/test/perform_watch_spec.ts b/packages/compiler-cli/test/perform_watch_spec.ts index 1ce31d7ac6d84..6cc620ca773b6 100644 --- a/packages/compiler-cli/test/perform_watch_spec.ts +++ b/packages/compiler-cli/test/perform_watch_spec.ts @@ -14,7 +14,7 @@ import * as ts from 'typescript'; import * as ng from '../index'; import {FileChangeEvent, performWatchCompilation} from '../src/perform_watch'; -import {TestSupport, expectNoDiagnostics, setup} from './test_support'; +import {expectNoDiagnostics, setup, TestSupport} from './test_support'; describe('perform watch', () => { let testSupport: TestSupport; @@ -105,23 +105,23 @@ describe('perform watch', () => { performWatchCompilation(host); expect(fs.existsSync(mainNgFactory)).toBe(true); - expect(fileExistsSpy !).toHaveBeenCalledWith(mainTsPath); - expect(fileExistsSpy !).toHaveBeenCalledWith(utilTsPath); - expect(getSourceFileSpy !).toHaveBeenCalledWith(mainTsPath, ts.ScriptTarget.ES5); - expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5); + expect(fileExistsSpy!).toHaveBeenCalledWith(mainTsPath); + expect(fileExistsSpy!).toHaveBeenCalledWith(utilTsPath); + expect(getSourceFileSpy!).toHaveBeenCalledWith(mainTsPath, ts.ScriptTarget.ES5); + expect(getSourceFileSpy!).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5); - fileExistsSpy !.calls.reset(); - getSourceFileSpy !.calls.reset(); + fileExistsSpy!.calls.reset(); + getSourceFileSpy!.calls.reset(); // trigger a single file change // -> all other files should be cached host.triggerFileChange(FileChangeEvent.Change, utilTsPath); expectNoDiagnostics(config.options, host.diagnostics); - expect(fileExistsSpy !).not.toHaveBeenCalledWith(mainTsPath); - expect(fileExistsSpy !).toHaveBeenCalledWith(utilTsPath); - expect(getSourceFileSpy !).not.toHaveBeenCalledWith(mainTsPath, ts.ScriptTarget.ES5); - expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5); + expect(fileExistsSpy!).not.toHaveBeenCalledWith(mainTsPath); + expect(fileExistsSpy!).toHaveBeenCalledWith(utilTsPath); + expect(getSourceFileSpy!).not.toHaveBeenCalledWith(mainTsPath, ts.ScriptTarget.ES5); + expect(getSourceFileSpy!).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5); // trigger a folder change // -> nothing should be cached @@ -129,10 +129,10 @@ describe('perform watch', () => { FileChangeEvent.CreateDeleteDir, path.resolve(testSupport.basePath, 'src')); expectNoDiagnostics(config.options, host.diagnostics); - expect(fileExistsSpy !).toHaveBeenCalledWith(mainTsPath); - expect(fileExistsSpy !).toHaveBeenCalledWith(utilTsPath); - expect(getSourceFileSpy !).toHaveBeenCalledWith(mainTsPath, ts.ScriptTarget.ES5); - expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5); + expect(fileExistsSpy!).toHaveBeenCalledWith(mainTsPath); + expect(fileExistsSpy!).toHaveBeenCalledWith(utilTsPath); + expect(getSourceFileSpy!).toHaveBeenCalledWith(mainTsPath, ts.ScriptTarget.ES5); + expect(getSourceFileSpy!).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5); }); // https://github.com/angular/angular/pull/26036 @@ -239,10 +239,18 @@ class MockWatchHost { diagnostics: ng.Diagnostic[] = []; constructor(public config: ng.ParsedConfiguration) {} - reportDiagnostics(diags: ng.Diagnostics) { this.diagnostics.push(...(diags as ng.Diagnostic[])); } - readConfiguration() { return this.config; } - createCompilerHost(options: ng.CompilerOptions) { return ng.createCompilerHost({options}); } - createEmitCallback() { return undefined; } + reportDiagnostics(diags: ng.Diagnostics) { + this.diagnostics.push(...(diags as ng.Diagnostic[])); + } + readConfiguration() { + return this.config; + } + createCompilerHost(options: ng.CompilerOptions) { + return ng.createCompilerHost({options}); + } + createEmitCallback() { + return undefined; + } onFileChange( options: ng.CompilerOptions, listener: (event: FileChangeEvent, fileName: string) => void, ready: () => void) { @@ -258,7 +266,9 @@ class MockWatchHost { this.timeoutListeners[id] = callback; return id; } - clearTimeout(timeoutId: any): void { delete this.timeoutListeners[timeoutId]; } + clearTimeout(timeoutId: any): void { + delete this.timeoutListeners[timeoutId]; + } flushTimeouts() { const listeners = this.timeoutListeners; this.timeoutListeners = {}; diff --git a/packages/compiler-cli/test/test_support.ts b/packages/compiler-cli/test/test_support.ts index 84f07874afdfc..5d337d4def080 100644 --- a/packages/compiler-cli/test/test_support.ts +++ b/packages/compiler-cli/test/test_support.ts @@ -15,7 +15,7 @@ import {NodeJSFileSystem, setFileSystem} from '../src/ngtsc/file_system'; import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '../test/helpers'; // TEST_TMPDIR is always set by Bazel. -const tmpdir = process.env.TEST_TMPDIR !; +const tmpdir = process.env.TEST_TMPDIR!; export function makeTempDir(): string { let dir: string; @@ -97,8 +97,11 @@ function createTestSupportFor(basePath: string) { } function writeFiles(...mockDirs: {[fileName: string]: string}[]) { - mockDirs.forEach( - (dir) => { Object.keys(dir).forEach((fileName) => { write(fileName, dir[fileName]); }); }); + mockDirs.forEach((dir) => { + Object.keys(dir).forEach((fileName) => { + write(fileName, dir[fileName]); + }); + }); } function createCompilerOptions(overrideOptions: ng.CompilerOptions = {}): ng.CompilerOptions { diff --git a/packages/compiler-cli/test/transformers/compiler_host_spec.ts b/packages/compiler-cli/test/transformers/compiler_host_spec.ts index 9a43783b541b4..123306c2cac35 100644 --- a/packages/compiler-cli/test/transformers/compiler_host_spec.ts +++ b/packages/compiler-cli/test/transformers/compiler_host_spec.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {MetadataCollector} from '../../src/metadata/collector'; import {CompilerHost, CompilerOptions, LibrarySummary} from '../../src/transformers/api'; -import {TsCompilerAotCompilerTypeCheckHostAdapter, createCompilerHost} from '../../src/transformers/compiler_host'; +import {createCompilerHost, TsCompilerAotCompilerTypeCheckHostAdapter} from '../../src/transformers/compiler_host'; import {Directory, Entry, MockAotContext, MockCompilerHost} from '../mocks'; const dummyModule = 'export let foo: any[];'; @@ -53,12 +53,15 @@ describe('NgCompilerHost', () => { } = {}) { return new TsCompilerAotCompilerTypeCheckHostAdapter( rootNames, options, ngHost, new MetadataCollector(), codeGenerator, - new Map(librarySummaries.map(entry => [entry.fileName, entry] as[string, LibrarySummary]))); + new Map( + librarySummaries.map(entry => [entry.fileName, entry] as [string, LibrarySummary]))); } describe('fileNameToModuleName', () => { let host: TsCompilerAotCompilerTypeCheckHostAdapter; - beforeEach(() => { host = createHost(); }); + beforeEach(() => { + host = createHost(); + }); it('should use a package import when accessing a package from a source file', () => { expect(host.fileNameToModuleName('/tmp/node_modules/@angular/core.d.ts', '/tmp/main.ts')) @@ -239,9 +242,8 @@ describe('NgCompilerHost', () => { it('should not get tripped on nested node_modules', () => { const genSf = generate('/tmp/node_modules/lib1/node_modules/lib2/thing', { - 'tmp': { - 'node_modules': {'lib1': {'node_modules': {'lib2': {'thing.ts': `// some content`}}}} - } + 'tmp': + {'node_modules': {'lib1': {'node_modules': {'lib2': {'thing.ts': `// some content`}}}}} }); expect(genSf.moduleName).toBe('lib2/thing.ngfactory'); }); @@ -387,8 +389,9 @@ describe('NgCompilerHost', () => { () => host.updateGeneratedFile(new compiler.GeneratedFile( '/tmp/src/index.ts', '/tmp/src/index.ngfactory.ts', [new compiler.DeclareVarStmt( - 'x', new compiler.ExternalExpr( - new compiler.ExternalReference('otherModule', 'aName')))]))) + 'x', + new compiler.ExternalExpr( + new compiler.ExternalReference('otherModule', 'aName')))]))) .toThrowError([ `Illegal State: external references changed in /tmp/src/index.ngfactory.ts.`, `Old: aModule.`, `New: otherModule` diff --git a/packages/compiler-cli/test/transformers/inline_resources_spec.ts b/packages/compiler-cli/test/transformers/inline_resources_spec.ts index 290dbb4929bdf..3dbe49afea512 100644 --- a/packages/compiler-cli/test/transformers/inline_resources_spec.ts +++ b/packages/compiler-cli/test/transformers/inline_resources_spec.ts @@ -8,8 +8,8 @@ import * as ts from 'typescript'; -import {MetadataCollector, isClassMetadata} from '../../src/metadata/index'; -import {InlineResourcesMetadataTransformer, getInlineResourcesTransformFactory} from '../../src/transformers/inline_resources'; +import {isClassMetadata, MetadataCollector} from '../../src/metadata/index'; +import {getInlineResourcesTransformFactory, InlineResourcesMetadataTransformer} from '../../src/transformers/inline_resources'; import {MetadataCache} from '../../src/transformers/metadata_cache'; import {MockAotContext, MockCompilerHost} from '../mocks'; diff --git a/packages/compiler-cli/test/transformers/lower_expressions_spec.ts b/packages/compiler-cli/test/transformers/lower_expressions_spec.ts index da873f8f0b290..99b7e4572569d 100644 --- a/packages/compiler-cli/test/transformers/lower_expressions_spec.ts +++ b/packages/compiler-cli/test/transformers/lower_expressions_spec.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {MetadataCollector, ModuleMetadata} from '../../src/metadata/index'; -import {LowerMetadataTransform, LoweringRequest, RequestLocationMap, getExpressionLoweringTransformFactory} from '../../src/transformers/lower_expressions'; +import {getExpressionLoweringTransformFactory, LoweringRequest, LowerMetadataTransform, RequestLocationMap} from '../../src/transformers/lower_expressions'; import {MetadataCache} from '../../src/transformers/metadata_cache'; import {Directory, MockAotContext, MockCompilerHost} from '../mocks'; @@ -196,14 +196,16 @@ function convert(annotatedSource: string) { const program = ts.createProgram( [fileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host); - const moduleSourceFile = program.getSourceFile(fileName) !; + const moduleSourceFile = program.getSourceFile(fileName)!; const transformers: ts.CustomTransformers = { before: [getExpressionLoweringTransformFactory( { - getRequests(sourceFile: ts.SourceFile): RequestLocationMap{ + getRequests(sourceFile: ts.SourceFile): RequestLocationMap { if (sourceFile.fileName == moduleSourceFile.fileName) { return requests; - } else {return new Map();} + } else { + return new Map(); + } } }, program)] @@ -254,6 +256,7 @@ function collect(annotatedSource: string) { 'someName.ts', unannotatedSource, ts.ScriptTarget.Latest, /* setParentNodes */ true); return { metadata: cache.getMetadata(sourceFile), - requests: transformer.getRequests(sourceFile), annotations + requests: transformer.getRequests(sourceFile), + annotations }; } \ No newline at end of file diff --git a/packages/compiler-cli/test/transformers/metadata_reader_spec.ts b/packages/compiler-cli/test/transformers/metadata_reader_spec.ts index 306c215483d55..13437c54ecd1e 100644 --- a/packages/compiler-cli/test/transformers/metadata_reader_spec.ts +++ b/packages/compiler-cli/test/transformers/metadata_reader_spec.ts @@ -23,10 +23,9 @@ describe('metadata reader', () => { readFile: (fileName) => context.readFile(fileName), getSourceFileMetadata: (fileName) => { const sourceText = context.readFile(fileName); - return sourceText != null ? - metadataCollector.getMetadata( - ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest)) : - undefined; + return sourceText != null ? metadataCollector.getMetadata(ts.createSourceFile( + fileName, sourceText, ts.ScriptTarget.Latest)) : + undefined; }, }; }); @@ -42,11 +41,13 @@ describe('metadata reader', () => { expect(readMetadata('node_modules/@angular/unused.d.ts', host)).toEqual([dummyMetadata]); }); - it('should be able to read empty metadata ', - () => { expect(readMetadata('node_modules/@angular/empty.d.ts', host)).toEqual([]); }); + it('should be able to read empty metadata ', () => { + expect(readMetadata('node_modules/@angular/empty.d.ts', host)).toEqual([]); + }); - it('should return undefined for missing modules', - () => { expect(readMetadata('node_modules/@angular/missing.d.ts', host)).toBeUndefined(); }); + it('should return undefined for missing modules', () => { + expect(readMetadata('node_modules/@angular/missing.d.ts', host)).toBeUndefined(); + }); it(`should add missing v${METADATA_VERSION} metadata from v1 metadata and .d.ts files`, () => { expect(readMetadata('metadata_versions/v1.d.ts', host)).toEqual([ @@ -117,8 +118,8 @@ const FILES: Entry = { 'node_modules': { '@angular': { 'core.d.ts': dummyModule, - 'core.metadata.json': - `{"__symbolic":"module", "version": ${METADATA_VERSION}, "metadata": {"foo": {"__symbolic": "class"}}}`, + 'core.metadata.json': `{"__symbolic":"module", "version": ${ + METADATA_VERSION}, "metadata": {"foo": {"__symbolic": "class"}}}`, 'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}}, 'unused.d.ts': dummyModule, 'empty.d.ts': 'export declare var a: string;', diff --git a/packages/compiler-cli/test/transformers/nocollapse_hack_spec.ts b/packages/compiler-cli/test/transformers/nocollapse_hack_spec.ts deleted file mode 100644 index 38742da4399e7..0000000000000 --- a/packages/compiler-cli/test/transformers/nocollapse_hack_spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @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 {nocollapseHack} from '../../src/transformers/nocollapse_hack'; - -describe('@nocollapse hack', () => { - it('should add @nocollapse to a basic class', () => { - const decl = `Foo.ɵinj = define(...);`; - expect(nocollapseHack(decl)).toEqual('/** @nocollapse */ ' + decl); - }); - - it('should add nocollapse to an if (false) declaration of the kind generated by tsickle', () => { - const decl = ` - if (false) { - /** @type {?} */ - Foo.ɵinj; - } - `; - expect(nocollapseHack(decl)).toContain('/** @nocollapse @type {?} */'); - }); -}); diff --git a/packages/compiler-cli/test/transformers/node_emitter_spec.ts b/packages/compiler-cli/test/transformers/node_emitter_spec.ts index 9fdbe6ccbb009..0b53d4b380c22 100644 --- a/packages/compiler-cli/test/transformers/node_emitter_spec.ts +++ b/packages/compiler-cli/test/transformers/node_emitter_spec.ts @@ -33,12 +33,12 @@ describe('TypeScriptNodeEmitter', () => { beforeEach(() => { context = new MockAotContext('/', FILES); host = new MockCompilerHost(context); - emitter = new TypeScriptNodeEmitter(); + emitter = new TypeScriptNodeEmitter(false); someVar = o.variable('someVar', null, null); }); function emitStmt( - stmt: o.Statement | o.Statement[], format: Format = Format.Flat, preamble?: string): string { + stmt: o.Statement|o.Statement[], format: Format = Format.Flat, preamble?: string): string { const stmts = Array.isArray(stmt) ? stmt : [stmt]; const program = ts.createProgram( @@ -246,8 +246,8 @@ describe('TypeScriptNodeEmitter', () => { expect(emitStmt(new o.DeclareFunctionStmt( 'someFn', [], [new o.ReturnStatement(o.literal(1))], o.INT_TYPE))) .toEqual(`function someFn() { return 1; }`); - expect(emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1', o.INT_TYPE)], [ - ]))).toEqual(`function someFn(param1) { }`); + expect(emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1', o.INT_TYPE)], []))) + .toEqual(`function someFn(param1) { }`); }); describe('comments', () => { @@ -256,8 +256,9 @@ describe('TypeScriptNodeEmitter', () => { .toBe('/* SomePreamble */ a;'); }); - it('should support singleline comments', - () => { expect(emitStmt(new o.CommentStmt('Simple comment'))).toBe('// Simple comment'); }); + it('should support singleline comments', () => { + expect(emitStmt(new o.CommentStmt('Simple comment'))).toBe('// Simple comment'); + }); it('should support multiline comments', () => { expect(emitStmt(new o.CommentStmt('Multiline comment', true))) @@ -314,86 +315,90 @@ describe('TypeScriptNodeEmitter', () => { `try { body(); } catch (error) { var stack = error.stack; catchFn(error, stack); }`); }); - it('should support support throwing', - () => { expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); }); + it('should support support throwing', () => { + expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); + }); describe('classes', () => { let callSomeMethod: o.Statement; - beforeEach(() => { callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); }); + beforeEach(() => { + callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); + }); it('should support declaring classes', () => { - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ - ]))).toEqual('class SomeClass { }'); - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [], [ + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, []))) + .toEqual('class SomeClass { }'); + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, [], [ o.StmtModifier.Exported ]))).toEqual('class SomeClass { } exports.SomeClass = SomeClass;'); - expect(emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null !, [ - ]))).toEqual('class SomeClass extends SomeSuperClass { }'); + expect( + emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null!, []))) + .toEqual('class SomeClass extends SomeSuperClass { }'); }); it('should support declaring constructors', () => { const superCall = o.SUPER_EXPR.callFn([o.variable('someParam')]).toStmt(); - expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], new o.ClassMethod(null !, [], []), []))) + expect(emitStmt( + new o.ClassStmt('SomeClass', null!, [], [], new o.ClassMethod(null!, [], []), []))) .toEqual(`class SomeClass { constructor() { } }`); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], - new o.ClassMethod(null !, [new o.FnParam('someParam', o.INT_TYPE)], []), []))) + 'SomeClass', null!, [], [], + new o.ClassMethod(null!, [new o.FnParam('someParam', o.INT_TYPE)], []), []))) .toEqual(`class SomeClass { constructor(someParam) { } }`); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], new o.ClassMethod(null !, [], [superCall]), []))) + 'SomeClass', null!, [], [], new o.ClassMethod(null!, [], [superCall]), []))) .toEqual(`class SomeClass { constructor() { super(someParam); } }`); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], new o.ClassMethod(null !, [], [callSomeMethod]), []))) + 'SomeClass', null!, [], [], new o.ClassMethod(null!, [], [callSomeMethod]), []))) .toEqual(`class SomeClass { constructor() { this.someMethod(); } }`); }); it('should support declaring fields', () => { expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [new o.ClassField('someField')], [], null !, []))) + 'SomeClass', null!, [new o.ClassField('someField')], [], null!, []))) .toEqual(`class SomeClass { constructor() { this.someField = null; } }`); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [new o.ClassField('someField', o.INT_TYPE)], [], null !, []))) + 'SomeClass', null!, [new o.ClassField('someField', o.INT_TYPE)], [], null!, []))) .toEqual(`class SomeClass { constructor() { this.someField = null; } }`); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, - [new o.ClassField('someField', o.INT_TYPE, [o.StmtModifier.Private])], [], null !, + 'SomeClass', null!, + [new o.ClassField('someField', o.INT_TYPE, [o.StmtModifier.Private])], [], null!, []))) .toEqual(`class SomeClass { constructor() { this.someField = null; } }`); }); it('should support declaring getters', () => { expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [new o.ClassGetter('someGetter', [])], null !, []))) + 'SomeClass', null!, [], [new o.ClassGetter('someGetter', [])], null!, []))) .toEqual(`class SomeClass { get someGetter() { } }`); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [new o.ClassGetter('someGetter', [], o.INT_TYPE)], null !, + 'SomeClass', null!, [], [new o.ClassGetter('someGetter', [], o.INT_TYPE)], null!, []))) .toEqual(`class SomeClass { get someGetter() { } }`); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [new o.ClassGetter('someGetter', [callSomeMethod])], - null !, []))) + 'SomeClass', null!, [], [new o.ClassGetter('someGetter', [callSomeMethod])], null!, + []))) .toEqual(`class SomeClass { get someGetter() { this.someMethod(); } }`); expect( emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], - [new o.ClassGetter('someGetter', [], null !, [o.StmtModifier.Private])], null !, []))) + 'SomeClass', null!, [], + [new o.ClassGetter('someGetter', [], null!, [o.StmtModifier.Private])], null!, []))) .toEqual(`class SomeClass { get someGetter() { } }`); }); it('should support methods', () => { - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, [ new o.ClassMethod('someMethod', [], []) ]))).toEqual(`class SomeClass { someMethod() { } }`); - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, [ new o.ClassMethod('someMethod', [], [], o.INT_TYPE) ]))).toEqual(`class SomeClass { someMethod() { } }`); - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, [ new o.ClassMethod('someMethod', [new o.FnParam('someParam', o.INT_TYPE)], []) ]))).toEqual(`class SomeClass { someMethod(someParam) { } }`); - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, [ new o.ClassMethod('someMethod', [], [callSomeMethod]) ]))).toEqual(`class SomeClass { someMethod() { this.someMethod(); } }`); }); @@ -431,7 +436,7 @@ describe('TypeScriptNodeEmitter', () => { it('should support combined types', () => { const writeVarExpr = o.variable('a').set(o.NULL_EXPR); - expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(null !)))).toEqual('var a = null;'); + expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(null!)))).toEqual('var a = null;'); expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(o.INT_TYPE)))).toEqual('var a = null;'); expect(emitStmt(writeVarExpr.toDeclStmt(new o.MapType(null)))).toEqual('var a = null;'); @@ -439,7 +444,7 @@ describe('TypeScriptNodeEmitter', () => { }); describe('source maps', () => { - function emitStmt(stmt: o.Statement | o.Statement[], preamble?: string): string { + function emitStmt(stmt: o.Statement|o.Statement[], preamble?: string): string { const stmts = Array.isArray(stmt) ? stmt : [stmt]; const program = ts.createProgram( @@ -473,13 +478,15 @@ describe('TypeScriptNodeEmitter', () => { function mappingItemsOf(text: string) { // find the source map: const sourceMapMatch = /sourceMappingURL\=data\:application\/json;base64,(.*)$/.exec(text); - const sourceMapBase64 = sourceMapMatch ![1]; + const sourceMapBase64 = sourceMapMatch![1]; const sourceMapBuffer = Buffer.from(sourceMapBase64, 'base64'); const sourceMapText = sourceMapBuffer.toString('utf8'); const sourceMapParsed = JSON.parse(sourceMapText); const consumer = new sourceMap.SourceMapConsumer(sourceMapParsed); const mappings: any[] = []; - consumer.eachMapping((mapping: any) => { mappings.push(mapping); }); + consumer.eachMapping((mapping: any) => { + mappings.push(mapping); + }); return mappings; } @@ -503,7 +510,7 @@ describe('TypeScriptNodeEmitter', () => { generatedColumn: 0, originalLine: 1, originalColumn: 0, - name: null ! // TODO: Review use of `!` here (#19904) + name: null! // TODO: Review use of `!` here (#19904) }, { source: sourceUrl, @@ -511,7 +518,7 @@ describe('TypeScriptNodeEmitter', () => { generatedColumn: 16, originalLine: 1, originalColumn: 26, - name: null ! // TODO: Review use of `!` here (#19904) + name: null! // TODO: Review use of `!` here (#19904) } ]); }); @@ -558,7 +565,10 @@ const FILES: Directory = { somePackage: {'someGenFile.ts': `export var a: number;`} }; -const enum Format { Raw, Flat } +const enum Format { + Raw, + Flat +} function normalizeResult(result: string, format: Format): string { // Remove TypeScript prefixes diff --git a/packages/compiler-cli/test/transformers/program_spec.ts b/packages/compiler-cli/test/transformers/program_spec.ts index a7013540050b1..8f3dc5ccdd34a 100644 --- a/packages/compiler-cli/test/transformers/program_spec.ts +++ b/packages/compiler-cli/test/transformers/program_spec.ts @@ -10,11 +10,12 @@ import * as ng from '@angular/compiler-cli'; import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; + import {formatDiagnostics} from '../../src/perform_compile'; import {CompilerHost, EmitFlags, LazyRoute} from '../../src/transformers/api'; import {createSrcToOutPathMapper} from '../../src/transformers/program'; import {StructureIsReused, tsStructureIsReused} from '../../src/transformers/util'; -import {TestSupport, expectNoDiagnosticsInProgram, setup, stripAnsi} from '../test_support'; +import {expectNoDiagnosticsInProgram, setup, stripAnsi, TestSupport} from '../test_support'; describe('ng program', () => { let testSupport: TestSupport; @@ -83,21 +84,21 @@ describe('ng program', () => { const originalGetSourceFile = host.getSourceFile; const cache = new Map<string, ts.SourceFile>(); - host.getSourceFile = function( - fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile | - undefined { - const sf = originalGetSourceFile.call(host, fileName, languageVersion); - if (sf) { - if (cache.has(sf.fileName)) { - const oldSf = cache.get(sf.fileName) !; - if (oldSf.getFullText() === sf.getFullText()) { - return oldSf; - } - } - cache.set(sf.fileName, sf); - } - return sf; - }; + host.getSourceFile = function(fileName: string, languageVersion: ts.ScriptTarget): + ts.SourceFile| + undefined { + const sf = originalGetSourceFile.call(host, fileName, languageVersion); + if (sf) { + if (cache.has(sf.fileName)) { + const oldSf = cache.get(sf.fileName)!; + if (oldSf.getFullText() === sf.getFullText()) { + return oldSf; + } + } + cache.set(sf.fileName, sf); + } + return sf; + }; return host; } @@ -248,7 +249,8 @@ describe('ng program', () => { fileCache.delete(path.posix.join(testSupport.basePath, 'src/index.ts')); const p6 = ng.createProgram({ rootNames: [path.posix.join(testSupport.basePath, 'src/index.ts')], - options: testSupport.createCompilerOptions(options), host, + options: testSupport.createCompilerOptions(options), + host, oldProgram: p5 }); const p7 = compile(p6, options, undefined, host).program; @@ -295,7 +297,6 @@ describe('ng program', () => { describe( 'verify that program structure is reused within tsc in order to speed up incremental compilation', () => { - it('should reuse the old ts program completely if nothing changed', () => { testSupport.writeFiles({'src/index.ts': createModuleAndCompSource('main')}); const host = createWatchModeHost(); @@ -351,7 +352,6 @@ describe('ng program', () => { expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.SafeModules); }); }); - }); it('should not typecheck templates if skipTemplateCodegen is set but fullTemplateTypeCheck is not', @@ -473,7 +473,7 @@ describe('ng program', () => { host.writeFile = (fileName: string, data: string, writeByteOrderMark: boolean, - onError: ((message: string) => void) | undefined, + onError: ((message: string) => void)|undefined, sourceFiles?: ReadonlyArray<ts.SourceFile>) => { written.set(fileName, {original: sourceFiles, data}); }; @@ -487,19 +487,19 @@ describe('ng program', () => { const writeData = written.get(path.posix.join(testSupport.basePath, fileName)); expect(writeData).toBeTruthy(); expect( - writeData !.original !.some( + writeData!.original!.some( sf => sf.fileName === path.posix.join(testSupport.basePath, checks.originalFileName))) .toBe(true); switch (checks.shouldBe) { case ShouldBe.Empty: - expect(writeData !.data).toMatch(/^(\s*\/\*([^*]|\*[^/])*\*\/\s*)?$/); + expect(writeData!.data).toMatch(/^(\s*\/\*([^*]|\*[^/])*\*\/\s*)?$/); break; case ShouldBe.EmptyExport: - expect(writeData !.data) + expect(writeData!.data) .toMatch(/^((\s*\/\*([^*]|\*[^/])*\*\/\s*)|(\s*export\s*{\s*}\s*;\s*)|())$/); break; case ShouldBe.NoneEmpty: - expect(writeData !.data).not.toBe(''); + expect(writeData!.data).not.toBe(''); break; } } @@ -1099,15 +1099,15 @@ describe('ng program', () => { }); const host = ng.createCompilerHost({options}); const originalGetSourceFile = host.getSourceFile; - host.getSourceFile = (fileName: string, languageVersion: ts.ScriptTarget, - onError?: ((message: string) => void) | undefined): ts.SourceFile | - undefined => { - // We should never try to load .ngfactory.ts files - if (fileName.match(/\.ngfactory\.ts$/)) { - throw new Error(`Non existent ngfactory file: ` + fileName); - } - return originalGetSourceFile.call(host, fileName, languageVersion, onError); - }; + host.getSourceFile = + (fileName: string, languageVersion: ts.ScriptTarget, + onError?: ((message: string) => void)|undefined): ts.SourceFile|undefined => { + // We should never try to load .ngfactory.ts files + if (fileName.match(/\.ngfactory\.ts$/)) { + throw new Error(`Non existent ngfactory file: ` + fileName); + } + return originalGetSourceFile.call(host, fileName, languageVersion, onError); + }; const program = ng.createProgram({rootNames: allRootNames, options, host}); const structuralErrors = program.getNgStructuralDiagnostics(); expect(structuralErrors.length).toBe(1); @@ -1115,5 +1115,4 @@ describe('ng program', () => { .toContain('Function expressions are not supported'); }); }); - }); diff --git a/packages/compiler-cli/test/transformers/r3_metadata_transform_spec.ts b/packages/compiler-cli/test/transformers/r3_metadata_transform_spec.ts index 47b1d23dfb23b..7ab230c0e615a 100644 --- a/packages/compiler-cli/test/transformers/r3_metadata_transform_spec.ts +++ b/packages/compiler-cli/test/transformers/r3_metadata_transform_spec.ts @@ -9,12 +9,11 @@ import {ClassField, ClassMethod, ClassStmt, PartialModule, Statement, StmtModifier} from '@angular/compiler'; import * as ts from 'typescript'; -import {MetadataCollector, isClassMetadata} from '../../src/metadata/index'; +import {isClassMetadata, MetadataCollector} from '../../src/metadata/index'; import {MetadataCache} from '../../src/transformers/metadata_cache'; import {PartialModuleMetadataTransformer} from '../../src/transformers/r3_metadata_transform'; describe('r3_transform_spec', () => { - it('should add a static method to collected metadata', () => { const fileName = '/some/directory/someFileName.ts'; const className = 'SomeClass'; diff --git a/packages/compiler-cli/test/transformers/r3_transform_spec.ts b/packages/compiler-cli/test/transformers/r3_transform_spec.ts index 992165f0867fb..adf93e26b68ca 100644 --- a/packages/compiler-cli/test/transformers/r3_transform_spec.ts +++ b/packages/compiler-cli/test/transformers/r3_transform_spec.ts @@ -30,8 +30,9 @@ describe('r3_transform_spec', () => { .toContain('static someMethod(v) { return v; }'); }); - it('should be able to generate a static field declaration', - () => { expect(emitStaticField(o.literal(10))).toContain('SomeClass.someField = 10'); }); + it('should be able to generate a static field declaration', () => { + expect(emitStaticField(o.literal(10))).toContain('SomeClass.someField = 10'); + }); it('should be able to import a symbol', () => { expect(emitStaticMethod(new o.ReturnStatement( @@ -40,14 +41,16 @@ describe('r3_transform_spec', () => { }); it('should be able to modify multiple classes in the same module', () => { - const result = emit(getAngularClassTransformerFactory([{ - fileName: someGenFileName, - statements: [ - classMethod(new o.ReturnStatement(o.variable('v')), ['v'], 'someMethod', 'SomeClass'), - classMethod( - new o.ReturnStatement(o.variable('v')), ['v'], 'someOtherMethod', 'SomeOtherClass') - ] - }])); + const result = emit(getAngularClassTransformerFactory( + [{ + fileName: someGenFileName, + statements: [ + classMethod(new o.ReturnStatement(o.variable('v')), ['v'], 'someMethod', 'SomeClass'), + classMethod( + new o.ReturnStatement(o.variable('v')), ['v'], 'someOtherMethod', 'SomeOtherClass') + ] + }], + false)); expect(result).toContain('static someMethod(v) { return v; }'); expect(result).toContain('static someOtherMethod(v) { return v; }'); }); @@ -88,13 +91,13 @@ describe('r3_transform_spec', () => { } function emitStaticMethod( - stmt: o.Statement | o.Statement[], parameters: string[] = [], - methodName: string = 'someMethod', className: string = 'SomeClass'): string { + stmt: o.Statement|o.Statement[], parameters: string[] = [], methodName: string = 'someMethod', + className: string = 'SomeClass'): string { const module: PartialModule = { fileName: someGenFileName, statements: [classMethod(stmt, parameters, methodName, className)] }; - return emit(getAngularClassTransformerFactory([module])); + return emit(getAngularClassTransformerFactory([module], false)); } function emitStaticField( @@ -104,7 +107,7 @@ describe('r3_transform_spec', () => { fileName: someGenFileName, statements: [classField(initializer, fieldName, className)] }; - return emit(getAngularClassTransformerFactory([module])); + return emit(getAngularClassTransformerFactory([module], false)); } }); @@ -120,7 +123,7 @@ const FILES: Directory = { }; function classMethod( - stmt: o.Statement | o.Statement[], parameters: string[] = [], methodName: string = 'someMethod', + stmt: o.Statement|o.Statement[], parameters: string[] = [], methodName: string = 'someMethod', className: string = 'SomeClass'): o.ClassStmt { const statements = Array.isArray(stmt) ? stmt : [stmt]; return new o.ClassStmt( diff --git a/packages/compiler-cli/test/typescript_support_spec.ts b/packages/compiler-cli/test/typescript_support_spec.ts index 6a6c03c34ad04..0995ac5238f9c 100644 --- a/packages/compiler-cli/test/typescript_support_spec.ts +++ b/packages/compiler-cli/test/typescript_support_spec.ts @@ -11,8 +11,8 @@ describe('checkVersion', () => { const MIN_TS_VERSION = '2.7.2'; const MAX_TS_VERSION = '2.8.0'; - const versionError = (version: string) => - `The Angular Compiler requires TypeScript >=${MIN_TS_VERSION} and <${MAX_TS_VERSION} but ${version} was found instead.`; + const versionError = (version: string) => `The Angular Compiler requires TypeScript >=${ + MIN_TS_VERSION} and <${MAX_TS_VERSION} but ${version} was found instead.`; it('should not throw when a supported TypeScript version is used', () => { expect(() => checkVersion('2.7.2', MIN_TS_VERSION, MAX_TS_VERSION)).not.toThrow(); diff --git a/packages/compiler/src/aot/compiler.ts b/packages/compiler/src/aot/compiler.ts index 27e08eeb9698f..24ea205e9e11f 100644 --- a/packages/compiler/src/aot/compiler.ts +++ b/packages/compiler/src/aot/compiler.ts @@ -11,7 +11,7 @@ import {CompilerConfig} from '../config'; import {ConstantPool} from '../constant_pool'; import {ViewEncapsulation} from '../core'; import {MessageBundle} from '../i18n/message_bundle'; -import {Identifiers, createTokenForExternalReference} from '../identifiers'; +import {createTokenForExternalReference, Identifiers} from '../identifiers'; import {InjectableCompiler} from '../injectable_compiler'; import {CompileMetadataResolver} from '../metadata_resolver'; import {HtmlParser} from '../ml_parser/html_parser'; @@ -31,9 +31,9 @@ import {SummaryResolver} from '../summary_resolver'; import {BindingParser} from '../template_parser/binding_parser'; import {TemplateAst} from '../template_parser/template_ast'; import {TemplateParser} from '../template_parser/template_parser'; -import {OutputContext, ValueVisitor, error, newArray, syntaxError, visitValue} from '../util'; +import {error, newArray, OutputContext, syntaxError, ValueVisitor, visitValue} from '../util'; import {TypeCheckCompiler} from '../view_compiler/type_check_compiler'; -import {ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler'; +import {ViewCompiler, ViewCompileResult} from '../view_compiler/view_compiler'; import {AotCompilerHost} from './compiler_host'; import {AotCompilerOptions} from './compiler_options'; @@ -46,7 +46,11 @@ import {StaticSymbolResolver} from './static_symbol_resolver'; import {createForJitStub, serializeSummaries} from './summary_serializer'; import {ngfactoryFilePath, normalizeGenFileSuffix, splitTypescriptSuffix, summaryFileName, summaryForJitFileName} from './util'; -const enum StubEmitFlags { Basic = 1 << 0, TypeCheck = 1 << 1, All = TypeCheck | Basic } +const enum StubEmitFlags { + Basic = 1 << 0, + TypeCheck = 1 << 1, + All = TypeCheck | Basic +} export class AotCompiler { private _templateAstCache = @@ -64,7 +68,9 @@ export class AotCompiler { private _summaryResolver: SummaryResolver<StaticSymbol>, private _symbolResolver: StaticSymbolResolver) {} - clearCache() { this._metadataResolver.clearCache(); } + clearCache() { + this._metadataResolver.clearCache(); + } analyzeModulesSync(rootFiles: string[]): NgAnalyzedModules { const analyzeResult = analyzeAndValidateNgModules( @@ -123,7 +129,7 @@ export class AotCompiler { const fileSuffix = normalizeGenFileSuffix(splitTypescriptSuffix(file.fileName, true)[1]); file.directives.forEach((dirSymbol) => { const compMeta = - this._metadataResolver.getNonNormalizedDirectiveMetadata(dirSymbol) !.metadata; + this._metadataResolver.getNonNormalizedDirectiveMetadata(dirSymbol)!.metadata; if (!compMeta.isComponent) { return; } @@ -149,7 +155,8 @@ export class AotCompiler { if (genFileName.endsWith('.ngfactory.ts')) { if (!originalFileName) { throw new Error( - `Assertion error: require the original file for .ngfactory.ts stubs. File: ${genFileName}`); + `Assertion error: require the original file for .ngfactory.ts stubs. File: ${ + genFileName}`); } const originalFile = this._analyzeFile(originalFileName); this._createNgFactoryStub(outputCtx, originalFile, StubEmitFlags.Basic); @@ -157,7 +164,8 @@ export class AotCompiler { if (this._options.enableSummariesForJit) { if (!originalFileName) { throw new Error( - `Assertion error: require the original file for .ngsummary.ts stubs. File: ${genFileName}`); + `Assertion error: require the original file for .ngsummary.ts stubs. File: ${ + genFileName}`); } const originalFile = this._analyzeFile(originalFileName); _createEmptyStub(outputCtx); @@ -319,10 +327,10 @@ export class AotCompiler { const html = compMeta.template !.template !; // Template URL points to either an HTML or TS file depending on whether // the file is used with `templateUrl:` or `template:`, respectively. - const templateUrl = compMeta.template !.templateUrl !; + const templateUrl = compMeta.template !.templateUrl!; const interpolationConfig = InterpolationConfig.fromArray(compMeta.template !.interpolation); - errors.push(...messageBundle.updateFromTemplate(html, templateUrl, interpolationConfig) !); + errors.push(...messageBundle.updateFromTemplate(html, templateUrl, interpolationConfig)!); }); }); @@ -342,7 +350,7 @@ export class AotCompiler { if (!contextMap.has(fileName)) { contextMap.set(fileName, this._createOutputContext(fileName)); } - return contextMap.get(fileName) !; + return contextMap.get(fileName)!; }; files.forEach( @@ -381,13 +389,13 @@ export class AotCompiler { directives.forEach(directiveType => { const directiveMetadata = this._metadataResolver.getDirectiveMetadata(directiveType); if (directiveMetadata.isComponent) { - const module = ngModuleByPipeOrDirective.get(directiveType) !; + const module = ngModuleByPipeOrDirective.get(directiveType)!; module || - error( - `Cannot determine the module for component '${identifierName(directiveMetadata.type)}'`); + error(`Cannot determine the module for component '${ + identifierName(directiveMetadata.type)}'`); - let htmlAst = directiveMetadata.template !.htmlAst !; - const preserveWhitespaces = directiveMetadata !.template !.preserveWhitespaces; + let htmlAst = directiveMetadata.template !.htmlAst!; + const preserveWhitespaces = directiveMetadata!.template !.preserveWhitespaces; if (!preserveWhitespaces) { htmlAst = removeWhitespaces(htmlAst); @@ -412,7 +420,9 @@ export class AotCompiler { const pipes = module.transitiveModule.pipes.map( pipe => this._metadataResolver.getPipeSummary(pipe.reference)); - pipes.forEach(pipe => { pipeTypeByName.set(pipe.name, pipe.type.reference); }); + pipes.forEach(pipe => { + pipeTypeByName.set(pipe.name, pipe.type.reference); + }); compileR3Component( context, directiveMetadata, render3Ast, this.reflector, hostBindingParser, @@ -484,8 +494,8 @@ export class AotCompiler { } const ngModule = ngModuleByPipeOrDirective.get(dirType); if (!ngModule) { - throw new Error( - `Internal Error: cannot determine the module for component ${identifierName(compMeta.type)}!`); + throw new Error(`Internal Error: cannot determine the module for component ${ + identifierName(compMeta.type)}!`); } // compile styles @@ -524,27 +534,27 @@ export class AotCompiler { .map(symbol => this._symbolResolver.resolveSymbol(symbol)); const typeData: { summary: CompileTypeSummary, - metadata: CompileNgModuleMetadata | CompileDirectiveMetadata | CompilePipeMetadata | - CompileTypeMetadata + metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|CompilePipeMetadata| + CompileTypeMetadata }[] = [ ...ngModules.map( meta => ({ - summary: this._metadataResolver.getNgModuleSummary(meta.type.reference) !, - metadata: this._metadataResolver.getNgModuleMetadata(meta.type.reference) ! + summary: this._metadataResolver.getNgModuleSummary(meta.type.reference)!, + metadata: this._metadataResolver.getNgModuleMetadata(meta.type.reference)! })), ...directives.map(ref => ({ - summary: this._metadataResolver.getDirectiveSummary(ref) !, - metadata: this._metadataResolver.getDirectiveMetadata(ref) ! + summary: this._metadataResolver.getDirectiveSummary(ref)!, + metadata: this._metadataResolver.getDirectiveMetadata(ref)! })), ...pipes.map(ref => ({ - summary: this._metadataResolver.getPipeSummary(ref) !, - metadata: this._metadataResolver.getPipeMetadata(ref) ! + summary: this._metadataResolver.getPipeSummary(ref)!, + metadata: this._metadataResolver.getPipeMetadata(ref)! })), ...injectables.map( ref => ({ - summary: this._metadataResolver.getInjectableSummary(ref.symbol) !, - metadata: this._metadataResolver.getInjectableSummary(ref.symbol) !.type + summary: this._metadataResolver.getInjectableSummary(ref.symbol)!, + metadata: this._metadataResolver.getInjectableSummary(ref.symbol)!.type })) ]; const forJitOutputCtx = this._options.enableSummariesForJit ? @@ -621,7 +631,7 @@ export class AotCompiler { .toDeclStmt( o.importType( Identifiers.ComponentFactory, - [o.expressionType(outputCtx.importExpr(compMeta.type.reference)) !], + [o.expressionType(outputCtx.importExpr(compMeta.type.reference))!], [o.TypeModifier.Const]), [o.StmtModifier.Final, o.StmtModifier.Exported])); } @@ -648,15 +658,15 @@ export class AotCompiler { directiveIdentifiers: CompileIdentifierMetadata[]): {template: TemplateAst[], pipes: CompilePipeSummary[]} { if (this._templateAstCache.has(compMeta.type.reference)) { - return this._templateAstCache.get(compMeta.type.reference) !; + return this._templateAstCache.get(compMeta.type.reference)!; } - const preserveWhitespaces = compMeta !.template !.preserveWhitespaces; + const preserveWhitespaces = compMeta!.template !.preserveWhitespaces; const directives = directiveIdentifiers.map(dir => this._metadataResolver.getDirectiveSummary(dir.reference)); const pipes = ngModule.transitiveModule.pipes.map( pipe => this._metadataResolver.getPipeSummary(pipe.reference)); const result = this._templateParser.parse( - compMeta, compMeta.template !.htmlAst !, directives, pipes, ngModule.schemas, + compMeta, compMeta.template !.htmlAst!, directives, pipes, ngModule.schemas, templateSourceUrl(ngModule.type, compMeta, compMeta.template !), preserveWhitespaces); this._templateAstCache.set(compMeta.type.reference, result); return result; @@ -664,8 +674,7 @@ export class AotCompiler { private _createOutputContext(genFilePath: string): OutputContext { const importExpr = - (symbol: StaticSymbol, typeParams: o.Type[] | null = null, - useSummaries: boolean = true) => { + (symbol: StaticSymbol, typeParams: o.Type[]|null = null, useSummaries: boolean = true) => { if (!(symbol instanceof StaticSymbol)) { throw new Error(`Internal error: unknown identifier ${JSON.stringify(symbol)}`); } @@ -710,7 +719,7 @@ export class AotCompiler { stylesheetMetadata: CompileStylesheetMetadata, isShimmed: boolean, fileSuffix: string): GeneratedFile { const outputCtx = this._createOutputContext( - _stylesModuleUrl(stylesheetMetadata.moduleUrl !, isShimmed, fileSuffix)); + _stylesModuleUrl(stylesheetMetadata.moduleUrl!, isShimmed, fileSuffix)); const compiledStylesheet = this._styleCompiler.compileStyles(outputCtx, compMeta, stylesheetMetadata, isShimmed); _resolveStyleStatements(this._symbolResolver, compiledStylesheet, isShimmed, fileSuffix); @@ -748,8 +757,8 @@ export class AotCompiler { return allLazyRoutes; } seenRoutes.add(symbol); - const lazyRoutes = listLazyRoutes( - self._metadataResolver.getNgModuleMetadata(symbol, true) !, self.reflector); + const lazyRoutes = + listLazyRoutes(self._metadataResolver.getNgModuleMetadata(symbol, true)!, self.reflector); for (const lazyRoute of lazyRoutes) { allLazyRoutes.push(lazyRoute); visitLazyRoute(lazyRoute.referencedModule, seenRoutes, allLazyRoutes); @@ -803,7 +812,9 @@ export interface NgAnalyzedFile { exportsNonSourceFiles: boolean; } -export interface NgAnalyzeModulesHost { isSourceFile(filePath: string): boolean; } +export interface NgAnalyzeModulesHost { + isSourceFile(filePath: string): boolean; +} export function analyzeNgModules( fileNames: string[], host: NgAnalyzeModulesHost, staticSymbolResolver: StaticSymbolResolver, @@ -823,8 +834,8 @@ export function analyzeAndValidateNgModules( function validateAnalyzedModules(analyzedModules: NgAnalyzedModules): NgAnalyzedModules { if (analyzedModules.symbolsMissingModule && analyzedModules.symbolsMissingModule.length) { const messages = analyzedModules.symbolsMissingModule.map( - s => - `Cannot determine the module for class ${s.name} in ${s.filePath}! Add ${s.name} to the NgModule to fix it.`); + s => `Cannot determine the module for class ${s.name} in ${s.filePath}! Add ${ + s.name} to the NgModule to fix it.`); throw syntaxError(messages.join('\n')); } return analyzedModules; @@ -918,8 +929,13 @@ export function analyzeFile( }); } return { - fileName, directives, abstractDirectives, pipes, - ngModules, injectables, exportsNonSourceFiles, + fileName, + directives, + abstractDirectives, + pipes, + ngModules, + injectables, + exportsNonSourceFiles, }; } @@ -957,7 +973,9 @@ function isValueExportingNonSourceFile(host: NgAnalyzeModulesHost, metadata: any let exportsNonSourceFiles = false; class Visitor implements ValueVisitor { - visitArray(arr: any[], context: any): any { arr.forEach(v => visitValue(v, this, context)); } + visitArray(arr: any[], context: any): any { + arr.forEach(v => visitValue(v, this, context)); + } visitStringMap(map: {[key: string]: any}, context: any): any { Object.keys(map).forEach((key) => visitValue(map[key], this, context)); } diff --git a/packages/compiler/src/aot/compiler_factory.ts b/packages/compiler/src/aot/compiler_factory.ts index 6122764f43844..e6cdaeaad9c94 100644 --- a/packages/compiler/src/aot/compiler_factory.ts +++ b/packages/compiler/src/aot/compiler_factory.ts @@ -36,9 +36,9 @@ import {StaticSymbol, StaticSymbolCache} from './static_symbol'; import {StaticSymbolResolver} from './static_symbol_resolver'; import {AotSummaryResolver} from './summary_resolver'; -export function createAotUrlResolver(host: { - resourceNameToFileName(resourceName: string, containingFileName: string): string | null; -}): UrlResolver { +export function createAotUrlResolver( + host: {resourceNameToFileName(resourceName: string, containingFileName: string): string|null;}): + UrlResolver { return { resolve: (basePath: string, url: string) => { const filePath = host.resourceNameToFileName(url, basePath); diff --git a/packages/compiler/src/aot/formatted_error.ts b/packages/compiler/src/aot/formatted_error.ts index b99e0f2f289ab..2f47a6c98caaa 100644 --- a/packages/compiler/src/aot/formatted_error.ts +++ b/packages/compiler/src/aot/formatted_error.ts @@ -20,7 +20,7 @@ export interface FormattedMessageChain { next?: FormattedMessageChain[]; } -export type FormattedError = Error & { +export type FormattedError = Error&{ chain: FormattedMessageChain; position?: Position; }; @@ -34,10 +34,10 @@ function indentStr(level: number): string { return half + half + (level % 2 === 1 ? ' ' : ''); } -function formatChain(chain: FormattedMessageChain | undefined, indent: number = 0): string { +function formatChain(chain: FormattedMessageChain|undefined, indent: number = 0): string { if (!chain) return ''; const position = chain.position ? - `${chain.position.fileName}(${chain.position.line+1},${chain.position.column+1})` : + `${chain.position.fileName}(${chain.position.line + 1},${chain.position.column + 1})` : ''; const prefix = position && indent === 0 ? `${position}: ` : ''; const postfix = position && indent !== 0 ? ` at ${position}` : ''; diff --git a/packages/compiler/src/aot/generated_file.ts b/packages/compiler/src/aot/generated_file.ts index 0973fbaa60fca..ff1fa07cc2b6b 100644 --- a/packages/compiler/src/aot/generated_file.ts +++ b/packages/compiler/src/aot/generated_file.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Statement, areAllEquivalent} from '../output/output_ast'; +import {areAllEquivalent, Statement} from '../output/output_ast'; import {TypeScriptEmitter} from '../output/ts_emitter'; export class GeneratedFile { @@ -36,7 +36,7 @@ export class GeneratedFile { } // Note: the constructor guarantees that if this.source is not filled, // then this.stmts is. - return areAllEquivalent(this.stmts !, other.stmts !); + return areAllEquivalent(this.stmts!, other.stmts!); } } diff --git a/packages/compiler/src/aot/lazy_routes.ts b/packages/compiler/src/aot/lazy_routes.ts index ab32b34d1ca52..9351518315368 100644 --- a/packages/compiler/src/aot/lazy_routes.ts +++ b/packages/compiler/src/aot/lazy_routes.ts @@ -34,7 +34,7 @@ export function listLazyRoutes( return allLazyRoutes; } -function _collectLoadChildren(routes: string | Route | Route[], target: string[] = []): string[] { +function _collectLoadChildren(routes: string|Route|Route[], target: string[] = []): string[] { if (typeof routes === 'string') { target.push(routes); } else if (Array.isArray(routes)) { diff --git a/packages/compiler/src/aot/static_reflector.ts b/packages/compiler/src/aot/static_reflector.ts index 6b6008ab49353..c3d4d5459744a 100644 --- a/packages/compiler/src/aot/static_reflector.ts +++ b/packages/compiler/src/aot/static_reflector.ts @@ -8,12 +8,12 @@ import {CompileSummaryKind} from '../compile_metadata'; import {CompileReflector} from '../compile_reflector'; -import {MetadataFactory, createAttribute, createComponent, createContentChild, createContentChildren, createDirective, createHost, createHostBinding, createHostListener, createInject, createInjectable, createInput, createNgModule, createOptional, createOutput, createPipe, createSelf, createSkipSelf, createViewChild, createViewChildren} from '../core'; +import {createAttribute, createComponent, createContentChild, createContentChildren, createDirective, createHost, createHostBinding, createHostListener, createInject, createInjectable, createInput, createNgModule, createOptional, createOutput, createPipe, createSelf, createSkipSelf, createViewChild, createViewChildren, MetadataFactory} from '../core'; import * as o from '../output/output_ast'; import {SummaryResolver} from '../summary_resolver'; import {syntaxError} from '../util'; -import {FormattedMessageChain, formattedError} from './formatted_error'; +import {formattedError, FormattedMessageChain} from './formatted_error'; import {StaticSymbol} from './static_symbol'; import {StaticSymbolResolver} from './static_symbol_resolver'; @@ -50,13 +50,13 @@ export class StaticReflector implements CompileReflector { private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>(); private resolvedExternalReferences = new Map<string, StaticSymbol>(); // TODO(issue/24571): remove '!'. - private injectionToken !: StaticSymbol; + private injectionToken!: StaticSymbol; // TODO(issue/24571): remove '!'. - private opaqueToken !: StaticSymbol; + private opaqueToken!: StaticSymbol; // TODO(issue/24571): remove '!'. - ROUTES !: StaticSymbol; + ROUTES!: StaticSymbol; // TODO(issue/24571): remove '!'. - private ANALYZE_FOR_ENTRY_COMPONENTS !: StaticSymbol; + private ANALYZE_FOR_ENTRY_COMPONENTS!: StaticSymbol; private annotationForParentClassWithSummaryKind = new Map<CompileSummaryKind, MetadataFactory<any>[]>(); @@ -110,10 +110,10 @@ export class StaticReflector implements CompileReflector { if (declarationSymbol) return declarationSymbol; } const refSymbol = - this.symbolResolver.getSymbolByModule(ref.moduleName !, ref.name !, containingFile); + this.symbolResolver.getSymbolByModule(ref.moduleName!, ref.name!, containingFile); const declarationSymbol = this.findSymbolDeclaration(refSymbol); if (!containingFile) { - this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName !); + this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName!); this.symbolResolver.recordImportAs(declarationSymbol, refSymbol); } if (key) { @@ -192,16 +192,20 @@ export class StaticReflector implements CompileReflector { const summary = this.summaryResolver.resolveSummary(parentType); if (summary && summary.type) { const requiredAnnotationTypes = - this.annotationForParentClassWithSummaryKind.get(summary.type.summaryKind !) !; + this.annotationForParentClassWithSummaryKind.get(summary.type.summaryKind!)!; const typeHasRequiredAnnotation = requiredAnnotationTypes.some( (requiredType) => ownAnnotations.some(ann => requiredType.isTypeOf(ann))); if (!typeHasRequiredAnnotation) { this.reportError( formatMetadataError( metadataError( - `Class ${type.name} in ${type.filePath} extends from a ${CompileSummaryKind[summary.type.summaryKind!]} in another compilation unit without duplicating the decorator`, + `Class ${type.name} in ${type.filePath} extends from a ${ + CompileSummaryKind[summary.type.summaryKind! + ]} in another compilation unit without duplicating the decorator`, /* summary */ undefined, - `Please add a ${requiredAnnotationTypes.map((type) => type.ngMetadataName).join(' or ')} decorator to the class`), + `Please add a ${ + requiredAnnotationTypes.map((type) => type.ngMetadataName) + .join(' or ')} decorator to the class`), type), type); } @@ -221,7 +225,7 @@ export class StaticReflector implements CompileReflector { if (parentType) { const parentPropMetadata = this.propMetadata(parentType); Object.keys(parentPropMetadata).forEach((parentProp) => { - propMetadata ![parentProp] = parentPropMetadata[parentProp]; + propMetadata![parentProp] = parentPropMetadata[parentProp]; }); } @@ -231,10 +235,10 @@ export class StaticReflector implements CompileReflector { const prop = (<any[]>propData) .find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method'); const decorators: any[] = []; - if (propMetadata ![propName]) { - decorators.push(...propMetadata ![propName]); + if (propMetadata![propName]) { + decorators.push(...propMetadata![propName]); } - propMetadata ![propName] = decorators; + propMetadata![propName] = decorators; if (prop && prop['decorators']) { decorators.push(...this.simplify(type, prop['decorators'])); } @@ -271,7 +275,7 @@ export class StaticReflector implements CompileReflector { if (decorators) { nestedResult.push(...decorators); } - parameters !.push(nestedResult); + parameters!.push(nestedResult); }); } else if (parentType) { parameters = this.parameters(parentType); @@ -297,7 +301,7 @@ export class StaticReflector implements CompileReflector { if (parentType) { const parentMethodNames = this._methodNames(parentType); Object.keys(parentMethodNames).forEach((parentProp) => { - methodNames ![parentProp] = parentMethodNames[parentProp]; + methodNames![parentProp] = parentMethodNames[parentProp]; }); } @@ -305,7 +309,7 @@ export class StaticReflector implements CompileReflector { Object.keys(members).forEach((propName) => { const propData = members[propName]; const isMethod = (<any[]>propData).some(a => a['__symbolic'] == 'method'); - methodNames ![propName] = methodNames ![propName] || isMethod; + methodNames![propName] = methodNames![propName] || isMethod; }); this.methodCache.set(type, methodNames); } @@ -485,7 +489,7 @@ export class StaticReflector implements CompileReflector { // Propagate the message text up but add a message to the chain that explains how we got // here. // e.chain implies e.symbol - const summaryMsg = e.chain ? 'references \'' + e.symbol !.name + '\'' : errorSummary(e); + const summaryMsg = e.chain ? 'references \'' + e.symbol!.name + '\'' : errorSummary(e); const summary = `'${nestedContext.name}' ${summaryMsg}`; const chain = {message: summary, position: e.position, next: e.chain}; // TODO(chuckj): retrieve the position information indirectly from the collectors node @@ -494,7 +498,8 @@ export class StaticReflector implements CompileReflector { { message: e.message, advise: e.advise, - context: e.context, chain, + context: e.context, + chain, symbol: nestedContext }, context); @@ -566,7 +571,8 @@ export class StaticReflector implements CompileReflector { { message: FUNCTION_CALL_NOT_SUPPORTED, context: functionSymbol, - value: targetFunction, position + value: targetFunction, + position }, context); } @@ -875,7 +881,7 @@ interface MetadataMessageChain { next?: MetadataMessageChain; } -type MetadataError = Error & { +type MetadataError = Error&{ position?: Position; advise?: string; summary?: string; @@ -916,7 +922,8 @@ function expandedMessage(message: string, context: any): string { switch (message) { case REFERENCE_TO_NONEXPORTED_CLASS: if (context && context.className) { - return `References to a non-exported class are not supported in decorators but ${context.className} was referenced.`; + return `References to a non-exported class are not supported in decorators but ${ + context.className} was referenced.`; } break; case VARIABLE_NOT_INITIALIZED: @@ -935,7 +942,8 @@ function expandedMessage(message: string, context: any): string { return 'Function calls are not supported in decorators'; case REFERENCE_TO_LOCAL_SYMBOL: if (context && context.name) { - return `Reference to a local (non-exported) symbols are not supported in decorators but '${context.name}' was referenced`; + return `Reference to a local (non-exported) symbols are not supported in decorators but '${ + context.name}' was referenced`; } break; case LAMBDA_NOT_SUPPORTED: @@ -1040,7 +1048,9 @@ abstract class BindingScope { } class PopulatedScope extends BindingScope { - constructor(private bindings: Map<string, any>) { super(); } + constructor(private bindings: Map<string, any>) { + super(); + } resolve(name: string): any { return this.bindings.has(name) ? this.bindings.get(name) : BindingScope.missing; @@ -1048,7 +1058,7 @@ class PopulatedScope extends BindingScope { } function formatMetadataMessageChain( - chain: MetadataMessageChain, advise: string | undefined): FormattedMessageChain { + chain: MetadataMessageChain, advise: string|undefined): FormattedMessageChain { const expanded = expandedMessage(chain.message, chain.context); const nesting = chain.symbol ? ` in '${chain.symbol.name}'` : ''; const message = `${expanded}${nesting}`; diff --git a/packages/compiler/src/aot/static_symbol.ts b/packages/compiler/src/aot/static_symbol.ts index cb47dd9e67f42..aaec3f0f2d0e3 100644 --- a/packages/compiler/src/aot/static_symbol.ts +++ b/packages/compiler/src/aot/static_symbol.ts @@ -31,7 +31,7 @@ export class StaticSymbolCache { get(declarationFile: string, name: string, members?: string[]): StaticSymbol { members = members || []; - const memberSuffix = members.length ? `.${ members.join('.')}` : ''; + const memberSuffix = members.length ? `.${members.join('.')}` : ''; const key = `"${declarationFile}".${name}${memberSuffix}`; let result = this.cache.get(key); if (!result) { diff --git a/packages/compiler/src/aot/static_symbol_resolver.ts b/packages/compiler/src/aot/static_symbol_resolver.ts index 8ec20dcd2ac13..de5db1f246d40 100644 --- a/packages/compiler/src/aot/static_symbol_resolver.ts +++ b/packages/compiler/src/aot/static_symbol_resolver.ts @@ -76,12 +76,12 @@ export class StaticSymbolResolver { resolveSymbol(staticSymbol: StaticSymbol): ResolvedStaticSymbol { if (staticSymbol.members.length > 0) { - return this._resolveSymbolMembers(staticSymbol) !; + return this._resolveSymbolMembers(staticSymbol)!; } // Note: always ask for a summary first, // as we might have read shallow metadata via a .d.ts file // for the symbol. - const resultFromSummary = this._resolveSymbolFromSummary(staticSymbol) !; + const resultFromSummary = this._resolveSymbolFromSummary(staticSymbol)!; if (resultFromSummary) { return resultFromSummary; } @@ -93,7 +93,7 @@ export class StaticSymbolResolver { // have summaries, only .d.ts files. So we always need to check both, the summary // and metadata. this._createSymbolsOf(staticSymbol.filePath); - return this.resolvedSymbols.get(staticSymbol) !; + return this.resolvedSymbols.get(staticSymbol)!; } /** @@ -119,15 +119,14 @@ export class StaticSymbolResolver { const baseSymbol = this.getStaticSymbol(summarizedFileName, summarizedName, staticSymbol.members); const baseImportAs = this.getImportAs(baseSymbol, useSummaries); - return baseImportAs ? - this.getStaticSymbol( - summaryForJitFileName(baseImportAs.filePath), summaryForJitName(baseImportAs.name), - baseSymbol.members) : - null; + return baseImportAs ? this.getStaticSymbol( + summaryForJitFileName(baseImportAs.filePath), + summaryForJitName(baseImportAs.name), baseSymbol.members) : + null; } let result = (useSummaries && this.summaryResolver.getImportAs(staticSymbol)) || null; if (!result) { - result = this.importAs.get(staticSymbol) !; + result = this.importAs.get(staticSymbol)!; } return result; } @@ -347,8 +346,8 @@ export class StaticSymbolResolver { // correctly. const originFilePath = this.resolveModule(origin, filePath); if (!originFilePath) { - this.reportError(new Error( - `Couldn't resolve original symbol for ${origin} from ${this.host.getOutputName(filePath)}`)); + this.reportError(new Error(`Couldn't resolve original symbol for ${origin} from ${ + this.host.getOutputName(filePath)}`)); } else { this.symbolResourcePaths.set(symbol, originFilePath); } @@ -413,7 +412,7 @@ export class StaticSymbolResolver { } let filePath: string; if (module) { - filePath = self.resolveModule(module, sourceSymbol.filePath) !; + filePath = self.resolveModule(module, sourceSymbol.filePath)!; if (!filePath) { return { __symbolic: 'error', @@ -501,8 +500,11 @@ export class StaticSymbolResolver { } if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) { const errorMessage = moduleMetadata['version'] == 2 ? - `Unsupported metadata version ${moduleMetadata['version']} for module ${module}. This module should be compiled with a newer version of ngc` : - `Metadata version mismatch for module ${this.host.getOutputName(module)}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`; + `Unsupported metadata version ${moduleMetadata['version']} for module ${ + module}. This module should be compiled with a newer version of ngc` : + `Metadata version mismatch for module ${ + this.host.getOutputName(module)}, found version ${ + moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`; this.reportError(new Error(errorMessage)); } this.metadataCache.set(module, moduleMetadata); @@ -514,9 +516,8 @@ export class StaticSymbolResolver { getSymbolByModule(module: string, symbolName: string, containingFile?: string): StaticSymbol { const filePath = this.resolveModule(module, containingFile); if (!filePath) { - this.reportError( - new Error(`Could not resolve module ${module}${containingFile ? ' relative to ' + - this.host.getOutputName(containingFile) : ''}`)); + this.reportError(new Error(`Could not resolve module ${module}${ + containingFile ? ' relative to ' + this.host.getOutputName(containingFile) : ''}`)); return this.getStaticSymbol(`ERROR:${module}`, symbolName); } return this.getStaticSymbol(filePath, symbolName); diff --git a/packages/compiler/src/aot/summary_resolver.ts b/packages/compiler/src/aot/summary_resolver.ts index 0f2c1f855eeb4..9d795b287b44b 100644 --- a/packages/compiler/src/aot/summary_resolver.ts +++ b/packages/compiler/src/aot/summary_resolver.ts @@ -71,7 +71,7 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> { let summary = this.summaryCache.get(rootSymbol); if (!summary) { this._loadSummaryFile(staticSymbol.filePath); - summary = this.summaryCache.get(staticSymbol) !; + summary = this.summaryCache.get(staticSymbol)!; } return (rootSymbol === staticSymbol && summary) || null; } @@ -85,7 +85,7 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> { getImportAs(staticSymbol: StaticSymbol): StaticSymbol { staticSymbol.assertNoMembers(); - return this.importAs.get(staticSymbol) !; + return this.importAs.get(staticSymbol)!; } /** @@ -95,7 +95,9 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> { return this.knownFileNameToModuleNames.get(importedFilePath) || null; } - addSummary(summary: Summary<StaticSymbol>) { this.summaryCache.set(summary.symbol, summary); } + addSummary(summary: Summary<StaticSymbol>) { + this.summaryCache.set(summary.symbol, summary); + } private _loadSummaryFile(filePath: string): boolean { let hasSummary = this.loadedFilePaths.get(filePath); @@ -121,7 +123,9 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> { if (moduleName) { this.knownFileNameToModuleNames.set(filePath, moduleName); } - importAs.forEach((importAs) => { this.importAs.set(importAs.symbol, importAs.importAs); }); + importAs.forEach((importAs) => { + this.importAs.set(importAs.symbol, importAs.importAs); + }); } return hasSummary; } diff --git a/packages/compiler/src/aot/summary_serializer.ts b/packages/compiler/src/aot/summary_serializer.ts index fbc7941491e4e..8157d3d2281aa 100644 --- a/packages/compiler/src/aot/summary_serializer.ts +++ b/packages/compiler/src/aot/summary_serializer.ts @@ -15,12 +15,12 @@ import {ResolvedStaticSymbol, StaticSymbolResolver, unwrapResolvedMetadata} from import {isLoweredSymbol, ngfactoryFilePath, summaryForJitFileName, summaryForJitName} from './util'; export function serializeSummaries( - srcFileName: string, forJitCtx: OutputContext | null, + srcFileName: string, forJitCtx: OutputContext|null, summaryResolver: SummaryResolver<StaticSymbol>, symbolResolver: StaticSymbolResolver, symbols: ResolvedStaticSymbol[], types: { summary: CompileTypeSummary, - metadata: CompileNgModuleMetadata | CompileDirectiveMetadata | CompilePipeMetadata | - CompileTypeMetadata + metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|CompilePipeMetadata| + CompileTypeMetadata }[], createExternalSymbolReexports = false): {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} { @@ -41,7 +41,9 @@ export function serializeSummaries( const {json, exportAs} = toJsonSerializer.serialize(createExternalSymbolReexports); if (forJitCtx) { const forJitSerializer = new ForJitSerializer(forJitCtx, symbolResolver, summaryResolver); - types.forEach(({summary, metadata}) => { forJitSerializer.addSourceType(summary, metadata); }); + types.forEach(({summary, metadata}) => { + forJitSerializer.addSourceType(summary, metadata); + }); toJsonSerializer.unprocessedSymbolSummariesBySymbol.forEach((summary) => { if (summaryResolver.isLibraryFile(summary.symbol.filePath) && summary.type) { forJitSerializer.addLibType(summary.type); @@ -55,7 +57,7 @@ export function serializeSummaries( export function deserializeSummaries( symbolCache: StaticSymbolCache, summaryResolver: SummaryResolver<StaticSymbol>, libraryFileName: string, json: string): { - moduleName: string | null, + moduleName: string|null, summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] } { @@ -144,7 +146,7 @@ class ToJsonSerializer extends ValueTransformer { processedSummary.metadata = this.processValue(metadata, SerializationFlags.ResolveValue); if (metadata instanceof StaticSymbol && this.summaryResolver.isLibraryFile(metadata.filePath)) { - const declarationSymbol = this.symbols[this.indexBySymbol.get(metadata) !]; + const declarationSymbol = this.symbols[this.indexBySymbol.get(metadata)!]; if (!isLoweredSymbol(declarationSymbol.name)) { // Note: symbols that were introduced during codegen in the user file can have a reexport // if a user used `export *`. However, we can't rely on this as tsickle will change @@ -194,7 +196,7 @@ class ToJsonSerializer extends ValueTransformer { summaries: this.processedSummaries, symbols: this.symbols.map((symbol, index) => { symbol.assertNoMembers(); - let importAs: string|number = undefined !; + let importAs: string|number = undefined!; if (this.summaryResolver.isLibraryFile(symbol.filePath)) { const reexportSymbol = this.reexportedBy.get(symbol); if (reexportSymbol) { @@ -202,7 +204,7 @@ class ToJsonSerializer extends ValueTransformer { // user, we just proxy the external static symbol reference to the manual export. // This ensures that the AOT compiler imports the external symbol through the // user export and does not introduce another dependency which is not needed. - importAs = this.indexBySymbol.get(reexportSymbol) !; + importAs = this.indexBySymbol.get(reexportSymbol)!; } else if (createExternalSymbolReexports) { // In this case, the given external static symbol is *not* manually exported by // the user, and we manually create a re-export in the factory file so that we @@ -270,7 +272,7 @@ class ToJsonSerializer extends ValueTransformer { if (this.unprocessedSymbolSummariesBySymbol.has(baseSymbol)) { // the summary for this symbol was already added // -> nothing to do. - return index !; + return index!; } summary = this.loadSummary(baseSymbol); if (summary && summary.metadata instanceof StaticSymbol) { @@ -324,8 +326,9 @@ class ForJitSerializer { private summaryResolver: SummaryResolver<StaticSymbol>) {} addSourceType( - summary: CompileTypeSummary, metadata: CompileNgModuleMetadata|CompileDirectiveMetadata| - CompilePipeMetadata|CompileTypeMetadata) { + summary: CompileTypeSummary, + metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|CompilePipeMetadata| + CompileTypeMetadata) { this.data.push({summary, metadata, isLibrary: false}); } @@ -356,7 +359,7 @@ class ForJitSerializer { const fnName = summaryForJitName(summary.type.reference.name); createSummaryForJitFunction( this.outputCtx, summary.type.reference, - this.serializeSummaryWithDeps(summary, metadata !)); + this.serializeSummaryWithDeps(summary, metadata!)); } } @@ -372,8 +375,9 @@ class ForJitSerializer { } private serializeSummaryWithDeps( - summary: CompileTypeSummary, metadata: CompileNgModuleMetadata|CompileDirectiveMetadata| - CompilePipeMetadata|CompileTypeMetadata): o.Expression { + summary: CompileTypeSummary, + metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|CompilePipeMetadata| + CompileTypeMetadata): o.Expression { const expressions: o.Expression[] = [this.serializeSummary(summary)]; let providers: CompileProviderMetadata[] = []; if (metadata instanceof CompileNgModuleMetadata) { @@ -403,7 +407,8 @@ class ForJitSerializer { // i.e. we didn't generate .ngsummary.ts files for these. expressions.push( ...providers.filter(provider => !!provider.useClass).map(provider => this.serializeSummary({ - summaryKind: CompileSummaryKind.Injectable, type: provider.useClass + summaryKind: CompileSummaryKind.Injectable, + type: provider.useClass } as CompileTypeSummary))); return o.literalArr(expressions); } @@ -425,7 +430,9 @@ class ForJitSerializer { return new o.LiteralMapExpr(Object.keys(map).map( (key) => new o.LiteralMapEntry(key, visitValue(map[key], this, context), false))); } - visitPrimitive(value: any, context: any): any { return o.literal(value); } + visitPrimitive(value: any, context: any): any { + return o.literal(value); + } visitOther(value: any, context: any): any { if (value instanceof StaticSymbol) { return outputCtx.importExpr(value); @@ -441,7 +448,7 @@ class ForJitSerializer { class FromJsonDeserializer extends ValueTransformer { // TODO(issue/24571): remove '!'. - private symbols !: StaticSymbol[]; + private symbols!: StaticSymbol[]; constructor( private symbolCache: StaticSymbolCache, @@ -450,11 +457,11 @@ class FromJsonDeserializer extends ValueTransformer { } deserialize(libraryFileName: string, json: string): { - moduleName: string | null, + moduleName: string|null, summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] } { - const data: {moduleName: string | null, summaries: any[], symbols: any[]} = JSON.parse(json); + const data: {moduleName: string|null, summaries: any[], symbols: any[]} = JSON.parse(json); const allImportAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] = []; this.symbols = data.symbols.map( (serializedSymbol) => this.symbolCache.get( diff --git a/packages/compiler/src/ast_path.ts b/packages/compiler/src/ast_path.ts index cb019a47ebdb8..2d1e3a95b1a41 100644 --- a/packages/compiler/src/ast_path.ts +++ b/packages/compiler/src/ast_path.ts @@ -26,23 +26,35 @@ export class AstPath<T> { constructor(private path: T[], public position: number = -1) {} - get empty(): boolean { return !this.path || !this.path.length; } - get head(): T|undefined { return this.path[0]; } - get tail(): T|undefined { return this.path[this.path.length - 1]; } + get empty(): boolean { + return !this.path || !this.path.length; + } + get head(): T|undefined { + return this.path[0]; + } + get tail(): T|undefined { + return this.path[this.path.length - 1]; + } parentOf(node: T|undefined): T|undefined { return node && this.path[this.path.indexOf(node) - 1]; } - childOf(node: T): T|undefined { return this.path[this.path.indexOf(node) + 1]; } + childOf(node: T): T|undefined { + return this.path[this.path.indexOf(node) + 1]; + } - first<N extends T>(ctor: {new (...args: any[]): N}): N|undefined { + first<N extends T>(ctor: {new(...args: any[]): N}): N|undefined { for (let i = this.path.length - 1; i >= 0; i--) { let item = this.path[i]; if (item instanceof ctor) return <N>item; } } - push(node: T) { this.path.push(node); } + push(node: T) { + this.path.push(node); + } - pop(): T { return this.path.pop() !; } + pop(): T { + return this.path.pop()!; + } } diff --git a/packages/compiler/src/compile_metadata.ts b/packages/compiler/src/compile_metadata.ts index 7d8b3be1f6960..adba967933bc6 100644 --- a/packages/compiler/src/compile_metadata.ts +++ b/packages/compiler/src/compile_metadata.ts @@ -24,8 +24,8 @@ export function sanitizeIdentifier(name: string): string { let _anonymousTypeIndex = 0; -export function identifierName(compileIdentifier: CompileIdentifierMetadata | null | undefined): - string|null { +export function identifierName(compileIdentifier: CompileIdentifierMetadata|null|undefined): string| + null { if (!compileIdentifier || !compileIdentifier.reference) { return null; } @@ -72,9 +72,13 @@ export function componentFactoryName(compType: any): string { return `${identifierName({reference: compType})}NgFactory`; } -export interface ProxyClass { setDelegate(delegate: any): void; } +export interface ProxyClass { + setDelegate(delegate: any): void; +} -export interface CompileIdentifierMetadata { reference: any; } +export interface CompileIdentifierMetadata { + reference: any; +} export enum CompileSummaryKind { Pipe, @@ -175,8 +179,8 @@ export class CompileStylesheetMetadata { styles: string[]; styleUrls: string[]; constructor( - {moduleUrl, styles, - styleUrls}: {moduleUrl?: string, styles?: string[], styleUrls?: string[]} = {}) { + {moduleUrl, styles, styleUrls}: + {moduleUrl?: string, styles?: string[], styleUrls?: string[]} = {}) { this.moduleUrl = moduleUrl || null; this.styles = _normalizeArray(styles); this.styleUrls = _normalizeArray(styleUrls); @@ -209,10 +213,21 @@ export class CompileTemplateMetadata { ngContentSelectors: string[]; interpolation: [string, string]|null; preserveWhitespaces: boolean; - constructor({encapsulation, template, templateUrl, htmlAst, styles, styleUrls, - externalStylesheets, animations, ngContentSelectors, interpolation, isInline, - preserveWhitespaces}: { - encapsulation: ViewEncapsulation | null, + constructor({ + encapsulation, + template, + templateUrl, + htmlAst, + styles, + styleUrls, + externalStylesheets, + animations, + ngContentSelectors, + interpolation, + isInline, + preserveWhitespaces + }: { + encapsulation: ViewEncapsulation|null, template: string|null, templateUrl: string|null, htmlAst: HtmlParseTreeResult|null, @@ -286,9 +301,27 @@ export interface CompileDirectiveSummary extends CompileTypeSummary { * Metadata regarding compilation of a directive. */ export class CompileDirectiveMetadata { - static create({isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, - host, providers, viewProviders, queries, guards, viewQueries, entryComponents, - template, componentViewType, rendererType, componentFactory}: { + static create({ + isHost, + type, + isComponent, + selector, + exportAs, + changeDetection, + inputs, + outputs, + host, + providers, + viewProviders, + queries, + guards, + viewQueries, + entryComponents, + template, + componentViewType, + rendererType, + componentFactory + }: { isHost: boolean, type: CompileTypeMetadata, isComponent: boolean, @@ -347,7 +380,10 @@ export class CompileDirectiveMetadata { return new CompileDirectiveMetadata({ isHost, type, - isComponent: !!isComponent, selector, exportAs, changeDetection, + isComponent: !!isComponent, + selector, + exportAs, + changeDetection, inputs: inputsMap, outputs: outputsMap, hostListeners, @@ -389,27 +425,29 @@ export class CompileDirectiveMetadata { rendererType: StaticSymbol|object|null; componentFactory: StaticSymbol|object|null; - constructor({isHost, - type, - isComponent, - selector, - exportAs, - changeDetection, - inputs, - outputs, - hostListeners, - hostProperties, - hostAttributes, - providers, - viewProviders, - queries, - guards, - viewQueries, - entryComponents, - template, - componentViewType, - rendererType, - componentFactory}: { + constructor({ + isHost, + type, + isComponent, + selector, + exportAs, + changeDetection, + inputs, + outputs, + hostListeners, + hostProperties, + hostAttributes, + providers, + viewProviders, + queries, + guards, + viewQueries, + entryComponents, + template, + componentViewType, + rendererType, + componentFactory + }: { isHost: boolean, type: CompileTypeMetadata, isComponent: boolean, @@ -534,7 +572,7 @@ export interface CompileNgModuleSummary extends CompileTypeSummary { export class CompileShallowModuleMetadata { // TODO(issue/24571): remove '!'. - type !: CompileTypeMetadata; + type!: CompileTypeMetadata; rawExports: any; rawImports: any; @@ -562,9 +600,21 @@ export class CompileNgModuleMetadata { transitiveModule: TransitiveCompileNgModuleMetadata; - constructor({type, providers, declaredDirectives, exportedDirectives, declaredPipes, - exportedPipes, entryComponents, bootstrapComponents, importedModules, - exportedModules, schemas, transitiveModule, id}: { + constructor({ + type, + providers, + declaredDirectives, + exportedDirectives, + declaredPipes, + exportedPipes, + entryComponents, + bootstrapComponents, + importedModules, + exportedModules, + schemas, + transitiveModule, + id + }: { type: CompileTypeMetadata, providers: CompileProviderMetadata[], declaredDirectives: CompileIdentifierMetadata[], @@ -595,7 +645,7 @@ export class CompileNgModuleMetadata { } toSummary(): CompileNgModuleSummary { - const module = this.transitiveModule !; + const module = this.transitiveModule!; return { summaryKind: CompileSummaryKind.NgModule, type: this.type, @@ -666,7 +716,7 @@ export class TransitiveCompileNgModuleMetadata { } } -function _normalizeArray(obj: any[] | undefined | null): any[] { +function _normalizeArray(obj: any[]|undefined|null): any[] { return obj || []; } @@ -698,7 +748,7 @@ export class ProviderMeta { } export function flatten<T>(list: Array<T|T[]>): T[] { - return list.reduce((flat: any[], item: T | T[]): T[] => { + return list.reduce((flat: any[], item: T|T[]): T[] => { const flatItem = Array.isArray(item) ? flatten(item) : item; return (<T[]>flat).concat(flatItem); }, []); @@ -712,7 +762,7 @@ function jitSourceUrl(url: string) { export function templateSourceUrl( ngModuleType: CompileIdentifierMetadata, compMeta: {type: CompileIdentifierMetadata}, - templateMeta: {isInline: boolean, templateUrl: string | null}) { + templateMeta: {isInline: boolean, templateUrl: string|null}) { let url: string; if (templateMeta.isInline) { if (compMeta.type.reference instanceof StaticSymbol) { @@ -723,13 +773,13 @@ export function templateSourceUrl( url = `${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.html`; } } else { - url = templateMeta.templateUrl !; + url = templateMeta.templateUrl!; } return compMeta.type.reference instanceof StaticSymbol ? url : jitSourceUrl(url); } export function sharedStylesheetJitUrl(meta: CompileStylesheetMetadata, id: number) { - const pathParts = meta.moduleUrl !.split(/\/\\/g); + const pathParts = meta.moduleUrl!.split(/\/\\/g); const baseName = pathParts[pathParts.length - 1]; return jitSourceUrl(`css/${id}${baseName}.ngstyle.js`); } diff --git a/packages/compiler/src/compiler_facade_interface.ts b/packages/compiler/src/compiler_facade_interface.ts index 79d06d41c1147..e563344f23f6a 100644 --- a/packages/compiler/src/compiler_facade_interface.ts +++ b/packages/compiler/src/compiler_facade_interface.ts @@ -22,7 +22,9 @@ * ``` */ -export interface ExportedCompilerFacade { ɵcompilerFacade: CompilerFacade; } +export interface ExportedCompilerFacade { + ɵcompilerFacade: CompilerFacade; +} export interface CompilerFacade { compilePipe(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3PipeMetadataFacade): @@ -44,13 +46,15 @@ export interface CompilerFacade { R3ResolvedDependencyType: typeof R3ResolvedDependencyType; R3FactoryTarget: typeof R3FactoryTarget; - ResourceLoader: {new (): ResourceLoader}; + ResourceLoader: {new(): ResourceLoader}; } -export interface CoreEnvironment { [name: string]: Function; } +export interface CoreEnvironment { + [name: string]: Function; +} export type ResourceLoader = { - get(url: string): Promise<string>| string; + get(url: string): Promise<string>|string; }; export type StringMap = { @@ -58,7 +62,7 @@ export type StringMap = { }; export type StringMapWithRename = { - [key: string]: string | [string, string]; + [key: string]: string|[string, string]; }; export type Provider = any; diff --git a/packages/compiler/src/compiler_util/expression_converter.ts b/packages/compiler/src/compiler_util/expression_converter.ts index b446f6a7501f8..dd7c741d8086b 100644 --- a/packages/compiler/src/compiler_util/expression_converter.ts +++ b/packages/compiler/src/compiler_util/expression_converter.ts @@ -11,7 +11,9 @@ import {Identifiers} from '../identifiers'; import * as o from '../output/output_ast'; import {ParseSourceSpan} from '../parse_util'; -export class EventHandlerVars { static event = o.variable('$event'); } +export class EventHandlerVars { + static event = o.variable('$event'); +} export interface LocalResolver { getLocal(name: string): o.Expression|null; @@ -68,7 +70,7 @@ export type InterpolationFunction = (args: o.Expression[]) => o.Expression; * used in an action binding (e.g. an event handler). */ export function convertActionBinding( - localResolver: LocalResolver | null, implicitReceiver: o.Expression, action: cdAst.AST, + localResolver: LocalResolver|null, implicitReceiver: o.Expression, action: cdAst.AST, bindingId: string, interpolationFunction?: InterpolationFunction, baseSourceSpan?: ParseSourceSpan, implicitReceiverAccesses?: Set<string>): ConvertActionBindingResult { @@ -110,7 +112,7 @@ export function convertActionBinding( } const lastIndex = actionStmts.length - 1; - let preventDefaultVar: o.ReadVarExpr = null !; + let preventDefaultVar: o.ReadVarExpr = null!; if (lastIndex >= 0) { const lastStatement = actionStmts[lastIndex]; const returnExpr = convertStmtIntoExpression(lastStatement); @@ -126,7 +128,9 @@ export function convertActionBinding( return new ConvertActionBindingResult(actionStmts, preventDefaultVar); } -export interface BuiltinConverter { (args: o.Expression[]): o.Expression; } +export interface BuiltinConverter { + (args: o.Expression[]): o.Expression; +} export interface BuiltinConverterFactory { createLiteralArrayConverter(argCount: number): BuiltinConverter; @@ -158,7 +162,7 @@ export enum BindingForm { * `convertPropertyBindingBuiltins`. */ export function convertPropertyBinding( - localResolver: LocalResolver | null, implicitReceiver: o.Expression, + localResolver: LocalResolver|null, implicitReceiver: o.Expression, expressionWithoutBuiltins: cdAst.AST, bindingId: string, form: BindingForm, interpolationFunction?: InterpolationFunction): ConvertPropertyBindingResult { if (!localResolver) { @@ -284,7 +288,9 @@ function convertToStatementIfNeeded(mode: _Mode, expr: o.Expression): o.Expressi } class _BuiltinAstConverter extends cdAst.AstTransformer { - constructor(private _converterFactory: BuiltinConverterFactory) { super(); } + constructor(private _converterFactory: BuiltinConverterFactory) { + super(); + } visitPipe(ast: cdAst.BindingPipe, context: any): any { const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context)); return new BuiltinFunctionCall( @@ -384,9 +390,10 @@ class _AstToIrVisitor implements cdAst.AstVisitor { visitConditional(ast: cdAst.Conditional, mode: _Mode): any { const value: o.Expression = this._visit(ast.condition, _Mode.Expression); return convertToStatementIfNeeded( - mode, value.conditional( - this._visit(ast.trueExp, _Mode.Expression), - this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span))); + mode, + value.conditional( + this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), + this.convertSourceSpan(ast.span))); } visitPipe(ast: cdAst.BindingPipe, mode: _Mode): any { @@ -400,7 +407,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor { if (ast instanceof BuiltinFunctionCall) { fnResult = ast.converter(convertedArgs); } else { - fnResult = this._visit(ast.target !, _Mode.Expression) + fnResult = this._visit(ast.target!, _Mode.Expression) .callFn(convertedArgs, this.convertSourceSpan(ast.span)); } return convertToStatementIfNeeded(mode, fnResult); @@ -467,7 +474,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor { mode, o.literal(ast.value, type, this.convertSourceSpan(ast.span))); } - private _getLocal(name: string): o.Expression|null { return this._localResolver.getLocal(name); } + private _getLocal(name: string): o.Expression|null { + return this._localResolver.getLocal(name); + } visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any { if (ast.receiver instanceof cdAst.ImplicitReceiver && ast.name == '$any') { @@ -558,8 +567,8 @@ class _AstToIrVisitor implements cdAst.AstVisitor { // Otherwise it's an error. const receiver = ast.name; const value = (ast.value instanceof cdAst.PropertyRead) ? ast.value.name : undefined; - throw new Error( - `Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.`); + throw new Error(`Cannot assign value "${value}" to template variable "${ + receiver}". Template variables are read-only.`); } } } @@ -579,7 +588,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor { return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); } - visitAll(asts: cdAst.AST[], mode: _Mode): any { return asts.map(ast => this._visit(ast, mode)); } + visitAll(asts: cdAst.AST[], mode: _Mode): any { + return asts.map(ast => this._visit(ast, mode)); + } visitQuote(ast: cdAst.Quote, mode: _Mode): any { throw new Error(`Quotes are not supported for evaluation! @@ -634,7 +645,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor { // which comes in as leftMostSafe to this routine. let guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression); - let temporary: o.ReadVarExpr = undefined !; + let temporary: o.ReadVarExpr = undefined!; if (this.needsTemporary(leftMostSafe.receiver)) { // If the expression has method calls or pipes then we need to save the result into a // temporary variable to avoid calling stateful or impure code more than once. @@ -652,14 +663,16 @@ class _AstToIrVisitor implements cdAst.AstVisitor { // leftMostNode with its unguarded version in the call to `this.visit()`. if (leftMostSafe instanceof cdAst.SafeMethodCall) { this._nodeMap.set( - leftMostSafe, new cdAst.MethodCall( - leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, - leftMostSafe.name, leftMostSafe.args)); + leftMostSafe, + new cdAst.MethodCall( + leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, leftMostSafe.name, + leftMostSafe.args)); } else { this._nodeMap.set( - leftMostSafe, new cdAst.PropertyRead( - leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, - leftMostSafe.name)); + leftMostSafe, + new cdAst.PropertyRead( + leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, + leftMostSafe.name)); } // Recursively convert the node now without the guarded member access. @@ -690,25 +703,63 @@ class _AstToIrVisitor implements cdAst.AstVisitor { return (this._nodeMap.get(ast) || ast).visit(visitor); }; return ast.visit({ - visitBinary(ast: cdAst.Binary) { return null; }, - visitChain(ast: cdAst.Chain) { return null; }, - visitConditional(ast: cdAst.Conditional) { return null; }, - visitFunctionCall(ast: cdAst.FunctionCall) { return null; }, - visitImplicitReceiver(ast: cdAst.ImplicitReceiver) { return null; }, - visitInterpolation(ast: cdAst.Interpolation) { return null; }, - visitKeyedRead(ast: cdAst.KeyedRead) { return visit(this, ast.obj); }, - visitKeyedWrite(ast: cdAst.KeyedWrite) { return null; }, - visitLiteralArray(ast: cdAst.LiteralArray) { return null; }, - visitLiteralMap(ast: cdAst.LiteralMap) { return null; }, - visitLiteralPrimitive(ast: cdAst.LiteralPrimitive) { return null; }, - visitMethodCall(ast: cdAst.MethodCall) { return visit(this, ast.receiver); }, - visitPipe(ast: cdAst.BindingPipe) { return null; }, - visitPrefixNot(ast: cdAst.PrefixNot) { return null; }, - visitNonNullAssert(ast: cdAst.NonNullAssert) { return null; }, - visitPropertyRead(ast: cdAst.PropertyRead) { return visit(this, ast.receiver); }, - visitPropertyWrite(ast: cdAst.PropertyWrite) { return null; }, - visitQuote(ast: cdAst.Quote) { return null; }, - visitSafeMethodCall(ast: cdAst.SafeMethodCall) { return visit(this, ast.receiver) || ast; }, + visitBinary(ast: cdAst.Binary) { + return null; + }, + visitChain(ast: cdAst.Chain) { + return null; + }, + visitConditional(ast: cdAst.Conditional) { + return null; + }, + visitFunctionCall(ast: cdAst.FunctionCall) { + return null; + }, + visitImplicitReceiver(ast: cdAst.ImplicitReceiver) { + return null; + }, + visitInterpolation(ast: cdAst.Interpolation) { + return null; + }, + visitKeyedRead(ast: cdAst.KeyedRead) { + return visit(this, ast.obj); + }, + visitKeyedWrite(ast: cdAst.KeyedWrite) { + return null; + }, + visitLiteralArray(ast: cdAst.LiteralArray) { + return null; + }, + visitLiteralMap(ast: cdAst.LiteralMap) { + return null; + }, + visitLiteralPrimitive(ast: cdAst.LiteralPrimitive) { + return null; + }, + visitMethodCall(ast: cdAst.MethodCall) { + return visit(this, ast.receiver); + }, + visitPipe(ast: cdAst.BindingPipe) { + return null; + }, + visitPrefixNot(ast: cdAst.PrefixNot) { + return null; + }, + visitNonNullAssert(ast: cdAst.NonNullAssert) { + return null; + }, + visitPropertyRead(ast: cdAst.PropertyRead) { + return visit(this, ast.receiver); + }, + visitPropertyWrite(ast: cdAst.PropertyWrite) { + return null; + }, + visitQuote(ast: cdAst.Quote) { + return null; + }, + visitSafeMethodCall(ast: cdAst.SafeMethodCall) { + return visit(this, ast.receiver) || ast; + }, visitSafePropertyRead(ast: cdAst.SafePropertyRead) { return visit(this, ast.receiver) || ast; } @@ -726,29 +777,66 @@ class _AstToIrVisitor implements cdAst.AstVisitor { return ast.some(ast => visit(visitor, ast)); }; return ast.visit({ - visitBinary(ast: cdAst.Binary): - boolean{return visit(this, ast.left) || visit(this, ast.right);}, - visitChain(ast: cdAst.Chain) { return false; }, - visitConditional(ast: cdAst.Conditional): - boolean{return visit(this, ast.condition) || visit(this, ast.trueExp) || - visit(this, ast.falseExp);}, - visitFunctionCall(ast: cdAst.FunctionCall) { return true; }, - visitImplicitReceiver(ast: cdAst.ImplicitReceiver) { return false; }, - visitInterpolation(ast: cdAst.Interpolation) { return visitSome(this, ast.expressions); }, - visitKeyedRead(ast: cdAst.KeyedRead) { return false; }, - visitKeyedWrite(ast: cdAst.KeyedWrite) { return false; }, - visitLiteralArray(ast: cdAst.LiteralArray) { return true; }, - visitLiteralMap(ast: cdAst.LiteralMap) { return true; }, - visitLiteralPrimitive(ast: cdAst.LiteralPrimitive) { return false; }, - visitMethodCall(ast: cdAst.MethodCall) { return true; }, - visitPipe(ast: cdAst.BindingPipe) { return true; }, - visitPrefixNot(ast: cdAst.PrefixNot) { return visit(this, ast.expression); }, - visitNonNullAssert(ast: cdAst.PrefixNot) { return visit(this, ast.expression); }, - visitPropertyRead(ast: cdAst.PropertyRead) { return false; }, - visitPropertyWrite(ast: cdAst.PropertyWrite) { return false; }, - visitQuote(ast: cdAst.Quote) { return false; }, - visitSafeMethodCall(ast: cdAst.SafeMethodCall) { return true; }, - visitSafePropertyRead(ast: cdAst.SafePropertyRead) { return false; } + visitBinary(ast: cdAst.Binary): boolean { + return visit(this, ast.left) || visit(this, ast.right); + }, + visitChain(ast: cdAst.Chain) { + return false; + }, + visitConditional(ast: cdAst.Conditional): boolean { + return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp); + }, + visitFunctionCall(ast: cdAst.FunctionCall) { + return true; + }, + visitImplicitReceiver(ast: cdAst.ImplicitReceiver) { + return false; + }, + visitInterpolation(ast: cdAst.Interpolation) { + return visitSome(this, ast.expressions); + }, + visitKeyedRead(ast: cdAst.KeyedRead) { + return false; + }, + visitKeyedWrite(ast: cdAst.KeyedWrite) { + return false; + }, + visitLiteralArray(ast: cdAst.LiteralArray) { + return true; + }, + visitLiteralMap(ast: cdAst.LiteralMap) { + return true; + }, + visitLiteralPrimitive(ast: cdAst.LiteralPrimitive) { + return false; + }, + visitMethodCall(ast: cdAst.MethodCall) { + return true; + }, + visitPipe(ast: cdAst.BindingPipe) { + return true; + }, + visitPrefixNot(ast: cdAst.PrefixNot) { + return visit(this, ast.expression); + }, + visitNonNullAssert(ast: cdAst.PrefixNot) { + return visit(this, ast.expression); + }, + visitPropertyRead(ast: cdAst.PropertyRead) { + return false; + }, + visitPropertyWrite(ast: cdAst.PropertyWrite) { + return false; + }, + visitQuote(ast: cdAst.Quote) { + return false; + }, + visitSafeMethodCall(ast: cdAst.SafeMethodCall) { + return true; + }, + visitSafePropertyRead(ast: cdAst.SafePropertyRead) { + return false; + } }); } diff --git a/packages/compiler/src/config.ts b/packages/compiler/src/config.ts index c3f50e6a33c4a..69e0589e9f7c0 100644 --- a/packages/compiler/src/config.ts +++ b/packages/compiler/src/config.ts @@ -20,16 +20,21 @@ export class CompilerConfig { public preserveWhitespaces: boolean; public strictInjectionParameters: boolean; - constructor( - {defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, jitDevMode = false, - missingTranslation = null, preserveWhitespaces, strictInjectionParameters}: { - defaultEncapsulation?: ViewEncapsulation, - useJit?: boolean, - jitDevMode?: boolean, - missingTranslation?: MissingTranslationStrategy|null, - preserveWhitespaces?: boolean, - strictInjectionParameters?: boolean, - } = {}) { + constructor({ + defaultEncapsulation = ViewEncapsulation.Emulated, + useJit = true, + jitDevMode = false, + missingTranslation = null, + preserveWhitespaces, + strictInjectionParameters + }: { + defaultEncapsulation?: ViewEncapsulation, + useJit?: boolean, + jitDevMode?: boolean, + missingTranslation?: MissingTranslationStrategy|null, + preserveWhitespaces?: boolean, + strictInjectionParameters?: boolean, + } = {}) { this.defaultEncapsulation = defaultEncapsulation; this.useJit = !!useJit; this.jitDevMode = !!jitDevMode; @@ -40,6 +45,6 @@ export class CompilerConfig { } export function preserveWhitespacesDefault( - preserveWhitespacesOption: boolean | null, defaultSetting = false): boolean { + preserveWhitespacesOption: boolean|null, defaultSetting = false): boolean { return preserveWhitespacesOption === null ? defaultSetting : preserveWhitespacesOption; } diff --git a/packages/compiler/src/constant_pool.ts b/packages/compiler/src/constant_pool.ts index 7be8ff12a2c6a..ae58887171b33 100644 --- a/packages/compiler/src/constant_pool.ts +++ b/packages/compiler/src/constant_pool.ts @@ -7,7 +7,7 @@ */ import * as o from './output/output_ast'; -import {OutputContext, error} from './util'; +import {error, OutputContext} from './util'; const CONSTANT_PREFIX = '_c'; @@ -21,7 +21,12 @@ const CONSTANT_PREFIX = '_c'; */ const UNKNOWN_VALUE_KEY = o.variable('<unknown>'); -export const enum DefinitionKind {Injector, Directive, Component, Pipe} +export const enum DefinitionKind { + Injector, + Directive, + Component, + Pipe +} /** * Context to use when producing a key. @@ -43,7 +48,7 @@ class FixupExpression extends o.Expression { private original: o.Expression; // TODO(issue/24571): remove '!'. - shared !: boolean; + shared!: boolean; constructor(public resolved: o.Expression) { super(resolved.type); @@ -64,7 +69,9 @@ class FixupExpression extends o.Expression { return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved); } - isConstant() { return true; } + isConstant() { + return true; + } fixup(expression: o.Expression) { this.resolved = expression; @@ -169,7 +176,7 @@ export class ConstantPool { const resultExpressions = values.map( (e, index) => e.isConstant() ? this.getConstLiteral(e, true) : o.variable(`a${index}`)); const parameters = - resultExpressions.filter(isVariable).map(e => new o.FnParam(e.name !, o.DYNAMIC_TYPE)); + resultExpressions.filter(isVariable).map(e => new o.FnParam(e.name!, o.DYNAMIC_TYPE)); const pureFunctionDeclaration = o.fn(parameters, [new o.ReturnStatement(resultMap(resultExpressions))], o.INFERRED_TYPE); const name = this.freshName(); @@ -190,7 +197,9 @@ export class ConstantPool { * a digit so the prefix should be a constant string (not based on user input) and * must not end in a digit. */ - uniqueName(prefix: string): string { return `${prefix}${this.nextNameIndex++}`; } + uniqueName(prefix: string): string { + return `${prefix}${this.nextNameIndex++}`; + } private definitionsOf(kind: DefinitionKind): Map<any, FixupExpression> { switch (kind) { @@ -222,7 +231,9 @@ export class ConstantPool { return '<unknown>'; } - private freshName(): string { return this.uniqueName(CONSTANT_PREFIX); } + private freshName(): string { + return this.uniqueName(CONSTANT_PREFIX); + } private keyOf(expression: o.Expression) { return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT); @@ -259,7 +270,9 @@ class KeyVisitor implements o.ExpressionVisitor { `EX:${ast.value.runtime.name}`; } - visitReadVarExpr(node: o.ReadVarExpr) { return `VAR:${node.name}`; } + visitReadVarExpr(node: o.ReadVarExpr) { + return `VAR:${node.name}`; + } visitTypeofExpr(node: o.TypeofExpr, context: any): string { return `TYPEOF:${node.expr.visitExpression(this, context)}`; @@ -284,7 +297,7 @@ class KeyVisitor implements o.ExpressionVisitor { visitLocalizedString = invalid; } -function invalid<T>(this: o.ExpressionVisitor, arg: o.Expression | o.Statement): never { +function invalid<T>(this: o.ExpressionVisitor, arg: o.Expression|o.Statement): never { throw new Error( `Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`); } diff --git a/packages/compiler/src/core.ts b/packages/compiler/src/core.ts index 70da45b3aa46f..f5d29c5d59257 100644 --- a/packages/compiler/src/core.ts +++ b/packages/compiler/src/core.ts @@ -14,12 +14,16 @@ import {CssSelector} from './selector'; -export interface Inject { token: any; } +export interface Inject { + token: any; +} export const createInject = makeMetadataFactory<Inject>('Inject', (token: any) => ({token})); export const createInjectionToken = makeMetadataFactory<object>( 'InjectionToken', (desc: string) => ({_desc: desc, ɵprov: undefined})); -export interface Attribute { attributeName?: string; } +export interface Attribute { + attributeName?: string; +} export const createAttribute = makeMetadataFactory<Attribute>('Attribute', (attributeName?: string) => ({attributeName})); @@ -37,14 +41,17 @@ export const createContentChildren = makeMetadataFactory<Query>( (selector?: any, data: any = {}) => ({selector, first: false, isViewQuery: false, descendants: false, ...data})); export const createContentChild = makeMetadataFactory<Query>( - 'ContentChild', (selector?: any, data: any = {}) => - ({selector, first: true, isViewQuery: false, descendants: true, ...data})); + 'ContentChild', + (selector?: any, data: any = {}) => + ({selector, first: true, isViewQuery: false, descendants: true, ...data})); export const createViewChildren = makeMetadataFactory<Query>( - 'ViewChildren', (selector?: any, data: any = {}) => - ({selector, first: false, isViewQuery: true, descendants: true, ...data})); + 'ViewChildren', + (selector?: any, data: any = {}) => + ({selector, first: false, isViewQuery: true, descendants: true, ...data})); export const createViewChild = makeMetadataFactory<Query>( - 'ViewChild', (selector: any, data: any) => - ({selector, first: true, isViewQuery: true, descendants: true, ...data})); + 'ViewChild', + (selector: any, data: any) => + ({selector, first: true, isViewQuery: true, descendants: true, ...data})); export interface Directive { selector?: string; @@ -94,15 +101,21 @@ export interface Pipe { } export const createPipe = makeMetadataFactory<Pipe>('Pipe', (p: Pipe) => ({pure: true, ...p})); -export interface Input { bindingPropertyName?: string; } +export interface Input { + bindingPropertyName?: string; +} export const createInput = makeMetadataFactory<Input>('Input', (bindingPropertyName?: string) => ({bindingPropertyName})); -export interface Output { bindingPropertyName?: string; } +export interface Output { + bindingPropertyName?: string; +} export const createOutput = makeMetadataFactory<Output>( 'Output', (bindingPropertyName?: string) => ({bindingPropertyName})); -export interface HostBinding { hostPropertyName?: string; } +export interface HostBinding { + hostPropertyName?: string; +} export const createHostBinding = makeMetadataFactory<HostBinding>( 'HostBinding', (hostPropertyName?: string) => ({hostPropertyName})); @@ -140,7 +153,9 @@ export interface Injectable { } export const createInjectable = makeMetadataFactory('Injectable', (injectable: Injectable = {}) => injectable); -export interface SchemaMetadata { name: string; } +export interface SchemaMetadata { + name: string; +} export const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata = { name: 'custom-elements' @@ -155,7 +170,9 @@ export const createSelf = makeMetadataFactory('Self'); export const createSkipSelf = makeMetadataFactory('SkipSelf'); export const createHost = makeMetadataFactory('Host'); -export interface Type extends Function { new (...args: any[]): any; } +export interface Type extends Function { + new(...args: any[]): any; +} export const Type = Function; export enum SecurityContext { @@ -240,7 +257,10 @@ export const enum InjectFlags { Optional = 1 << 3, } -export const enum ArgumentType {Inline = 0, Dynamic = 1} +export const enum ArgumentType { + Inline = 0, + Dynamic = 1 +} export const enum BindingFlags { TypeElementAttribute = 1 << 0, @@ -255,7 +275,10 @@ export const enum BindingFlags { Types = TypeElementAttribute | TypeElementClass | TypeElementStyle | TypeProperty } -export const enum QueryBindingType {First = 0, All = 1} +export const enum QueryBindingType { + First = 0, + All = 1 +} export const enum QueryValueType { ElementRef = 0, @@ -324,7 +347,7 @@ export const enum SelectorFlags { // These are a copy the CSS types from core/src/render3/interfaces/projection.ts // They are duplicated here as they cannot be directly referenced from core. -export type R3CssSelector = (string | SelectorFlags)[]; +export type R3CssSelector = (string|SelectorFlags)[]; export type R3CssSelectorList = R3CssSelector[]; function parserSelectorToSimpleSelector(selector: CssSelector): R3CssSelector { @@ -363,7 +386,7 @@ function parserSelectorToR3Selector(selector: CssSelector): R3CssSelector { return positive.concat(...negative); } -export function parseSelectorToR3Selector(selector: string | null): R3CssSelectorList { +export function parseSelectorToR3Selector(selector: string|null): R3CssSelectorList { return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : []; } diff --git a/packages/compiler/src/css_parser/css_ast.ts b/packages/compiler/src/css_parser/css_ast.ts index b217b089b657d..08e86510a5668 100644 --- a/packages/compiler/src/css_parser/css_ast.ts +++ b/packages/compiler/src/css_parser/css_ast.ts @@ -46,8 +46,12 @@ export interface CssAstVisitor { export abstract class CssAst { constructor(public location: ParseSourceSpan) {} - get start(): ParseLocation { return this.location.start; } - get end(): ParseLocation { return this.location.end; } + get start(): ParseLocation { + return this.location.start; + } + get end(): ParseLocation { + return this.location.end; + } abstract visit(visitor: CssAstVisitor, context?: any): any; } @@ -55,11 +59,15 @@ export class CssStyleValueAst extends CssAst { constructor(location: ParseSourceSpan, public tokens: CssToken[], public strValue: string) { super(location); } - visit(visitor: CssAstVisitor, context?: any): any { return visitor.visitCssValue(this); } + visit(visitor: CssAstVisitor, context?: any): any { + return visitor.visitCssValue(this); + } } export abstract class CssRuleAst extends CssAst { - constructor(location: ParseSourceSpan) { super(location); } + constructor(location: ParseSourceSpan) { + super(location); + } } export class CssBlockRuleAst extends CssRuleAst { @@ -158,7 +166,9 @@ export class CssDefinitionAst extends CssAst { } export abstract class CssSelectorPartAst extends CssAst { - constructor(location: ParseSourceSpan) { super(location); } + constructor(location: ParseSourceSpan) { + super(location); + } } export class CssSelectorAst extends CssSelectorPartAst { @@ -195,8 +205,12 @@ export class CssPseudoSelectorAst extends CssSelectorPartAst { } export class CssBlockAst extends CssAst { - constructor(location: ParseSourceSpan, public entries: CssAst[]) { super(location); } - visit(visitor: CssAstVisitor, context?: any): any { return visitor.visitCssBlock(this, context); } + constructor(location: ParseSourceSpan, public entries: CssAst[]) { + super(location); + } + visit(visitor: CssAstVisitor, context?: any): any { + return visitor.visitCssBlock(this, context); + } } /* @@ -213,7 +227,9 @@ export class CssStylesBlockAst extends CssBlockAst { } export class CssStyleSheetAst extends CssAst { - constructor(location: ParseSourceSpan, public rules: CssAst[]) { super(location); } + constructor(location: ParseSourceSpan, public rules: CssAst[]) { + super(location); + } visit(visitor: CssAstVisitor, context?: any): any { return visitor.visitCssStyleSheet(this, context); } diff --git a/packages/compiler/src/css_parser/css_lexer.ts b/packages/compiler/src/css_parser/css_lexer.ts index ea574dbf95408..31fc632eba1d1 100644 --- a/packages/compiler/src/css_parser/css_lexer.ts +++ b/packages/compiler/src/css_parser/css_lexer.ts @@ -117,7 +117,7 @@ function _trackWhitespace(mode: CssLexerMode) { export class CssScanner { // TODO(issue/24571): remove '!'. - peek !: number; + peek!: number; peekPeek: number; length: number = 0; index: number = -1; @@ -135,7 +135,9 @@ export class CssScanner { this.advance(); } - getMode(): CssLexerMode { return this._currentMode; } + getMode(): CssLexerMode { + return this._currentMode; + } setMode(mode: CssLexerMode) { if (this._currentMode != mode) { @@ -198,7 +200,7 @@ export class CssScanner { const previousLine = this.line; const previousColumn = this.column; - let next: CssToken = undefined !; + let next: CssToken = undefined!; const output = this.scan(); if (output != null) { // just incase the inner scan method returned an error @@ -236,9 +238,10 @@ export class CssScanner { } error = cssScannerError( - next, generateErrorMessage( - this.input, errorMessage, next.strValue, previousIndex, previousLine, - previousColumn)); + next, + generateErrorMessage( + this.input, errorMessage, next.strValue, previousIndex, previousLine, + previousColumn)); } return new LexedCssResult(error, next); @@ -254,7 +257,7 @@ export class CssScanner { const token = this._scan(); if (token == null) return null; - const error = this._currentError !; + const error = this._currentError!; this._currentError = null; if (!trackWS) { @@ -461,7 +464,7 @@ export class CssScanner { const startingColumn = this.column; this.advance(); if (isIdentifierStart(this.peek, this.peekPeek)) { - const ident = this.scanIdentifier() !; + const ident = this.scanIdentifier()!; const strValue = '@' + ident.strValue; return new CssToken(start, startingColumn, this.line, CssTokenType.AtKeyword, strValue); } else { diff --git a/packages/compiler/src/css_parser/css_parser.ts b/packages/compiler/src/css_parser/css_parser.ts index 2650e3deecb01..eeebbf8d614aa 100644 --- a/packages/compiler/src/css_parser/css_parser.ts +++ b/packages/compiler/src/css_parser/css_parser.ts @@ -9,7 +9,7 @@ import * as chars from '../chars'; import {ParseError, ParseLocation, ParseSourceFile, ParseSourceSpan} from '../parse_util'; -import {BlockType, CssAst, CssAtRulePredicateAst, CssBlockAst, CssBlockDefinitionRuleAst, CssBlockRuleAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssPseudoSelectorAst, CssRuleAst, CssSelectorAst, CssSelectorRuleAst, CssSimpleSelectorAst, CssStyleSheetAst, CssStyleValueAst, CssStylesBlockAst, CssUnknownRuleAst, CssUnknownTokenListAst, mergeTokens} from './css_ast'; +import {BlockType, CssAst, CssAtRulePredicateAst, CssBlockAst, CssBlockDefinitionRuleAst, CssBlockRuleAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssPseudoSelectorAst, CssRuleAst, CssSelectorAst, CssSelectorRuleAst, CssSimpleSelectorAst, CssStylesBlockAst, CssStyleSheetAst, CssStyleValueAst, CssUnknownRuleAst, CssUnknownTokenListAst, mergeTokens} from './css_ast'; import {CssLexer, CssLexerMode, CssScanner, CssToken, CssTokenType, generateErrorMessage, getRawMessage, isNewline} from './css_lexer'; const SPACE_OPERATOR = ' '; @@ -84,11 +84,11 @@ export class ParsedCssResult { export class CssParser { private _errors: CssParseError[] = []; // TODO(issue/24571): remove '!'. - private _file !: ParseSourceFile; + private _file!: ParseSourceFile; // TODO(issue/24571): remove '!'. - private _scanner !: CssScanner; + private _scanner!: CssScanner; // TODO(issue/24571): remove '!'. - private _lastToken !: CssToken; + private _lastToken!: CssToken; /** * @param css the CSS code that will be parsed @@ -125,11 +125,13 @@ export class CssParser { // EOF token that was emitted sometime during the lexing span = this._generateSourceSpan(firstRule, this._lastToken); } - return new CssStyleSheetAst(span !, results); + return new CssStyleSheetAst(span!, results); } /** @internal */ - _getSourceContent(): string { return this._scanner != null ? this._scanner.input : ''; } + _getSourceContent(): string { + return this._scanner != null ? this._scanner.input : ''; + } /** @internal */ _extractSourceContent(start: number, end: number): string { @@ -159,9 +161,9 @@ export class CssParser { let endColumn: number = -1; let endIndex: number = -1; if (end instanceof CssAst) { - endLine = end.location.end.line !; - endColumn = end.location.end.col !; - endIndex = end.location.end.offset !; + endLine = end.location.end.line!; + endColumn = end.location.end.col!; + endIndex = end.location.end.offset!; } else if (end instanceof CssToken) { endLine = end.line; endColumn = end.column; @@ -253,7 +255,7 @@ export class CssParser { case BlockType.Viewport: case BlockType.FontFace: - block = this._parseStyleBlock(delimiters) !; + block = this._parseStyleBlock(delimiters)!; span = this._generateSourceSpan(startToken, block); return new CssBlockRuleAst(span, type, block); @@ -293,7 +295,7 @@ export class CssParser { span = this._generateSourceSpan(startToken, tokens[tokens.length - 1]); query = new CssAtRulePredicateAst(span, strValue, tokens); block = this._parseBlock(delimiters); - strValue = this._extractSourceContent(start, block.end.offset !); + strValue = this._extractSourceContent(start, block.end.offset!); span = this._generateSourceSpan(startToken, block); return new CssBlockDefinitionRuleAst(span, strValue, type, query, block); @@ -310,11 +312,15 @@ export class CssParser { token); this._collectUntilDelim(delimiters | LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG) - .forEach((token) => { listOfTokens.push(token); }); + .forEach((token) => { + listOfTokens.push(token); + }); if (this._scanner.peek == chars.$LBRACE) { listOfTokens.push(this._consume(CssTokenType.Character, '{')); this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG) - .forEach((token) => { listOfTokens.push(token); }); + .forEach((token) => { + listOfTokens.push(token); + }); listOfTokens.push(this._consume(CssTokenType.Character, '}')); } endToken = listOfTokens[listOfTokens.length - 1]; @@ -339,7 +345,9 @@ export class CssParser { const innerTokens: CssToken[] = []; selectors.forEach((selector: CssSelectorAst) => { selector.selectorParts.forEach((part: CssSimpleSelectorAst) => { - part.tokens.forEach((token: CssToken) => { innerTokens.push(token); }); + part.tokens.forEach((token: CssToken) => { + innerTokens.push(token); + }); }); }); const endToken = innerTokens[innerTokens.length - 1]; @@ -376,7 +384,7 @@ export class CssParser { /** @internal */ _scan(): CssToken { - const output = this._scanner.scan() !; + const output = this._scanner.scan()!; const token = output.token; const error = output.error; if (error != null) { @@ -387,7 +395,9 @@ export class CssParser { } /** @internal */ - _getScannerIndex(): number { return this._scanner.index; } + _getScannerIndex(): number { + return this._scanner.index; + } /** @internal */ _consume(type: CssTokenType, value: string|null = null): CssToken { @@ -432,7 +442,7 @@ export class CssParser { } const stylesBlock = this._parseStyleBlock(delimiters | RBRACE_DELIM_FLAG); const span = this._generateSourceSpan(stepTokens[0], stylesBlock); - const ast = new CssKeyframeDefinitionAst(span, stepTokens, stylesBlock !); + const ast = new CssKeyframeDefinitionAst(span, stepTokens, stylesBlock!); this._scanner.setMode(CssLexerMode.BLOCK); return ast; @@ -523,7 +533,7 @@ export class CssParser { const selectorCssTokens: CssToken[] = []; const pseudoSelectors: CssPseudoSelectorAst[] = []; - let previousToken: CssToken = undefined !; + let previousToken: CssToken = undefined!; const selectorPartDelimiters = delimiters | SPACE_DELIM_FLAG; let loopOverSelector = !characterContainsDelimiter(this._scanner.peek, selectorPartDelimiters); @@ -576,7 +586,8 @@ export class CssParser { hasAttributeError || this._scanner.getMode() == CssLexerMode.ATTRIBUTE_SELECTOR; if (hasAttributeError) { this._error( - `Unbalanced CSS attribute selector at column ${previousToken.line}:${previousToken.column}`, + `Unbalanced CSS attribute selector at column ${previousToken.line}:${ + previousToken.column}`, previousToken); } @@ -671,8 +682,8 @@ export class CssParser { endTokenOrAst = operator; } - const span = this._generateSourceSpan(startTokenOrAst !, endTokenOrAst); - return new CssSimpleSelectorAst(span, selectorCssTokens, strValue, pseudoSelectors, operator !); + const span = this._generateSourceSpan(startTokenOrAst!, endTokenOrAst); + return new CssSimpleSelectorAst(span, selectorCssTokens, strValue, pseudoSelectors, operator!); } /** @internal */ @@ -701,7 +712,7 @@ export class CssParser { const tokens: CssToken[] = []; let wsStr = ''; - let previous: CssToken = undefined !; + let previous: CssToken = undefined!; while (!characterContainsDelimiter(this._scanner.peek, delimiters)) { let token: CssToken; if (previous != null && previous.type == CssTokenType.Identifier && @@ -841,7 +852,9 @@ export class CssParser { const remainingTokens = this._collectUntilDelim( delimiters | COLON_DELIM_FLAG | SEMICOLON_DELIM_FLAG, CssTokenType.Identifier); if (remainingTokens.length > 0) { - remainingTokens.forEach((token) => { propStr.push(token.strValue); }); + remainingTokens.forEach((token) => { + propStr.push(token.strValue); + }); } endToken = prop = @@ -868,7 +881,7 @@ export class CssParser { } const span = this._generateSourceSpan(prop, endToken); - return new CssDefinitionAst(span, prop, value !); + return new CssDefinitionAst(span, prop, value!); } /** @internal */ @@ -899,5 +912,7 @@ export class CssParseError extends ParseError { return new CssParseError(span, 'CSS Parse Error: ' + errMsg); } - constructor(span: ParseSourceSpan, message: string) { super(span, message); } + constructor(span: ParseSourceSpan, message: string) { + super(span, message); + } } diff --git a/packages/compiler/src/directive_normalizer.ts b/packages/compiler/src/directive_normalizer.ts index 38e54961ba1db..366cea285a889 100644 --- a/packages/compiler/src/directive_normalizer.ts +++ b/packages/compiler/src/directive_normalizer.ts @@ -17,7 +17,7 @@ import {ResourceLoader} from './resource_loader'; import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver'; import {PreparsedElementType, preparseElement} from './template_parser/template_preparser'; import {UrlResolver} from './url_resolver'; -import {SyncAsync, isDefined, stringify, syntaxError} from './util'; +import {isDefined, stringify, SyncAsync, syntaxError} from './util'; export interface PrenormalizedTemplateMetadata { ngModuleType: any; @@ -40,16 +40,19 @@ export class DirectiveNormalizer { private _resourceLoader: ResourceLoader, private _urlResolver: UrlResolver, private _htmlParser: HtmlParser, private _config: CompilerConfig) {} - clearCache(): void { this._resourceLoaderCache.clear(); } + clearCache(): void { + this._resourceLoaderCache.clear(); + } clearCacheFor(normalizedDirective: CompileDirectiveMetadata): void { if (!normalizedDirective.isComponent) { return; } const template = normalizedDirective.template !; - this._resourceLoaderCache.delete(template.templateUrl !); - template.externalStylesheets.forEach( - (stylesheet) => { this._resourceLoaderCache.delete(stylesheet.moduleUrl !); }); + this._resourceLoaderCache.delete(template.templateUrl!); + template.externalStylesheets.forEach((stylesheet) => { + this._resourceLoaderCache.delete(stylesheet.moduleUrl!); + }); } private _fetch(url: string): SyncAsync<string> { @@ -65,17 +68,18 @@ export class DirectiveNormalizer { SyncAsync<CompileTemplateMetadata> { if (isDefined(prenormData.template)) { if (isDefined(prenormData.templateUrl)) { - throw syntaxError( - `'${stringify(prenormData.componentType)}' component cannot define both template and templateUrl`); + throw syntaxError(`'${ + stringify(prenormData + .componentType)}' component cannot define both template and templateUrl`); } if (typeof prenormData.template !== 'string') { - throw syntaxError( - `The template specified for component ${stringify(prenormData.componentType)} is not a string`); + throw syntaxError(`The template specified for component ${ + stringify(prenormData.componentType)} is not a string`); } } else if (isDefined(prenormData.templateUrl)) { if (typeof prenormData.templateUrl !== 'string') { - throw syntaxError( - `The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`); + throw syntaxError(`The templateUrl specified for component ${ + stringify(prenormData.componentType)} is not a string`); } } else { throw syntaxError( @@ -84,8 +88,8 @@ export class DirectiveNormalizer { if (isDefined(prenormData.preserveWhitespaces) && typeof prenormData.preserveWhitespaces !== 'boolean') { - throw syntaxError( - `The preserveWhitespaces option for component ${stringify(prenormData.componentType)} must be a boolean`); + throw syntaxError(`The preserveWhitespaces option for component ${ + stringify(prenormData.componentType)} must be a boolean`); } return SyncAsync.then( @@ -101,7 +105,7 @@ export class DirectiveNormalizer { template = prenomData.template; templateUrl = prenomData.moduleUrl; } else { - templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl !); + templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl!); template = this._fetch(templateUrl); } return SyncAsync.then( @@ -112,7 +116,7 @@ export class DirectiveNormalizer { prenormData: PrenormalizedTemplateMetadata, template: string, templateAbsUrl: string): PreparsedTemplate { const isInline = !!prenormData.template; - const interpolationConfig = InterpolationConfig.fromArray(prenormData.interpolation !); + const interpolationConfig = InterpolationConfig.fromArray(prenormData.interpolation!); const templateUrl = templateSourceUrl( {reference: prenormData.ngModuleType}, {type: {reference: prenormData.componentType}}, {isInline, templateUrl: templateAbsUrl}); @@ -140,8 +144,12 @@ export class DirectiveNormalizer { .styleUrls; return { template, - templateUrl: templateAbsUrl, isInline, - htmlAst: rootNodesAndErrors, styles, inlineStyleUrls, styleUrls, + templateUrl: templateAbsUrl, + isInline, + htmlAst: rootNodesAndErrors, + styles, + inlineStyleUrls, + styleUrls, ngContentSelectors: visitor.ngContentSelectors, }; } @@ -172,7 +180,7 @@ export class DirectiveNormalizer { const styleUrls = preparsedTemplate.styleUrls; const externalStylesheets = styleUrls.map(styleUrl => { - const stylesheet = stylesheets.get(styleUrl) !; + const stylesheet = stylesheets.get(styleUrl)!; const styles = [...stylesheet.styles]; this._inlineStyles(stylesheet.styleUrls, stylesheets, styles); return new CompileStylesheetMetadata({moduleUrl: styleUrl, styles: styles}); @@ -190,11 +198,14 @@ export class DirectiveNormalizer { encapsulation, template: preparsedTemplate.template, templateUrl: preparsedTemplate.templateUrl, - htmlAst: preparsedTemplate.htmlAst, styles, styleUrls, + htmlAst: preparsedTemplate.htmlAst, + styles, + styleUrls, ngContentSelectors: preparsedTemplate.ngContentSelectors, animations: prenormData.animations, interpolation: prenormData.interpolation, - isInline: preparsedTemplate.isInline, externalStylesheets, + isInline: preparsedTemplate.isInline, + externalStylesheets, preserveWhitespaces: preserveWhitespacesDefault( prenormData.preserveWhitespaces, this._config.preserveWhitespaces), }); @@ -204,7 +215,7 @@ export class DirectiveNormalizer { styleUrls: string[], stylesheets: Map<string, CompileStylesheetMetadata>, targetStyles: string[]) { styleUrls.forEach(styleUrl => { - const stylesheet = stylesheets.get(styleUrl) !; + const stylesheet = stylesheets.get(styleUrl)!; stylesheet.styles.forEach(style => targetStyles.push(style)); this._inlineStyles(stylesheet.styleUrls, stylesheets, targetStyles); }); @@ -232,7 +243,7 @@ export class DirectiveNormalizer { } private _normalizeStylesheet(stylesheet: CompileStylesheetMetadata): CompileStylesheetMetadata { - const moduleUrl = stylesheet.moduleUrl !; + const moduleUrl = stylesheet.moduleUrl!; const allStyleUrls = stylesheet.styleUrls.filter(isStyleUrlResolvable) .map(url => this._urlResolver.resolve(moduleUrl, url)); @@ -297,13 +308,21 @@ class TemplatePreparseVisitor implements html.Visitor { return null; } - visitExpansion(ast: html.Expansion, context: any): any { html.visitAll(this, ast.cases); } + visitExpansion(ast: html.Expansion, context: any): any { + html.visitAll(this, ast.cases); + } visitExpansionCase(ast: html.ExpansionCase, context: any): any { html.visitAll(this, ast.expression); } - visitComment(ast: html.Comment, context: any): any { return null; } - visitAttribute(ast: html.Attribute, context: any): any { return null; } - visitText(ast: html.Text, context: any): any { return null; } + visitComment(ast: html.Comment, context: any): any { + return null; + } + visitAttribute(ast: html.Attribute, context: any): any { + return null; + } + visitText(ast: html.Text, context: any): any { + return null; + } } diff --git a/packages/compiler/src/directive_resolver.ts b/packages/compiler/src/directive_resolver.ts index be786b4a9bb44..78764f5f597b4 100644 --- a/packages/compiler/src/directive_resolver.ts +++ b/packages/compiler/src/directive_resolver.ts @@ -7,7 +7,7 @@ */ import {CompileReflector} from './compile_reflector'; -import {Component, Directive, Type, createComponent, createContentChild, createContentChildren, createDirective, createHostBinding, createHostListener, createInput, createOutput, createViewChild, createViewChildren} from './core'; +import {Component, createComponent, createContentChild, createContentChildren, createDirective, createHostBinding, createHostListener, createInput, createOutput, createViewChild, createViewChildren, Directive, Type} from './core'; import {resolveForwardRef, splitAtColon, stringify} from './util'; const QUERY_METADATA_IDENTIFIERS = [ @@ -109,7 +109,9 @@ export class DirectiveResolver { return this._merge(dm, inputs, outputs, host, queries, guards, directiveType); } - private _extractPublicName(def: string) { return splitAtColon(def, [null !, def])[1].trim(); } + private _extractPublicName(def: string) { + return splitAtColon(def, [null!, def])[1].trim(); + } private _dedupeBindings(bindings: string[]): string[] { const names = new Set<string>(); @@ -168,7 +170,8 @@ export class DirectiveResolver { host: mergedHost, exportAs: directive.exportAs, queries: mergedQueries, - providers: directive.providers, guards + providers: directive.providers, + guards }); } } diff --git a/packages/compiler/src/expression_parser/ast.ts b/packages/compiler/src/expression_parser/ast.ts index ecf5492c5eb4e..756831b97b0c9 100644 --- a/packages/compiler/src/expression_parser/ast.ts +++ b/packages/compiler/src/expression_parser/ast.ts @@ -31,8 +31,12 @@ export class AST { * Absolute location of the expression AST in a source code file. */ public sourceSpan: AbsoluteSourceSpan) {} - visit(visitor: AstVisitor, context: any = null): any { return null; } - toString(): string { return 'AST'; } + visit(visitor: AstVisitor, context: any = null): any { + return null; + } + toString(): string { + return 'AST'; + } } /** @@ -54,8 +58,12 @@ export class Quote extends AST { public uninterpretedExpression: string, public location: any) { super(span, sourceSpan); } - visit(visitor: AstVisitor, context: any = null): any { return visitor.visitQuote(this, context); } - toString(): string { return 'Quote'; } + visit(visitor: AstVisitor, context: any = null): any { + return visitor.visitQuote(this, context); + } + toString(): string { + return 'Quote'; + } } export class EmptyExpr extends AST { @@ -77,7 +85,9 @@ export class Chain extends AST { constructor(span: ParseSpan, sourceSpan: AbsoluteSourceSpan, public expressions: any[]) { super(span, sourceSpan); } - visit(visitor: AstVisitor, context: any = null): any { return visitor.visitChain(this, context); } + visit(visitor: AstVisitor, context: any = null): any { + return visitor.visitChain(this, context); + } } export class Conditional extends AST { @@ -148,7 +158,9 @@ export class BindingPipe extends AST { public args: any[], public nameSpan: AbsoluteSourceSpan) { super(span, sourceSpan); } - visit(visitor: AstVisitor, context: any = null): any { return visitor.visitPipe(this, context); } + visit(visitor: AstVisitor, context: any = null): any { + return visitor.visitPipe(this, context); + } } export class LiteralPrimitive extends AST { @@ -280,7 +292,9 @@ export class ASTWithSource extends AST { } return this.ast.visit(visitor, context); } - toString(): string { return `${this.source} in ${this.location}`; } + toString(): string { + return `${this.source} in ${this.location}`; + } } /** @@ -302,7 +316,7 @@ export class ASTWithSource extends AST { * the LHS of a HTML attribute to the expression in the RHS. All other bindings * in the example above are derived solely from the RHS. */ -export type TemplateBinding = VariableBinding | ExpressionBinding; +export type TemplateBinding = VariableBinding|ExpressionBinding; export class VariableBinding { /** @@ -379,7 +393,9 @@ export class RecursiveAstVisitor implements AstVisitor { this.visit(ast.left, context); this.visit(ast.right, context); } - visitChain(ast: Chain, context: any): any { this.visitAll(ast.expressions, context); } + visitChain(ast: Chain, context: any): any { + this.visitAll(ast.expressions, context); + } visitConditional(ast: Conditional, context: any): any { this.visit(ast.condition, context); this.visit(ast.trueExp, context); @@ -411,15 +427,23 @@ export class RecursiveAstVisitor implements AstVisitor { visitLiteralArray(ast: LiteralArray, context: any): any { this.visitAll(ast.expressions, context); } - visitLiteralMap(ast: LiteralMap, context: any): any { this.visitAll(ast.values, context); } + visitLiteralMap(ast: LiteralMap, context: any): any { + this.visitAll(ast.values, context); + } visitLiteralPrimitive(ast: LiteralPrimitive, context: any): any {} visitMethodCall(ast: MethodCall, context: any): any { this.visit(ast.receiver, context); this.visitAll(ast.args, context); } - visitPrefixNot(ast: PrefixNot, context: any): any { this.visit(ast.expression, context); } - visitNonNullAssert(ast: NonNullAssert, context: any): any { this.visit(ast.expression, context); } - visitPropertyRead(ast: PropertyRead, context: any): any { this.visit(ast.receiver, context); } + visitPrefixNot(ast: PrefixNot, context: any): any { + this.visit(ast.expression, context); + } + visitNonNullAssert(ast: NonNullAssert, context: any): any { + this.visit(ast.expression, context); + } + visitPropertyRead(ast: PropertyRead, context: any): any { + this.visit(ast.receiver, context); + } visitPropertyWrite(ast: PropertyWrite, context: any): any { this.visit(ast.receiver, context); this.visit(ast.value, context); @@ -441,7 +465,9 @@ export class RecursiveAstVisitor implements AstVisitor { } export class AstTransformer implements AstVisitor { - visitImplicitReceiver(ast: ImplicitReceiver, context: any): AST { return ast; } + visitImplicitReceiver(ast: ImplicitReceiver, context: any): AST { + return ast; + } visitInterpolation(ast: Interpolation, context: any): AST { return new Interpolation(ast.span, ast.sourceSpan, ast.strings, this.visitAll(ast.expressions)); @@ -476,7 +502,7 @@ export class AstTransformer implements AstVisitor { visitFunctionCall(ast: FunctionCall, context: any): AST { return new FunctionCall( - ast.span, ast.sourceSpan, ast.target !.visit(this), this.visitAll(ast.args)); + ast.span, ast.sourceSpan, ast.target!.visit(this), this.visitAll(ast.args)); } visitLiteralArray(ast: LiteralArray, context: any): AST { @@ -542,7 +568,9 @@ export class AstTransformer implements AstVisitor { // A transformer that only creates new nodes if the transformer makes a change or // a change is made a child node. export class AstMemoryEfficientTransformer implements AstVisitor { - visitImplicitReceiver(ast: ImplicitReceiver, context: any): AST { return ast; } + visitImplicitReceiver(ast: ImplicitReceiver, context: any): AST { + return ast; + } visitInterpolation(ast: Interpolation, context: any): Interpolation { const expressions = this.visitAll(ast.expressions); @@ -551,7 +579,9 @@ export class AstMemoryEfficientTransformer implements AstVisitor { return ast; } - visitLiteralPrimitive(ast: LiteralPrimitive, context: any): AST { return ast; } + visitLiteralPrimitive(ast: LiteralPrimitive, context: any): AST { + return ast; + } visitPropertyRead(ast: PropertyRead, context: any): AST { const receiver = ast.receiver.visit(this); @@ -704,7 +734,9 @@ export class AstMemoryEfficientTransformer implements AstVisitor { return ast; } - visitQuote(ast: Quote, context: any): AST { return ast; } + visitQuote(ast: Quote, context: any): AST { + return ast; + } } // Bindings diff --git a/packages/compiler/src/expression_parser/lexer.ts b/packages/compiler/src/expression_parser/lexer.ts index c4ccbb87af66a..b942ecd4f7e1e 100644 --- a/packages/compiler/src/expression_parser/lexer.ts +++ b/packages/compiler/src/expression_parser/lexer.ts @@ -42,37 +42,61 @@ export class Token { return this.type == TokenType.Character && this.numValue == code; } - isNumber(): boolean { return this.type == TokenType.Number; } + isNumber(): boolean { + return this.type == TokenType.Number; + } - isString(): boolean { return this.type == TokenType.String; } + isString(): boolean { + return this.type == TokenType.String; + } isOperator(operator: string): boolean { return this.type == TokenType.Operator && this.strValue == operator; } - isIdentifier(): boolean { return this.type == TokenType.Identifier; } + isIdentifier(): boolean { + return this.type == TokenType.Identifier; + } - isKeyword(): boolean { return this.type == TokenType.Keyword; } + isKeyword(): boolean { + return this.type == TokenType.Keyword; + } - isKeywordLet(): boolean { return this.type == TokenType.Keyword && this.strValue == 'let'; } + isKeywordLet(): boolean { + return this.type == TokenType.Keyword && this.strValue == 'let'; + } - isKeywordAs(): boolean { return this.type == TokenType.Keyword && this.strValue == 'as'; } + isKeywordAs(): boolean { + return this.type == TokenType.Keyword && this.strValue == 'as'; + } - isKeywordNull(): boolean { return this.type == TokenType.Keyword && this.strValue == 'null'; } + isKeywordNull(): boolean { + return this.type == TokenType.Keyword && this.strValue == 'null'; + } isKeywordUndefined(): boolean { return this.type == TokenType.Keyword && this.strValue == 'undefined'; } - isKeywordTrue(): boolean { return this.type == TokenType.Keyword && this.strValue == 'true'; } + isKeywordTrue(): boolean { + return this.type == TokenType.Keyword && this.strValue == 'true'; + } - isKeywordFalse(): boolean { return this.type == TokenType.Keyword && this.strValue == 'false'; } + isKeywordFalse(): boolean { + return this.type == TokenType.Keyword && this.strValue == 'false'; + } - isKeywordThis(): boolean { return this.type == TokenType.Keyword && this.strValue == 'this'; } + isKeywordThis(): boolean { + return this.type == TokenType.Keyword && this.strValue == 'this'; + } - isError(): boolean { return this.type == TokenType.Error; } + isError(): boolean { + return this.type == TokenType.Error; + } - toNumber(): number { return this.type == TokenType.Number ? this.numValue : -1; } + toNumber(): number { + return this.type == TokenType.Number ? this.numValue : -1; + } toString(): string|null { switch (this.type) { diff --git a/packages/compiler/src/expression_parser/parser.ts b/packages/compiler/src/expression_parser/parser.ts index cb5f2dba08fb5..d003edff14035 100644 --- a/packages/compiler/src/expression_parser/parser.ts +++ b/packages/compiler/src/expression_parser/parser.ts @@ -10,8 +10,8 @@ import * as chars from '../chars'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config'; import {escapeRegExp} from '../util'; -import {AST, ASTWithSource, AbsoluteSourceSpan, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, ExpressionBinding, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralMapKey, LiteralPrimitive, MethodCall, NonNullAssert, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding, TemplateBindingIdentifier, VariableBinding} from './ast'; -import {EOF, Lexer, Token, TokenType, isIdentifier, isQuote} from './lexer'; +import {AbsoluteSourceSpan, AST, AstVisitor, ASTWithSource, Binary, BindingPipe, Chain, Conditional, EmptyExpr, ExpressionBinding, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralMapKey, LiteralPrimitive, MethodCall, NonNullAssert, ParserError, ParseSpan, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding, TemplateBindingIdentifier, VariableBinding} from './ast'; +import {EOF, isIdentifier, isQuote, Lexer, Token, TokenType} from './lexer'; export class SplitInterpolation { constructor(public strings: string[], public expressions: string[], public offsets: number[]) {} @@ -253,7 +253,8 @@ export class Parser { const parts = input.split(regexp); if (parts.length > 1) { this._reportError( - `Got interpolation (${interpolationConfig.start}${interpolationConfig.end}) where expression was expected`, + `Got interpolation (${interpolationConfig.start}${ + interpolationConfig.end}) where expression was expected`, input, `at column ${this._findInterpolationErrorColumn(parts, 1, interpolationConfig)} in`, location); @@ -300,7 +301,9 @@ export class _ParseAST { return i < this.tokens.length ? this.tokens[i] : EOF; } - get next(): Token { return this.peek(0); } + get next(): Token { + return this.peek(0); + } get inputIndex(): number { return (this.index < this.tokens.length) ? this.next.index + this.offset : @@ -310,7 +313,9 @@ export class _ParseAST { /** * Returns the absolute offset of the start of the current token. */ - get currentAbsoluteOffset(): number { return this.absoluteOffset + this.inputIndex; } + get currentAbsoluteOffset(): number { + return this.absoluteOffset + this.inputIndex; + } span(start: number) { // `end` is either the @@ -326,10 +331,12 @@ export class _ParseAST { if (!this.sourceSpanCache.has(serial)) { this.sourceSpanCache.set(serial, this.span(start).toAbsolute(this.absoluteOffset)); } - return this.sourceSpanCache.get(serial) !; + return this.sourceSpanCache.get(serial)!; } - advance() { this.index++; } + advance() { + this.index++; + } consumeOptionalCharacter(code: number): boolean { if (this.next.isCharacter(code)) { @@ -340,8 +347,12 @@ export class _ParseAST { } } - peekKeywordLet(): boolean { return this.next.isKeywordLet(); } - peekKeywordAs(): boolean { return this.next.isKeywordAs(); } + peekKeywordLet(): boolean { + return this.next.isKeywordLet(); + } + peekKeywordAs(): boolean { + return this.next.isKeywordAs(); + } expectCharacter(code: number) { if (this.consumeOptionalCharacter(code)) return; @@ -428,7 +439,9 @@ export class _ParseAST { return result; } - parseExpression(): AST { return this.parseConditional(); } + parseExpression(): AST { + return this.parseConditional(); + } parseConditional(): AST { const start = this.inputIndex; @@ -984,8 +997,8 @@ export class _ParseAST { (this.rbracesExpected <= 0 || !n.isCharacter(chars.$RBRACE)) && (this.rbracketsExpected <= 0 || !n.isCharacter(chars.$RBRACKET))) { if (this.next.isError()) { - this.errors.push(new ParserError( - this.next.toString() !, this.input, this.locationText(), this.location)); + this.errors.push( + new ParserError(this.next.toString()!, this.input, this.locationText(), this.location)); } this.advance(); n = this.next; @@ -1014,9 +1027,13 @@ class SimpleExpressionChecker implements AstVisitor { visitFunctionCall(ast: FunctionCall, context: any) {} - visitLiteralArray(ast: LiteralArray, context: any) { this.visitAll(ast.expressions); } + visitLiteralArray(ast: LiteralArray, context: any) { + this.visitAll(ast.expressions); + } - visitLiteralMap(ast: LiteralMap, context: any) { this.visitAll(ast.values); } + visitLiteralMap(ast: LiteralMap, context: any) { + this.visitAll(ast.values); + } visitBinary(ast: Binary, context: any) {} @@ -1026,13 +1043,17 @@ class SimpleExpressionChecker implements AstVisitor { visitConditional(ast: Conditional, context: any) {} - visitPipe(ast: BindingPipe, context: any) { this.errors.push('pipes'); } + visitPipe(ast: BindingPipe, context: any) { + this.errors.push('pipes'); + } visitKeyedRead(ast: KeyedRead, context: any) {} visitKeyedWrite(ast: KeyedWrite, context: any) {} - visitAll(asts: any[]): any[] { return asts.map(node => node.visit(this)); } + visitAll(asts: any[]): any[] { + return asts.map(node => node.visit(this)); + } visitChain(ast: Chain, context: any) {} @@ -1052,5 +1073,7 @@ class IvySimpleExpressionChecker extends SimpleExpressionChecker { ast.right.visit(this); } - visitPrefixNot(ast: PrefixNot, context: any) { ast.expression.visit(this); } + visitPrefixNot(ast: PrefixNot, context: any) { + ast.expression.visit(this); + } } diff --git a/packages/compiler/src/i18n/digest.ts b/packages/compiler/src/i18n/digest.ts index e81a26e6a1a7d..58ff21d394501 100644 --- a/packages/compiler/src/i18n/digest.ts +++ b/packages/compiler/src/i18n/digest.ts @@ -48,7 +48,9 @@ export function computeDecimalDigest(message: i18n.Message): string { * @internal */ class _SerializerVisitor implements i18n.Visitor { - visitText(text: i18n.Text, context: any): any { return text.value; } + visitText(text: i18n.Text, context: any): any { + return text.value; + } visitContainer(container: i18n.Container, context: any): any { return `[${container.children.map(child => child.visit(this)).join(', ')}]`; @@ -63,7 +65,8 @@ class _SerializerVisitor implements i18n.Visitor { visitTagPlaceholder(ph: i18n.TagPlaceholder, context: any): any { return ph.isVoid ? `<ph tag name="${ph.startName}"/>` : - `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`; + `<ph tag name="${ph.startName}">${ + ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`; } visitPlaceholder(ph: i18n.Placeholder, context: any): any { diff --git a/packages/compiler/src/i18n/extractor.ts b/packages/compiler/src/i18n/extractor.ts index 8b950feb6d109..43a6d06489e0c 100644 --- a/packages/compiler/src/i18n/extractor.ts +++ b/packages/compiler/src/i18n/extractor.ts @@ -78,11 +78,11 @@ export class Extractor { // Template URL points to either an HTML or TS file depending on // whether the file is used with `templateUrl:` or `template:`, // respectively. - const templateUrl = compMeta.template !.templateUrl !; + const templateUrl = compMeta.template !.templateUrl!; const interpolationConfig = InterpolationConfig.fromArray(compMeta.template !.interpolation); errors.push(...this.messageBundle.updateFromTemplate( - html, templateUrl, interpolationConfig) !); + html, templateUrl, interpolationConfig)!); }); }); diff --git a/packages/compiler/src/i18n/extractor_merger.ts b/packages/compiler/src/i18n/extractor_merger.ts index 0a45f6db38119..5133826c933ee 100644 --- a/packages/compiler/src/i18n/extractor_merger.ts +++ b/packages/compiler/src/i18n/extractor_merger.ts @@ -9,8 +9,9 @@ import * as html from '../ml_parser/ast'; import {InterpolationConfig} from '../ml_parser/interpolation_config'; import {ParseTreeResult} from '../ml_parser/parser'; + import * as i18n from './i18n_ast'; -import {I18nMessageFactory, createI18nMessageFactory} from './i18n_parser'; +import {createI18nMessageFactory, I18nMessageFactory} from './i18n_parser'; import {I18nError} from './parse_util'; import {TranslationBundle} from './translation_bundle'; @@ -56,44 +57,44 @@ enum _VisitorMode { */ class _Visitor implements html.Visitor { // TODO(issue/24571): remove '!'. - private _depth !: number; + private _depth!: number; // <el i18n>...</el> // TODO(issue/24571): remove '!'. - private _inI18nNode !: boolean; + private _inI18nNode!: boolean; // TODO(issue/24571): remove '!'. - private _inImplicitNode !: boolean; + private _inImplicitNode!: boolean; // <!--i18n-->...<!--/i18n--> // TODO(issue/24571): remove '!'. - private _inI18nBlock !: boolean; + private _inI18nBlock!: boolean; // TODO(issue/24571): remove '!'. - private _blockMeaningAndDesc !: string; + private _blockMeaningAndDesc!: string; // TODO(issue/24571): remove '!'. - private _blockChildren !: html.Node[]; + private _blockChildren!: html.Node[]; // TODO(issue/24571): remove '!'. - private _blockStartDepth !: number; + private _blockStartDepth!: number; // {<icu message>} // TODO(issue/24571): remove '!'. - private _inIcu !: boolean; + private _inIcu!: boolean; // set to void 0 when not in a section private _msgCountAtSectionStart: number|undefined; // TODO(issue/24571): remove '!'. - private _errors !: I18nError[]; + private _errors!: I18nError[]; // TODO(issue/24571): remove '!'. - private _mode !: _VisitorMode; + private _mode!: _VisitorMode; // _VisitorMode.Extract only // TODO(issue/24571): remove '!'. - private _messages !: i18n.Message[]; + private _messages!: i18n.Message[]; // _VisitorMode.Merge only // TODO(issue/24571): remove '!'. - private _translations !: TranslationBundle; + private _translations!: TranslationBundle; // TODO(issue/24571): remove '!'. - private _createI18nMessage !: I18nMessageFactory; + private _createI18nMessage!: I18nMessageFactory; constructor(private _implicitTags: string[], private _implicitAttrs: {[k: string]: string[]}) {} @@ -123,7 +124,7 @@ class _Visitor implements html.Visitor { this._translations = translations; // Construct a single fake root element - const wrapper = new html.Element('wrapper', [], nodes, undefined !, undefined, undefined); + const wrapper = new html.Element('wrapper', [], nodes, undefined!, undefined, undefined); const translatedNode = wrapper.visit(this, null); @@ -193,14 +194,14 @@ class _Visitor implements html.Visitor { i18nCommentsWarned = true; const details = comment.sourceSpan.details ? `, ${comment.sourceSpan.details}` : ''; // TODO(ocombe): use a log service once there is a public one available - console.warn( - `I18n comments are deprecated, use an <ng-container> element instead (${comment.sourceSpan.start}${details})`); + console.warn(`I18n comments are deprecated, use an <ng-container> element instead (${ + comment.sourceSpan.start}${details})`); } this._inI18nBlock = true; this._blockStartDepth = this._depth; this._blockChildren = []; this._blockMeaningAndDesc = - comment.value !.replace(_I18N_COMMENT_PREFIX_REGEXP, '').trim(); + comment.value!.replace(_I18N_COMMENT_PREFIX_REGEXP, '').trim(); this._openTranslatableSection(comment); } } else { @@ -208,7 +209,7 @@ class _Visitor implements html.Visitor { if (this._depth == this._blockStartDepth) { this._closeTranslatableSection(comment, this._blockChildren); this._inI18nBlock = false; - const message = this._addMessage(this._blockChildren, this._blockMeaningAndDesc) !; + const message = this._addMessage(this._blockChildren, this._blockMeaningAndDesc)!; // merge attributes in sections const nodes = this._translateMessage(comment, message); return html.visitAll(this, nodes); @@ -234,7 +235,7 @@ class _Visitor implements html.Visitor { const wasInI18nNode = this._inI18nNode; const wasInImplicitNode = this._inImplicitNode; let childNodes: html.Node[] = []; - let translatedChildNodes: html.Node[] = undefined !; + let translatedChildNodes: html.Node[] = undefined!; // Extract: // - top level nodes with the (implicit) "i18n" attribute if not already in a section @@ -249,7 +250,7 @@ class _Visitor implements html.Visitor { if (!this._isInTranslatableSection && !this._inIcu) { if (i18nAttr || isTopLevelImplicit) { this._inI18nNode = true; - const message = this._addMessage(el.children, i18nMeta) !; + const message = this._addMessage(el.children, i18nMeta)!; translatedChildNodes = this._translateMessage(el, message); } @@ -400,12 +401,14 @@ class _Visitor implements html.Visitor { } else { this._reportError( el, - `Unexpected translation for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`); + `Unexpected translation for attribute "${attr.name}" (id="${ + id || this._translations.digest(message)}")`); } } else { this._reportError( el, - `Translation unavailable for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`); + `Translation unavailable for attribute "${attr.name}" (id="${ + id || this._translations.digest(message)}")`); } } else { translatedAttributes.push(attr); @@ -476,7 +479,7 @@ class _Visitor implements html.Visitor { 0); if (significantChildren == 1) { - for (let i = this._messages.length - 1; i >= startIndex !; i--) { + for (let i = this._messages.length - 1; i >= startIndex!; i--) { const ast = this._messages[i].nodes; if (!(ast.length == 1 && ast[0] instanceof i18n.Text)) { this._messages.splice(i, 1); @@ -489,7 +492,7 @@ class _Visitor implements html.Visitor { } private _reportError(node: html.Node, msg: string): void { - this._errors.push(new I18nError(node.sourceSpan !, msg)); + this._errors.push(new I18nError(node.sourceSpan!, msg)); } } diff --git a/packages/compiler/src/i18n/i18n_ast.ts b/packages/compiler/src/i18n/i18n_ast.ts index 9ec6afe1ba611..ef0b1156f024e 100644 --- a/packages/compiler/src/i18n/i18n_ast.ts +++ b/packages/compiler/src/i18n/i18n_ast.ts @@ -57,24 +57,30 @@ export interface Node { export class Text implements Node { constructor(public value: string, public sourceSpan: ParseSourceSpan) {} - visit(visitor: Visitor, context?: any): any { return visitor.visitText(this, context); } + visit(visitor: Visitor, context?: any): any { + return visitor.visitText(this, context); + } } // TODO(vicb): do we really need this node (vs an array) ? export class Container implements Node { constructor(public children: Node[], public sourceSpan: ParseSourceSpan) {} - visit(visitor: Visitor, context?: any): any { return visitor.visitContainer(this, context); } + visit(visitor: Visitor, context?: any): any { + return visitor.visitContainer(this, context); + } } export class Icu implements Node { // TODO(issue/24571): remove '!'. - public expressionPlaceholder !: string; + public expressionPlaceholder!: string; constructor( public expression: string, public type: string, public cases: {[k: string]: Node}, public sourceSpan: ParseSourceSpan) {} - visit(visitor: Visitor, context?: any): any { return visitor.visitIcu(this, context); } + visit(visitor: Visitor, context?: any): any { + return visitor.visitIcu(this, context); + } } export class TagPlaceholder implements Node { @@ -83,13 +89,17 @@ export class TagPlaceholder implements Node { public closeName: string, public children: Node[], public isVoid: boolean, public sourceSpan: ParseSourceSpan) {} - visit(visitor: Visitor, context?: any): any { return visitor.visitTagPlaceholder(this, context); } + visit(visitor: Visitor, context?: any): any { + return visitor.visitTagPlaceholder(this, context); + } } export class Placeholder implements Node { constructor(public value: string, public name: string, public sourceSpan: ParseSourceSpan) {} - visit(visitor: Visitor, context?: any): any { return visitor.visitPlaceholder(this, context); } + visit(visitor: Visitor, context?: any): any { + return visitor.visitPlaceholder(this, context); + } } export class IcuPlaceholder implements Node { @@ -97,7 +107,9 @@ export class IcuPlaceholder implements Node { previousMessage?: Message; constructor(public value: Icu, public name: string, public sourceSpan: ParseSourceSpan) {} - visit(visitor: Visitor, context?: any): any { return visitor.visitIcuPlaceholder(this, context); } + visit(visitor: Visitor, context?: any): any { + return visitor.visitIcuPlaceholder(this, context); + } } /** @@ -106,7 +118,7 @@ export class IcuPlaceholder implements Node { * This information is either a `Message`, which indicates it is the root of an i18n message, or a * `Node`, which indicates is it part of a containing `Message`. */ -export type I18nMeta = Message | Node; +export type I18nMeta = Message|Node; export interface Visitor { visitText(text: Text, context?: any): any; @@ -119,7 +131,9 @@ export interface Visitor { // Clone the AST export class CloneVisitor implements Visitor { - visitText(text: Text, context?: any): Text { return new Text(text.value, text.sourceSpan); } + visitText(text: Text, context?: any): Text { + return new Text(text.value, text.sourceSpan); + } visitContainer(container: Container, context?: any): Container { const children = container.children.map(n => n.visit(this, context)); @@ -158,7 +172,9 @@ export class RecurseVisitor implements Visitor { } visitIcu(icu: Icu, context?: any): any { - Object.keys(icu.cases).forEach(k => { icu.cases[k].visit(this); }); + Object.keys(icu.cases).forEach(k => { + icu.cases[k].visit(this); + }); } visitTagPlaceholder(ph: TagPlaceholder, context?: any): any { diff --git a/packages/compiler/src/i18n/i18n_parser.ts b/packages/compiler/src/i18n/i18n_parser.ts index f1be0895c82d4..5c19595f72427 100644 --- a/packages/compiler/src/i18n/i18n_parser.ts +++ b/packages/compiler/src/i18n/i18n_parser.ts @@ -83,7 +83,7 @@ class _I18nVisitor implements html.Visitor { const isVoid: boolean = getHtmlTagDefinition(el.name).isVoid; const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid); - context.placeholderToContent[startPhName] = el.sourceSpan !.toString(); + context.placeholderToContent[startPhName] = el.sourceSpan!.toString(); let closePhName = ''; @@ -93,7 +93,7 @@ class _I18nVisitor implements html.Visitor { } const node = new i18n.TagPlaceholder( - el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan !); + el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan!); return context.visitNodeFn(el, node); } @@ -103,7 +103,7 @@ class _I18nVisitor implements html.Visitor { } visitText(text: html.Text, context: I18nMessageVisitorContext): i18n.Node { - const node = this._visitTextWithInterpolation(text.value, text.sourceSpan !, context); + const node = this._visitTextWithInterpolation(text.value, text.sourceSpan!, context); return context.visitNodeFn(text, node); } diff --git a/packages/compiler/src/i18n/message_bundle.ts b/packages/compiler/src/i18n/message_bundle.ts index 882ac54eb3d62..dcc7269c23f2e 100644 --- a/packages/compiler/src/i18n/message_bundle.ts +++ b/packages/compiler/src/i18n/message_bundle.ts @@ -47,7 +47,9 @@ export class MessageBundle { // Return the message in the internal format // The public (serialized) format might be different, see the `write` method. - getMessages(): i18n.Message[] { return this._messages; } + getMessages(): i18n.Message[] { + return this._messages; + } write(serializer: Serializer, filterSources?: (path: string) => string): string { const messages: {[id: string]: i18n.Message} = {}; @@ -88,18 +90,18 @@ class MapPlaceholderNames extends i18n.CloneVisitor { } visitTagPlaceholder(ph: i18n.TagPlaceholder, mapper: PlaceholderMapper): i18n.TagPlaceholder { - const startName = mapper.toPublicName(ph.startName) !; - const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) ! : ph.closeName; + const startName = mapper.toPublicName(ph.startName)!; + const closeName = ph.closeName ? mapper.toPublicName(ph.closeName)! : ph.closeName; const children = ph.children.map(n => n.visit(this, mapper)); return new i18n.TagPlaceholder( ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan); } visitPlaceholder(ph: i18n.Placeholder, mapper: PlaceholderMapper): i18n.Placeholder { - return new i18n.Placeholder(ph.value, mapper.toPublicName(ph.name) !, ph.sourceSpan); + return new i18n.Placeholder(ph.value, mapper.toPublicName(ph.name)!, ph.sourceSpan); } visitIcuPlaceholder(ph: i18n.IcuPlaceholder, mapper: PlaceholderMapper): i18n.IcuPlaceholder { - return new i18n.IcuPlaceholder(ph.value, mapper.toPublicName(ph.name) !, ph.sourceSpan); + return new i18n.IcuPlaceholder(ph.value, mapper.toPublicName(ph.name)!, ph.sourceSpan); } } diff --git a/packages/compiler/src/i18n/parse_util.ts b/packages/compiler/src/i18n/parse_util.ts index 37acd859ac61d..0a02a5b4169bf 100644 --- a/packages/compiler/src/i18n/parse_util.ts +++ b/packages/compiler/src/i18n/parse_util.ts @@ -12,5 +12,7 @@ import {ParseError, ParseSourceSpan} from '../parse_util'; * An i18n error. */ export class I18nError extends ParseError { - constructor(span: ParseSourceSpan, msg: string) { super(span, msg); } + constructor(span: ParseSourceSpan, msg: string) { + super(span, msg); + } } diff --git a/packages/compiler/src/i18n/serializers/placeholder.ts b/packages/compiler/src/i18n/serializers/placeholder.ts index def0996964c6d..34b131cb6b41e 100644 --- a/packages/compiler/src/i18n/serializers/placeholder.ts +++ b/packages/compiler/src/i18n/serializers/placeholder.ts @@ -106,7 +106,9 @@ export class PlaceholderRegistry { return start + strAttrs + end; } - private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); } + private _hashClosingTag(tag: string): string { + return this._hashTag(`/${tag}`, {}, false); + } private _generateUniqueName(base: string): string { const seen = this._placeHolderNameCounts.hasOwnProperty(base); diff --git a/packages/compiler/src/i18n/serializers/serializer.ts b/packages/compiler/src/i18n/serializers/serializer.ts index 4fb7dc8aee6c1..5cca7d2543dc7 100644 --- a/packages/compiler/src/i18n/serializers/serializer.ts +++ b/packages/compiler/src/i18n/serializers/serializer.ts @@ -15,13 +15,15 @@ export abstract class Serializer { abstract write(messages: i18n.Message[], locale: string|null): string; abstract load(content: string, url: string): - {locale: string | null, i18nNodesByMsgId: {[msgId: string]: i18n.Node[]}}; + {locale: string|null, i18nNodesByMsgId: {[msgId: string]: i18n.Node[]}}; abstract digest(message: i18n.Message): string; // Creates a name mapper, see `PlaceholderMapper` // Returning `null` means that no name mapping is used. - createNameMapper(message: i18n.Message): PlaceholderMapper|null { return null; } + createNameMapper(message: i18n.Message): PlaceholderMapper|null { + return null; + } } /** @@ -61,7 +63,9 @@ export class SimplePlaceholderMapper extends i18n.RecurseVisitor implements Plac null; } - visitText(text: i18n.Text, context?: any): any { return null; } + visitText(text: i18n.Text, context?: any): any { + return null; + } visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): any { this.visitPlaceholderName(ph.startName); @@ -69,7 +73,9 @@ export class SimplePlaceholderMapper extends i18n.RecurseVisitor implements Plac this.visitPlaceholderName(ph.closeName); } - visitPlaceholder(ph: i18n.Placeholder, context?: any): any { this.visitPlaceholderName(ph.name); } + visitPlaceholder(ph: i18n.Placeholder, context?: any): any { + this.visitPlaceholderName(ph.name); + } visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { this.visitPlaceholderName(ph.name); diff --git a/packages/compiler/src/i18n/serializers/xliff.ts b/packages/compiler/src/i18n/serializers/xliff.ts index b546ce14e1936..732d4eecfb258 100644 --- a/packages/compiler/src/i18n/serializers/xliff.ts +++ b/packages/compiler/src/i18n/serializers/xliff.ts @@ -46,9 +46,9 @@ export class Xliff extends Serializer { new xml.CR(10), new xml.Tag( _CONTEXT_TAG, {'context-type': 'sourcefile'}, [new xml.Text(source.filePath)]), - new xml.CR(10), new xml.Tag( - _CONTEXT_TAG, {'context-type': 'linenumber'}, - [new xml.Text(`${source.startLine}`)]), + new xml.CR(10), + new xml.Tag(_CONTEXT_TAG, {'context-type': 'linenumber'}, [new xml.Text( + `${source.startLine}`)]), new xml.CR(8)); contextTags.push(new xml.CR(8), contextGroupTag); }); @@ -112,14 +112,18 @@ export class Xliff extends Serializer { throw new Error(`xliff parse errors:\n${errors.join('\n')}`); } - return {locale: locale !, i18nNodesByMsgId}; + return {locale: locale!, i18nNodesByMsgId}; } - digest(message: i18n.Message): string { return digest(message); } + digest(message: i18n.Message): string { + return digest(message); + } } class _WriteVisitor implements i18n.Visitor { - visitText(text: i18n.Text, context?: any): xml.Node[] { return [new xml.Text(text.value)]; } + visitText(text: i18n.Text, context?: any): xml.Node[] { + return [new xml.Text(text.value)]; + } visitContainer(container: i18n.Container, context?: any): xml.Node[] { const nodes: xml.Node[] = []; @@ -161,8 +165,8 @@ class _WriteVisitor implements i18n.Visitor { } visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] { - const equivText = - `{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map((value: string) => value + ' {...}').join(' ')}}`; + const equivText = `{${ph.value.expression}, ${ph.value.type}, ${ + Object.keys(ph.value.cases).map((value: string) => value + ' {...}').join(' ')}}`; return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name, 'equiv-text': equivText})]; } @@ -175,11 +179,11 @@ class _WriteVisitor implements i18n.Visitor { // Extract messages as xml nodes from the xliff file class XliffParser implements ml.Visitor { // TODO(issue/24571): remove '!'. - private _unitMlString !: string | null; + private _unitMlString!: string|null; // TODO(issue/24571): remove '!'. - private _errors !: I18nError[]; + private _errors!: I18nError[]; // TODO(issue/24571): remove '!'. - private _msgIdToHtml !: {[msgId: string]: string}; + private _msgIdToHtml!: {[msgId: string]: string}; private _locale: string|null = null; parse(xliff: string, url: string) { @@ -201,7 +205,7 @@ class XliffParser implements ml.Visitor { visitElement(element: ml.Element, context: any): any { switch (element.name) { case _UNIT_TAG: - this._unitMlString = null !; + this._unitMlString = null!; const idAttr = element.attrs.find((attr) => attr.name === 'id'); if (!idAttr) { this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`); @@ -227,9 +231,9 @@ class XliffParser implements ml.Visitor { break; case _TARGET_TAG: - const innerTextStart = element.startSourceSpan !.end.offset; - const innerTextEnd = element.endSourceSpan !.start.offset; - const content = element.startSourceSpan !.start.file.content; + const innerTextStart = element.startSourceSpan!.end.offset; + const innerTextEnd = element.endSourceSpan!.start.offset; + const content = element.startSourceSpan!.start.file.content; const innerText = content.slice(innerTextStart, innerTextEnd); this._unitMlString = innerText; break; @@ -260,14 +264,14 @@ class XliffParser implements ml.Visitor { visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {} private _addError(node: ml.Node, message: string): void { - this._errors.push(new I18nError(node.sourceSpan !, message)); + this._errors.push(new I18nError(node.sourceSpan!, message)); } } // Convert ml nodes (xliff syntax) to i18n nodes class XmlToI18n implements ml.Visitor { // TODO(issue/24571): remove '!'. - private _errors !: I18nError[]; + private _errors!: I18nError[]; convert(message: string, url: string) { const xmlIcu = new XmlParser().parse(message, url, {tokenizeExpansionForms: true}); @@ -283,13 +287,15 @@ class XmlToI18n implements ml.Visitor { }; } - visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan !); } + visitText(text: ml.Text, context: any) { + return new i18n.Text(text.value, text.sourceSpan!); + } visitElement(el: ml.Element, context: any): i18n.Placeholder|ml.Node[]|null { if (el.name === _PLACEHOLDER_TAG) { const nameAttr = el.attrs.find((attr) => attr.name === 'id'); if (nameAttr) { - return new i18n.Placeholder('', nameAttr.value, el.sourceSpan !); + return new i18n.Placeholder('', nameAttr.value, el.sourceSpan!); } this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "id" attribute`); @@ -326,7 +332,7 @@ class XmlToI18n implements ml.Visitor { visitAttribute(attribute: ml.Attribute, context: any) {} private _addError(node: ml.Node, message: string): void { - this._errors.push(new I18nError(node.sourceSpan !, message)); + this._errors.push(new I18nError(node.sourceSpan!, message)); } } diff --git a/packages/compiler/src/i18n/serializers/xliff2.ts b/packages/compiler/src/i18n/serializers/xliff2.ts index a8ba85d597c89..a0d62bc7dc176 100644 --- a/packages/compiler/src/i18n/serializers/xliff2.ts +++ b/packages/compiler/src/i18n/serializers/xliff2.ts @@ -54,8 +54,8 @@ export class Xliff2 extends Serializer { message.sources.forEach((source: i18n.MessageSpan) => { notes.children.push(new xml.CR(8), new xml.Tag('note', {category: 'location'}, [ - new xml.Text( - `${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`) + new xml.Text(`${source.filePath}:${source.startLine}${ + source.endLine !== source.startLine ? ',' + source.endLine : ''}`) ])); }); @@ -105,17 +105,21 @@ export class Xliff2 extends Serializer { throw new Error(`xliff2 parse errors:\n${errors.join('\n')}`); } - return {locale: locale !, i18nNodesByMsgId}; + return {locale: locale!, i18nNodesByMsgId}; } - digest(message: i18n.Message): string { return decimalDigest(message); } + digest(message: i18n.Message): string { + return decimalDigest(message); + } } class _WriteVisitor implements i18n.Visitor { // TODO(issue/24571): remove '!'. - private _nextPlaceholderId !: number; + private _nextPlaceholderId!: number; - visitText(text: i18n.Text, context?: any): xml.Node[] { return [new xml.Text(text.value)]; } + visitText(text: i18n.Text, context?: any): xml.Node[] { + return [new xml.Text(text.value)]; + } visitContainer(container: i18n.Container, context?: any): xml.Node[] { const nodes: xml.Node[] = []; @@ -192,11 +196,11 @@ class _WriteVisitor implements i18n.Visitor { // Extract messages as xml nodes from the xliff file class Xliff2Parser implements ml.Visitor { // TODO(issue/24571): remove '!'. - private _unitMlString !: string | null; + private _unitMlString!: string|null; // TODO(issue/24571): remove '!'. - private _errors !: I18nError[]; + private _errors!: I18nError[]; // TODO(issue/24571): remove '!'. - private _msgIdToHtml !: {[msgId: string]: string}; + private _msgIdToHtml!: {[msgId: string]: string}; private _locale: string|null = null; parse(xliff: string, url: string) { @@ -242,9 +246,9 @@ class Xliff2Parser implements ml.Visitor { break; case _TARGET_TAG: - const innerTextStart = element.startSourceSpan !.end.offset; - const innerTextEnd = element.endSourceSpan !.start.offset; - const content = element.startSourceSpan !.start.file.content; + const innerTextStart = element.startSourceSpan!.end.offset; + const innerTextEnd = element.endSourceSpan!.start.offset; + const content = element.startSourceSpan!.start.file.content; const innerText = content.slice(innerTextStart, innerTextEnd); this._unitMlString = innerText; break; @@ -290,7 +294,7 @@ class Xliff2Parser implements ml.Visitor { // Convert ml nodes (xliff syntax) to i18n nodes class XmlToI18n implements ml.Visitor { // TODO(issue/24571): remove '!'. - private _errors !: I18nError[]; + private _errors!: I18nError[]; convert(message: string, url: string) { const xmlIcu = new XmlParser().parse(message, url, {tokenizeExpansionForms: true}); @@ -306,7 +310,9 @@ class XmlToI18n implements ml.Visitor { }; } - visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan); } + visitText(text: ml.Text, context: any) { + return new i18n.Text(text.value, text.sourceSpan); + } visitElement(el: ml.Element, context: any): i18n.Node[]|null { switch (el.name) { diff --git a/packages/compiler/src/i18n/serializers/xmb.ts b/packages/compiler/src/i18n/serializers/xmb.ts index eeddddc869e38..a12e785506d2d 100644 --- a/packages/compiler/src/i18n/serializers/xmb.ts +++ b/packages/compiler/src/i18n/serializers/xmb.ts @@ -57,10 +57,10 @@ export class Xmb extends Serializer { let sourceTags: xml.Tag[] = []; message.sources.forEach((source: i18n.MessageSpan) => { - sourceTags.push(new xml.Tag(_SOURCE_TAG, {}, [ - new xml.Text( - `${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`) - ])); + sourceTags.push(new xml.Tag( + _SOURCE_TAG, {}, + [new xml.Text(`${source.filePath}:${source.startLine}${ + source.endLine !== source.startLine ? ',' + source.endLine : ''}`)])); }); rootNode.children.push( @@ -85,7 +85,9 @@ export class Xmb extends Serializer { throw new Error('Unsupported'); } - digest(message: i18n.Message): string { return digest(message); } + digest(message: i18n.Message): string { + return digest(message); + } createNameMapper(message: i18n.Message): PlaceholderMapper { @@ -94,7 +96,9 @@ export class Xmb extends Serializer { } class _Visitor implements i18n.Visitor { - visitText(text: i18n.Text, context?: any): xml.Node[] { return [new xml.Text(text.value)]; } + visitText(text: i18n.Text, context?: any): xml.Node[] { + return [new xml.Text(text.value)]; + } visitContainer(container: i18n.Container, context: any): xml.Node[] { const nodes: xml.Node[] = []; diff --git a/packages/compiler/src/i18n/serializers/xml_helper.ts b/packages/compiler/src/i18n/serializers/xml_helper.ts index 27a8d10c5a8f7..6863cce5df897 100644 --- a/packages/compiler/src/i18n/serializers/xml_helper.ts +++ b/packages/compiler/src/i18n/serializers/xml_helper.ts @@ -25,7 +25,9 @@ class _Visitor implements IVisitor { return `<${tag.name}${strAttrs}>${strChildren.join('')}</${tag.name}>`; } - visitText(text: Text): string { return text.value; } + visitText(text: Text): string { + return text.value; + } visitDeclaration(decl: Declaration): string { return `<?xml${this._serializeAttributes(decl.attrs)} ?>`; @@ -47,7 +49,9 @@ export function serialize(nodes: Node[]): string { return nodes.map((node: Node): string => node.visit(_visitor)).join(''); } -export interface Node { visit(visitor: IVisitor): any; } +export interface Node { + visit(visitor: IVisitor): any; +} export class Declaration implements Node { public attrs: {[k: string]: string} = {}; @@ -58,13 +62,17 @@ export class Declaration implements Node { }); } - visit(visitor: IVisitor): any { return visitor.visitDeclaration(this); } + visit(visitor: IVisitor): any { + return visitor.visitDeclaration(this); + } } export class Doctype implements Node { constructor(public rootTag: string, public dtd: string) {} - visit(visitor: IVisitor): any { return visitor.visitDoctype(this); } + visit(visitor: IVisitor): any { + return visitor.visitDoctype(this); + } } export class Tag implements Node { @@ -78,18 +86,26 @@ export class Tag implements Node { }); } - visit(visitor: IVisitor): any { return visitor.visitTag(this); } + visit(visitor: IVisitor): any { + return visitor.visitTag(this); + } } export class Text implements Node { value: string; - constructor(unescapedValue: string) { this.value = escapeXml(unescapedValue); } + constructor(unescapedValue: string) { + this.value = escapeXml(unescapedValue); + } - visit(visitor: IVisitor): any { return visitor.visitText(this); } + visit(visitor: IVisitor): any { + return visitor.visitText(this); + } } export class CR extends Text { - constructor(ws: number = 0) { super(`\n${new Array(ws + 1).join(' ')}`); } + constructor(ws: number = 0) { + super(`\n${new Array(ws + 1).join(' ')}`); + } } const _ESCAPED_CHARS: [RegExp, string][] = [ diff --git a/packages/compiler/src/i18n/serializers/xtb.ts b/packages/compiler/src/i18n/serializers/xtb.ts index 441c03fa049e0..dede3078827f9 100644 --- a/packages/compiler/src/i18n/serializers/xtb.ts +++ b/packages/compiler/src/i18n/serializers/xtb.ts @@ -19,7 +19,9 @@ const _TRANSLATION_TAG = 'translation'; const _PLACEHOLDER_TAG = 'ph'; export class Xtb extends Serializer { - write(messages: i18n.Message[], locale: string|null): string { throw new Error('Unsupported'); } + write(messages: i18n.Message[], locale: string|null): string { + throw new Error('Unsupported'); + } load(content: string, url: string): {locale: string, i18nNodesByMsgId: {[msgId: string]: i18n.Node[]}} { @@ -49,10 +51,12 @@ export class Xtb extends Serializer { throw new Error(`xtb parse errors:\n${errors.join('\n')}`); } - return {locale: locale !, i18nNodesByMsgId}; + return {locale: locale!, i18nNodesByMsgId}; } - digest(message: i18n.Message): string { return digest(message); } + digest(message: i18n.Message): string { + return digest(message); + } createNameMapper(message: i18n.Message): PlaceholderMapper { return new SimplePlaceholderMapper(message, toPublicName); @@ -68,18 +72,20 @@ function createLazyProperty(messages: any, id: string, valueFn: () => any) { Object.defineProperty(messages, id, {enumerable: true, value}); return value; }, - set: _ => { throw new Error('Could not overwrite an XTB translation'); }, + set: _ => { + throw new Error('Could not overwrite an XTB translation'); + }, }); } // Extract messages as xml nodes from the xtb file class XtbParser implements ml.Visitor { // TODO(issue/24571): remove '!'. - private _bundleDepth !: number; + private _bundleDepth!: number; // TODO(issue/24571): remove '!'. - private _errors !: I18nError[]; + private _errors!: I18nError[]; // TODO(issue/24571): remove '!'. - private _msgIdToHtml !: {[msgId: string]: string}; + private _msgIdToHtml!: {[msgId: string]: string}; private _locale: string|null = null; parse(xtb: string, url: string) { @@ -124,10 +130,10 @@ class XtbParser implements ml.Visitor { if (this._msgIdToHtml.hasOwnProperty(id)) { this._addError(element, `Duplicated translations for msg ${id}`); } else { - const innerTextStart = element.startSourceSpan !.end.offset; - const innerTextEnd = element.endSourceSpan !.start.offset; - const content = element.startSourceSpan !.start.file.content; - const innerText = content.slice(innerTextStart !, innerTextEnd !); + const innerTextStart = element.startSourceSpan!.end.offset; + const innerTextEnd = element.endSourceSpan!.start.offset; + const content = element.startSourceSpan!.start.file.content; + const innerText = content.slice(innerTextStart!, innerTextEnd!); this._msgIdToHtml[id] = innerText; } } @@ -149,14 +155,14 @@ class XtbParser implements ml.Visitor { visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {} private _addError(node: ml.Node, message: string): void { - this._errors.push(new I18nError(node.sourceSpan !, message)); + this._errors.push(new I18nError(node.sourceSpan!, message)); } } // Convert ml nodes (xtb syntax) to i18n nodes class XmlToI18n implements ml.Visitor { // TODO(issue/24571): remove '!'. - private _errors !: I18nError[]; + private _errors!: I18nError[]; convert(message: string, url: string) { const xmlIcu = new XmlParser().parse(message, url, {tokenizeExpansionForms: true}); @@ -172,7 +178,9 @@ class XmlToI18n implements ml.Visitor { }; } - visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan !); } + visitText(text: ml.Text, context: any) { + return new i18n.Text(text.value, text.sourceSpan!); + } visitExpansion(icu: ml.Expansion, context: any) { const caseMap: {[value: string]: i18n.Node} = {}; @@ -195,7 +203,7 @@ class XmlToI18n implements ml.Visitor { if (el.name === _PLACEHOLDER_TAG) { const nameAttr = el.attrs.find((attr) => attr.name === 'name'); if (nameAttr) { - return new i18n.Placeholder('', nameAttr.value, el.sourceSpan !); + return new i18n.Placeholder('', nameAttr.value, el.sourceSpan!); } this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "name" attribute`); @@ -210,6 +218,6 @@ class XmlToI18n implements ml.Visitor { visitAttribute(attribute: ml.Attribute, context: any) {} private _addError(node: ml.Node, message: string): void { - this._errors.push(new I18nError(node.sourceSpan !, message)); + this._errors.push(new I18nError(node.sourceSpan!, message)); } } diff --git a/packages/compiler/src/i18n/translation_bundle.ts b/packages/compiler/src/i18n/translation_bundle.ts index 30da42a421061..62bfb357e9c13 100644 --- a/packages/compiler/src/i18n/translation_bundle.ts +++ b/packages/compiler/src/i18n/translation_bundle.ts @@ -30,7 +30,7 @@ export class TranslationBundle { missingTranslationStrategy: MissingTranslationStrategy = MissingTranslationStrategy.Warning, console?: Console) { this._i18nToHtml = new I18nToHtmlVisitor( - _i18nNodesByMsgId, locale, digest, mapperFactory !, missingTranslationStrategy, console); + _i18nNodesByMsgId, locale, digest, mapperFactory!, missingTranslationStrategy, console); } // Creates a `TranslationBundle` by parsing the given `content` with the `serializer`. @@ -40,7 +40,7 @@ export class TranslationBundle { console?: Console): TranslationBundle { const {locale, i18nNodesByMsgId} = serializer.load(content, url); const digestFn = (m: i18n.Message) => serializer.digest(m); - const mapperFactory = (m: i18n.Message) => serializer.createNameMapper(m) !; + const mapperFactory = (m: i18n.Message) => serializer.createNameMapper(m)!; return new TranslationBundle( i18nNodesByMsgId, locale, digestFn, mapperFactory, missingTranslationStrategy, console); } @@ -56,16 +56,18 @@ export class TranslationBundle { return html.nodes; } - has(srcMsg: i18n.Message): boolean { return this.digest(srcMsg) in this._i18nNodesByMsgId; } + has(srcMsg: i18n.Message): boolean { + return this.digest(srcMsg) in this._i18nNodesByMsgId; + } } class I18nToHtmlVisitor implements i18n.Visitor { // TODO(issue/24571): remove '!'. - private _srcMsg !: i18n.Message; + private _srcMsg!: i18n.Message; private _contextStack: {msg: i18n.Message, mapper: (name: string) => string}[] = []; private _errors: I18nError[] = []; // TODO(issue/24571): remove '!'. - private _mapper !: (name: string) => string; + private _mapper!: (name: string) => string; constructor( private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {}, private _locale: string|null, @@ -166,7 +168,7 @@ class I18nToHtmlVisitor implements i18n.Visitor { // When there is a translation use its nodes as the source // And create a mapper to convert serialized placeholder names to internal names nodes = this._i18nNodesByMsgId[id]; - this._mapper = (name: string) => mapper ? mapper.toInternalName(name) ! : name; + this._mapper = (name: string) => mapper ? mapper.toInternalName(name)! : name; } else { // When no translation has been found // - report an error / a warning / nothing, @@ -185,7 +187,7 @@ class I18nToHtmlVisitor implements i18n.Visitor { this._mapper = (name: string) => name; } const text = nodes.map(node => node.visit(this)).join(''); - const context = this._contextStack.pop() !; + const context = this._contextStack.pop()!; this._srcMsg = context.msg; this._mapper = context.mapper; return text; diff --git a/packages/compiler/src/injectable_compiler.ts b/packages/compiler/src/injectable_compiler.ts index 6018fb88090e5..d503c868b46ef 100644 --- a/packages/compiler/src/injectable_compiler.ts +++ b/packages/compiler/src/injectable_compiler.ts @@ -121,7 +121,7 @@ export class InjectableCompiler { compile(injectable: CompileInjectableMetadata, ctx: OutputContext): void { if (this.alwaysGenerateDef || injectable.providedIn !== undefined) { - const className = identifierName(injectable.type) !; + const className = identifierName(injectable.type)!; const clazz = new o.ClassStmt( className, null, [ diff --git a/packages/compiler/src/injectable_compiler_2.ts b/packages/compiler/src/injectable_compiler_2.ts index b3b0a2221b684..6fbe107c1656d 100644 --- a/packages/compiler/src/injectable_compiler_2.ts +++ b/packages/compiler/src/injectable_compiler_2.ts @@ -8,8 +8,8 @@ import {Identifiers} from './identifiers'; import * as o from './output/output_ast'; -import {R3DependencyMetadata, R3FactoryDelegateType, R3FactoryMetadata, R3FactoryTarget, compileFactoryFunction} from './render3/r3_factory'; -import {R3Reference, mapToMapExpression, typeWithParameters} from './render3/util'; +import {compileFactoryFunction, R3DependencyMetadata, R3FactoryDelegateType, R3FactoryMetadata, R3FactoryTarget} from './render3/r3_factory'; +import {mapToMapExpression, R3Reference, typeWithParameters} from './render3/util'; export interface InjectableDef { expression: o.Expression; diff --git a/packages/compiler/src/jit/compiler.ts b/packages/compiler/src/jit/compiler.ts index 8fd53b03c8834..ed6d8da08e6b0 100644 --- a/packages/compiler/src/jit/compiler.ts +++ b/packages/compiler/src/jit/compiler.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeSummary, CompileProviderMetadata, CompileStylesheetMetadata, CompileTypeSummary, ProviderMeta, ProxyClass, identifierName, ngModuleJitUrl, sharedStylesheetJitUrl, templateJitUrl, templateSourceUrl} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeSummary, CompileProviderMetadata, CompileStylesheetMetadata, CompileTypeSummary, identifierName, ngModuleJitUrl, ProviderMeta, ProxyClass, sharedStylesheetJitUrl, templateJitUrl, templateSourceUrl} from '../compile_metadata'; import {CompileReflector} from '../compile_reflector'; import {CompilerConfig} from '../config'; import {ConstantPool} from '../constant_pool'; @@ -20,7 +20,7 @@ import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; import {SummaryResolver} from '../summary_resolver'; import {TemplateAst} from '../template_parser/template_ast'; import {TemplateParser} from '../template_parser/template_parser'; -import {Console, OutputContext, SyncAsync, stringify} from '../util'; +import {Console, OutputContext, stringify, SyncAsync} from '../util'; import {ViewCompiler} from '../view_compiler/view_compiler'; export interface ModuleWithComponentFactories { @@ -97,7 +97,9 @@ export class JitCompiler { } } - hasAotSummary(ref: Type) { return !!this._summaryResolver.resolveSummary(ref); } + hasAotSummary(ref: Type) { + return !!this._summaryResolver.resolveSummary(ref); + } private _filterJitIdentifiers(ids: CompileIdentifierMetadata[]): any[] { return ids.map(mod => mod.reference).filter((ref) => !this.hasAotSummary(ref)); @@ -124,12 +126,12 @@ export class JitCompiler { private _loadModules(mainModule: any, isSync: boolean): SyncAsync<any> { const loading: Promise<any>[] = []; - const mainNgModule = this._metadataResolver.getNgModuleMetadata(mainModule) !; + const mainNgModule = this._metadataResolver.getNgModuleMetadata(mainModule)!; // Note: for runtime compilation, we want to transitively compile all modules, // so we also need to load the declared directives / pipes for all nested modules. this._filterJitIdentifiers(mainNgModule.transitiveModule.modules).forEach((nestedNgModule) => { // getNgModuleMetadata only returns null if the value passed in is not an NgModule - const moduleMeta = this._metadataResolver.getNgModuleMetadata(nestedNgModule) !; + const moduleMeta = this._metadataResolver.getNgModuleMetadata(nestedNgModule)!; this._filterJitIdentifiers(moduleMeta.declaredDirectives).forEach((ref) => { const promise = this._metadataResolver.loadDirectiveMetadata(moduleMeta.type.reference, ref, isSync); @@ -144,9 +146,9 @@ export class JitCompiler { } private _compileModule(moduleType: Type): object { - let ngModuleFactory = this._compiledNgModuleCache.get(moduleType) !; + let ngModuleFactory = this._compiledNgModuleCache.get(moduleType)!; if (!ngModuleFactory) { - const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType) !; + const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType)!; // Always provide a bound Compiler const extraProviders = this.getExtraNgModuleProviders(moduleMeta.type.reference); const outputCtx = createOutputContext(); @@ -162,13 +164,13 @@ export class JitCompiler { * @internal */ _compileComponents(mainModule: Type, allComponentFactories: object[]|null) { - const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule) !; + const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule)!; const moduleByJitDirective = new Map<any, CompileNgModuleMetadata>(); const templates = new Set<CompiledTemplate>(); const transJitModules = this._filterJitIdentifiers(ngModule.transitiveModule.modules); transJitModules.forEach((localMod) => { - const localModuleMeta = this._metadataResolver.getNgModuleMetadata(localMod) !; + const localModuleMeta = this._metadataResolver.getNgModuleMetadata(localMod)!; this._filterJitIdentifiers(localModuleMeta.declaredDirectives).forEach((dirRef) => { moduleByJitDirective.set(dirRef, localModuleMeta); const dirMeta = this._metadataResolver.getDirectiveMetadata(dirRef); @@ -184,12 +186,12 @@ export class JitCompiler { }); }); transJitModules.forEach((localMod) => { - const localModuleMeta = this._metadataResolver.getNgModuleMetadata(localMod) !; + const localModuleMeta = this._metadataResolver.getNgModuleMetadata(localMod)!; this._filterJitIdentifiers(localModuleMeta.declaredDirectives).forEach((dirRef) => { const dirMeta = this._metadataResolver.getDirectiveMetadata(dirRef); if (dirMeta.isComponent) { dirMeta.entryComponents.forEach((entryComponentType) => { - const moduleMeta = moduleByJitDirective.get(entryComponentType.componentType) !; + const moduleMeta = moduleByJitDirective.get(entryComponentType.componentType)!; templates.add( this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta)); }); @@ -197,7 +199,7 @@ export class JitCompiler { }); localModuleMeta.entryComponents.forEach((entryComponentType) => { if (!this.hasAotSummary(entryComponentType.componentType)) { - const moduleMeta = moduleByJitDirective.get(entryComponentType.componentType) !; + const moduleMeta = moduleByJitDirective.get(entryComponentType.componentType)!; templates.add( this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta)); } @@ -227,8 +229,9 @@ export class JitCompiler { private _createCompiledHostTemplate(compType: Type, ngModule: CompileNgModuleMetadata): CompiledTemplate { if (!ngModule) { - throw new Error( - `Component ${stringify(compType)} is not part of any NgModule or the module has not been imported into your module.`); + throw new Error(`Component ${ + stringify( + compType)} is not part of any NgModule or the module has not been imported into your module.`); } let compiledTemplate = this._compiledHostTemplateCache.get(compType); if (!compiledTemplate) { @@ -267,7 +270,7 @@ export class JitCompiler { compMeta.template !.externalStylesheets.forEach((stylesheetMeta) => { const compiledStylesheet = this._styleCompiler.compileStyles(createOutputContext(), compMeta, stylesheetMeta); - externalStylesheetsByModuleUrl.set(stylesheetMeta.moduleUrl !, compiledStylesheet); + externalStylesheetsByModuleUrl.set(stylesheetMeta.moduleUrl!, compiledStylesheet); }); this._resolveStylesCompileResult(componentStylesheet, externalStylesheetsByModuleUrl); const pipes = template.ngModule.transitiveModule.pipes.map( @@ -295,14 +298,14 @@ export class JitCompiler { const pipes = ngModule.transitiveModule.pipes.map( pipe => this._metadataResolver.getPipeSummary(pipe.reference)); return this._templateParser.parse( - compMeta, compMeta.template !.htmlAst !, directives, pipes, ngModule.schemas, + compMeta, compMeta.template !.htmlAst!, directives, pipes, ngModule.schemas, templateSourceUrl(ngModule.type, compMeta, compMeta.template !), preserveWhitespaces); } private _resolveStylesCompileResult( result: CompiledStylesheet, externalStylesheetsByModuleUrl: Map<string, CompiledStylesheet>) { result.dependencies.forEach((dep, i) => { - const nestedCompileResult = externalStylesheetsByModuleUrl.get(dep.moduleUrl) !; + const nestedCompileResult = externalStylesheetsByModuleUrl.get(dep.moduleUrl)!; const nestedStylesArr = this._resolveAndEvalStylesCompileResult( nestedCompileResult, externalStylesheetsByModuleUrl); dep.setValue(nestedStylesArr); @@ -329,7 +332,7 @@ export class JitCompiler { } class CompiledTemplate { - private _viewClass: Function = null !; + private _viewClass: Function = null!; isCompiled = false; constructor( diff --git a/packages/compiler/src/jit_compiler_facade.ts b/packages/compiler/src/jit_compiler_facade.ts index b1095f801d183..f2fb2c85591a6 100644 --- a/packages/compiler/src/jit_compiler_facade.ts +++ b/packages/compiler/src/jit_compiler_facade.ts @@ -16,13 +16,13 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/int import {DeclareVarStmt, Expression, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast'; import {JitEvaluator} from './output/output_jit'; import {ParseError, ParseSourceSpan, r3JitTypeSourceSpan} from './parse_util'; -import {R3DependencyMetadata, R3FactoryTarget, R3ResolvedDependencyType, compileFactoryFunction} from './render3/r3_factory'; +import {compileFactoryFunction, R3DependencyMetadata, R3FactoryTarget, R3ResolvedDependencyType} from './render3/r3_factory'; import {R3JitReflector} from './render3/r3_jit'; -import {R3InjectorMetadata, R3NgModuleMetadata, compileInjector, compileNgModule} from './render3/r3_module_compiler'; -import {R3PipeMetadata, compilePipeFromMetadata} from './render3/r3_pipe_compiler'; +import {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler'; +import {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler'; import {R3Reference} from './render3/util'; import {R3DirectiveMetadata, R3QueryMetadata} from './render3/view/api'; -import {ParsedHostBindings, compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings, verifyHostBindings} from './render3/view/compiler'; +import {compileComponentFromMetadata, compileDirectiveFromMetadata, ParsedHostBindings, parseHostBindings, verifyHostBindings} from './render3/view/compiler'; import {makeBindingParser, parseTemplate} from './render3/view/template'; import {ResourceLoader} from './resource_loader'; import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry'; @@ -277,7 +277,7 @@ function wrapExpression(obj: any, property: string): WrappedNodeExpr<any>|undefi } } -function computeProvidedIn(providedIn: Type | string | null | undefined): Expression { +function computeProvidedIn(providedIn: Type|string|null|undefined): Expression { if (providedIn == null || typeof providedIn === 'string') { return new LiteralExpr(providedIn); } else { @@ -296,16 +296,17 @@ function convertR3DependencyMetadata(facade: R3DependencyMetadataFacade): R3Depe } return { token: tokenExpr, + attribute: null, resolved: facade.resolved, host: facade.host, optional: facade.optional, self: facade.self, - skipSelf: facade.skipSelf + skipSelf: facade.skipSelf, }; } -function convertR3DependencyMetadataArray(facades: R3DependencyMetadataFacade[] | null | undefined): - R3DependencyMetadata[]|null { +function convertR3DependencyMetadataArray(facades: R3DependencyMetadataFacade[]|null| + undefined): R3DependencyMetadata[]|null { return facades == null ? null : facades.map(convertR3DependencyMetadata); } @@ -355,13 +356,11 @@ function isOutput(value: any): value is Output { } function parseInputOutputs(values: string[]): StringMap { - return values.reduce( - (map, value) => { - const [field, property] = value.split(',').map(piece => piece.trim()); - map[field] = property || field; - return map; - }, - {} as StringMap); + return values.reduce((map, value) => { + const [field, property] = value.split(',').map(piece => piece.trim()); + map[field] = property || field; + return map; + }, {} as StringMap); } export function publishFacade(global: any) { diff --git a/packages/compiler/src/metadata_resolver.ts b/packages/compiler/src/metadata_resolver.ts index b5e28006a330c..ff85eb216c00d 100644 --- a/packages/compiler/src/metadata_resolver.ts +++ b/packages/compiler/src/metadata_resolver.ts @@ -12,7 +12,7 @@ import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import * as cpl from './compile_metadata'; import {CompileReflector} from './compile_reflector'; import {CompilerConfig} from './config'; -import {ChangeDetectionStrategy, Component, Directive, Injectable, ModuleWithProviders, Provider, Query, SchemaMetadata, Type, ViewEncapsulation, createAttribute, createComponent, createHost, createInject, createInjectable, createInjectionToken, createNgModule, createOptional, createSelf, createSkipSelf} from './core'; +import {ChangeDetectionStrategy, Component, createAttribute, createComponent, createHost, createInject, createInjectable, createInjectionToken, createNgModule, createOptional, createSelf, createSkipSelf, Directive, Injectable, ModuleWithProviders, Provider, Query, SchemaMetadata, Type, ViewEncapsulation} from './core'; import {DirectiveNormalizer} from './directive_normalizer'; import {DirectiveResolver, findLast} from './directive_resolver'; import {Identifiers} from './identifiers'; @@ -23,7 +23,7 @@ import {PipeResolver} from './pipe_resolver'; import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {CssSelector} from './selector'; import {SummaryResolver} from './summary_resolver'; -import {Console, SyncAsync, ValueTransformer, isPromise, noUndefined, resolveForwardRef, stringify, syntaxError, visitValue} from './util'; +import {Console, isPromise, noUndefined, resolveForwardRef, stringify, SyncAsync, syntaxError, ValueTransformer, visitValue} from './util'; export type ErrorCollector = (error: any, type?: any) => void; @@ -55,7 +55,9 @@ export class CompileMetadataResolver { private _staticSymbolCache: StaticSymbolCache, private _reflector: CompileReflector, private _errorCollector?: ErrorCollector) {} - getReflector(): CompileReflector { return this._reflector; } + getReflector(): CompileReflector { + return this._reflector; + } clearCacheFor(type: Type) { const dirMeta = this._directiveCache.get(type); @@ -176,7 +178,7 @@ export class CompileMetadataResolver { } // Note: ! is ok here as this method should only be called with normalized directive // metadata, which always fills in the selector. - const template = CssSelector.parse(compMeta.selector !)[0].getMatchingElementTemplate(); + const template = CssSelector.parse(compMeta.selector!)[0].getMatchingElementTemplate(); const templateUrl = ''; const htmlAst = this._htmlParser.parse(template, templateUrl); return cpl.CompileDirectiveMetadata.create({ @@ -209,8 +211,8 @@ export class CompileMetadataResolver { guards: {}, viewQueries: [], componentViewType: hostViewType, - rendererType: - {id: '__Host__', encapsulation: ViewEncapsulation.None, styles: [], data: {}} as object, + rendererType: {id: '__Host__', encapsulation: ViewEncapsulation.None, styles: [], data: {}} as + object, entryComponents: [], componentFactory: null }); @@ -221,9 +223,9 @@ export class CompileMetadataResolver { return null; } directiveType = resolveForwardRef(directiveType); - const {annotation, metadata} = this.getNonNormalizedDirectiveMetadata(directiveType) !; + const {annotation, metadata} = this.getNonNormalizedDirectiveMetadata(directiveType)!; - const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata | null) => { + const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata|null) => { const normalizedDirMeta = new cpl.CompileDirectiveMetadata({ isHost: false, type: metadata.type, @@ -248,7 +250,7 @@ export class CompileMetadataResolver { template: templateMetadata }); if (templateMetadata) { - this.initComponentFactory(metadata.componentFactory !, templateMetadata.ngContentSelectors); + this.initComponentFactory(metadata.componentFactory!, templateMetadata.ngContentSelectors); } this._directiveCache.set(directiveType, normalizedDirMeta); this._summaryCache.set(directiveType, normalizedDirMeta.toSummary()); @@ -296,7 +298,7 @@ export class CompileMetadataResolver { if (!dirMeta) { return null; } - let nonNormalizedTemplateMetadata: cpl.CompileTemplateMetadata = undefined !; + let nonNormalizedTemplateMetadata: cpl.CompileTemplateMetadata = undefined!; if (createComponent.isTypeOf(dirMeta)) { // component @@ -323,7 +325,7 @@ export class CompileMetadataResolver { }); } - let changeDetectionStrategy: ChangeDetectionStrategy = null !; + let changeDetectionStrategy: ChangeDetectionStrategy = null!; let viewProviders: cpl.CompileProviderMetadata[] = []; let entryComponentMetadata: cpl.CompileEntryComponentMetadata[] = []; let selector = dirMeta.selector; @@ -331,7 +333,7 @@ export class CompileMetadataResolver { if (createComponent.isTypeOf(dirMeta)) { // Component const compMeta = dirMeta as Component; - changeDetectionStrategy = compMeta.changeDetection !; + changeDetectionStrategy = compMeta.changeDetection!; if (compMeta.viewProviders) { viewProviders = this._getProvidersMetadata( compMeta.viewProviders, entryComponentMetadata, @@ -339,7 +341,7 @@ export class CompileMetadataResolver { } if (compMeta.entryComponents) { entryComponentMetadata = flattenAndDedupeArray(compMeta.entryComponents) - .map((type) => this._getEntryComponentMetadata(type) !) + .map((type) => this._getEntryComponentMetadata(type)!) .concat(entryComponentMetadata); } if (!selector) { @@ -348,7 +350,7 @@ export class CompileMetadataResolver { } else { // Directive if (!selector) { - selector = null !; + selector = null!; } } @@ -401,11 +403,12 @@ export class CompileMetadataResolver { * This assumes `loadNgModuleDirectiveAndPipeMetadata` has been called first. */ getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata { - const dirMeta = this._directiveCache.get(directiveType) !; + const dirMeta = this._directiveCache.get(directiveType)!; if (!dirMeta) { this._reportError( syntaxError( - `Illegal state: getDirectiveMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`), + `Illegal state: getDirectiveMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Directive ${ + stringifyType(directiveType)}.`), directiveType); } return dirMeta; @@ -530,7 +533,7 @@ export class CompileMetadataResolver { if (meta.imports) { flattenAndDedupeArray(meta.imports).forEach((importedType) => { - let importedModuleType: Type = undefined !; + let importedModuleType: Type = undefined!; if (isValidType(importedType)) { importedModuleType = importedType; } else if (importedType && importedType.ngModule) { @@ -549,8 +552,9 @@ export class CompileMetadataResolver { if (!alreadyCollecting) alreadyCollecting = new Set(); if (alreadyCollecting.has(importedModuleType)) { this._reportError( - syntaxError( - `${this._getTypeDescriptor(importedModuleType)} '${stringifyType(importedType)}' is imported recursively by the module '${stringifyType(moduleType)}'.`), + syntaxError(`${this._getTypeDescriptor(importedModuleType)} '${ + stringifyType(importedType)}' is imported recursively by the module '${ + stringifyType(moduleType)}'.`), moduleType); return; } @@ -560,8 +564,9 @@ export class CompileMetadataResolver { alreadyCollecting.delete(importedModuleType); if (!importedModuleSummary) { this._reportError( - syntaxError( - `Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'. Please add a @NgModule annotation.`), + syntaxError(`Unexpected ${this._getTypeDescriptor(importedType)} '${ + stringifyType(importedType)}' imported by the module '${ + stringifyType(moduleType)}'. Please add a @NgModule annotation.`), moduleType); return; } @@ -569,7 +574,8 @@ export class CompileMetadataResolver { } else { this._reportError( syntaxError( - `Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`), + `Unexpected value '${stringifyType(importedType)}' imported by the module '${ + stringifyType(moduleType)}'`), moduleType); return; } @@ -581,15 +587,17 @@ export class CompileMetadataResolver { if (!isValidType(exportedType)) { this._reportError( syntaxError( - `Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`), + `Unexpected value '${stringifyType(exportedType)}' exported by the module '${ + stringifyType(moduleType)}'`), moduleType); return; } if (!alreadyCollecting) alreadyCollecting = new Set(); if (alreadyCollecting.has(exportedType)) { this._reportError( - syntaxError( - `${this._getTypeDescriptor(exportedType)} '${stringify(exportedType)}' is exported recursively by the module '${stringifyType(moduleType)}'`), + syntaxError(`${this._getTypeDescriptor(exportedType)} '${ + stringify(exportedType)}' is exported recursively by the module '${ + stringifyType(moduleType)}'`), moduleType); return; } @@ -612,7 +620,8 @@ export class CompileMetadataResolver { if (!isValidType(declaredType)) { this._reportError( syntaxError( - `Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`), + `Unexpected value '${stringifyType(declaredType)}' declared by the module '${ + stringifyType(moduleType)}'`), moduleType); return; } @@ -634,8 +643,10 @@ export class CompileMetadataResolver { this._addTypeToModule(declaredType, moduleType); } else { this._reportError( - syntaxError( - `Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`), + syntaxError(`Unexpected ${this._getTypeDescriptor(declaredType)} '${ + stringifyType(declaredType)}' declared by the module '${ + stringifyType( + moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`), moduleType); return; } @@ -653,8 +664,9 @@ export class CompileMetadataResolver { transitiveModule.addExportedPipe(exportedId); } else { this._reportError( - syntaxError( - `Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`), + syntaxError(`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${ + stringifyType(exportedId.reference)} from ${ + stringifyType(moduleType)} as it was neither declared nor imported!`), moduleType); return; } @@ -670,15 +682,16 @@ export class CompileMetadataResolver { if (meta.entryComponents) { entryComponents.push(...flattenAndDedupeArray(meta.entryComponents) - .map(type => this._getEntryComponentMetadata(type) !)); + .map(type => this._getEntryComponentMetadata(type)!)); } if (meta.bootstrap) { flattenAndDedupeArray(meta.bootstrap).forEach(type => { if (!isValidType(type)) { this._reportError( - syntaxError( - `Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`), + syntaxError(`Unexpected value '${ + stringifyType(type)}' used in the bootstrap property of module '${ + stringifyType(moduleType)}'`), moduleType); return; } @@ -687,7 +700,7 @@ export class CompileMetadataResolver { } entryComponents.push( - ...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference) !)); + ...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference)!)); if (meta.schemas) { schemas.push(...flattenAndDedupeArray(meta.schemas)); @@ -710,7 +723,7 @@ export class CompileMetadataResolver { }); entryComponents.forEach((id) => transitiveModule.addEntryComponent(id)); - providers.forEach((provider) => transitiveModule.addProvider(provider, compileMeta !.type)); + providers.forEach((provider) => transitiveModule.addProvider(provider, compileMeta!.type)); transitiveModule.addModule(compileMeta.type); this._ngModuleCache.set(moduleType, compileMeta); return compileMeta; @@ -753,9 +766,13 @@ export class CompileMetadataResolver { if (oldModule && oldModule !== moduleType) { this._reportError( syntaxError( - `Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` + - `Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` + - `You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`), + `Type ${stringifyType(type)} is part of the declarations of 2 modules: ${ + stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` + + `Please consider moving ${stringifyType(type)} to a higher module that imports ${ + stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` + + `You can also create a new NgModule that exports and includes ${ + stringifyType(type)} then import that NgModule in ${ + stringifyType(oldModule)} and ${stringifyType(moduleType)}.`), moduleType); return; } @@ -870,7 +887,8 @@ export class CompileMetadataResolver { if (!pipeMeta) { this._reportError( syntaxError( - `Illegal state: getPipeMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`), + `Illegal state: getPipeMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Pipe ${ + stringifyType(pipeType)}.`), pipeType); } return pipeMeta || null; @@ -898,7 +916,7 @@ export class CompileMetadataResolver { private _loadPipeMetadata(pipeType: any): cpl.CompilePipeMetadata { pipeType = resolveForwardRef(pipeType); - const pipeAnnotation = this._pipeResolver.resolve(pipeType) !; + const pipeAnnotation = this._pipeResolver.resolve(pipeType)!; const pipeMeta = new cpl.CompilePipeMetadata({ type: this._getTypeMetadata(pipeType), @@ -962,7 +980,6 @@ export class CompileMetadataResolver { isOptional, token: this._getTokenMetadata(token) }; - }); if (hasUnknownDeps) { @@ -1000,7 +1017,7 @@ export class CompileMetadataResolver { this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders); } else { provider = resolveForwardRef(provider); - let providerMeta: cpl.ProviderMeta = undefined !; + let providerMeta: cpl.ProviderMeta = undefined!; if (provider && typeof provider === 'object' && provider.hasOwnProperty('provide')) { this._validateProvider(provider); providerMeta = new cpl.ProviderMeta(provider.provide, provider); @@ -1027,8 +1044,11 @@ export class CompileMetadataResolver { []) .join(', '); this._reportError( - syntaxError( - `Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`), + syntaxError(`Invalid ${ + debugInfo ? + debugInfo : + 'provider'} - only instances of Provider and Type are allowed, got: [${ + providersInfo}]`), type); return; } @@ -1045,8 +1065,8 @@ export class CompileMetadataResolver { private _validateProvider(provider: any): void { if (provider.hasOwnProperty('useClass') && provider.useClass == null) { - this._reportError(syntaxError( - `Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}. + this._reportError(syntaxError(`Invalid provider for ${ + stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}. Usually it happens when: 1. There's a circular dependency (might be caused by using index.ts (barrel) files). 2. Class was used before it was declared. Use forwardRef in this case.`)); @@ -1085,12 +1105,12 @@ export class CompileMetadataResolver { cpl.CompileEntryComponentMetadata|null { const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType); if (dirMeta && dirMeta.metadata.isComponent) { - return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory !}; + return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory!}; } const dirSummary = <cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive); if (dirSummary && dirSummary.isComponent) { - return {componentType: dirType, componentFactory: dirSummary.componentFactory !}; + return {componentType: dirType, componentFactory: dirSummary.componentFactory!}; } if (throwIfNotFound) { throw syntaxError(`${dirType.name} cannot be used as an entry component.`); @@ -1108,9 +1128,9 @@ export class CompileMetadataResolver { } getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata { - let compileDeps: cpl.CompileDiDependencyMetadata[] = undefined !; - let compileTypeMetadata: cpl.CompileTypeMetadata = null !; - let compileFactoryMetadata: cpl.CompileFactoryMetadata = null !; + let compileDeps: cpl.CompileDiDependencyMetadata[] = undefined!; + let compileTypeMetadata: cpl.CompileTypeMetadata = null!; + let compileFactoryMetadata: cpl.CompileFactoryMetadata = null!; let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token); if (provider.useClass) { @@ -1152,7 +1172,9 @@ export class CompileMetadataResolver { return res; } - private _queryVarBindings(selector: any): string[] { return selector.split(/\s*,\s*/); } + private _queryVarBindings(selector: any): string[] { + return selector.split(/\s*,\s*/); + } private _getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type|Function): cpl.CompileQueryMetadata { @@ -1163,8 +1185,8 @@ export class CompileMetadataResolver { } else { if (!q.selector) { this._reportError( - syntaxError( - `Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`), + syntaxError(`Can't construct a query for the property "${propertyName}" of "${ + stringifyType(typeOrFunc)}" since the query selector wasn't defined.`), typeOrFunc); selectors = []; } else { @@ -1175,8 +1197,9 @@ export class CompileMetadataResolver { return { selectors, first: q.first, - descendants: q.descendants, propertyName, - read: q.read ? this._getTokenMetadata(q.read) : null !, + descendants: q.descendants, + propertyName, + read: q.read ? this._getTokenMetadata(q.read) : null!, static: q.static }; } diff --git a/packages/compiler/src/ml_parser/ast.ts b/packages/compiler/src/ml_parser/ast.ts index 20e8343553f5a..8dc7761030a87 100644 --- a/packages/compiler/src/ml_parser/ast.ts +++ b/packages/compiler/src/ml_parser/ast.ts @@ -24,7 +24,9 @@ export class Text extends NodeWithI18n { constructor(public value: string, sourceSpan: ParseSourceSpan, i18n?: I18nMeta) { super(sourceSpan, i18n); } - visit(visitor: Visitor, context: any): any { return visitor.visitText(this, context); } + visit(visitor: Visitor, context: any): any { + return visitor.visitText(this, context); + } } export class Expansion extends NodeWithI18n { @@ -33,7 +35,9 @@ export class Expansion extends NodeWithI18n { sourceSpan: ParseSourceSpan, public switchValueSourceSpan: ParseSourceSpan, i18n?: I18nMeta) { super(sourceSpan, i18n); } - visit(visitor: Visitor, context: any): any { return visitor.visitExpansion(this, context); } + visit(visitor: Visitor, context: any): any { + return visitor.visitExpansion(this, context); + } } export class ExpansionCase implements Node { @@ -41,7 +45,9 @@ export class ExpansionCase implements Node { public value: string, public expression: Node[], public sourceSpan: ParseSourceSpan, public valueSourceSpan: ParseSourceSpan, public expSourceSpan: ParseSourceSpan) {} - visit(visitor: Visitor, context: any): any { return visitor.visitExpansionCase(this, context); } + visit(visitor: Visitor, context: any): any { + return visitor.visitExpansionCase(this, context); + } } export class Attribute extends NodeWithI18n { @@ -50,7 +56,9 @@ export class Attribute extends NodeWithI18n { public valueSpan?: ParseSourceSpan, i18n?: I18nMeta) { super(sourceSpan, i18n); } - visit(visitor: Visitor, context: any): any { return visitor.visitAttribute(this, context); } + visit(visitor: Visitor, context: any): any { + return visitor.visitAttribute(this, context); + } } export class Element extends NodeWithI18n { @@ -60,12 +68,16 @@ export class Element extends NodeWithI18n { public endSourceSpan: ParseSourceSpan|null = null, i18n?: I18nMeta) { super(sourceSpan, i18n); } - visit(visitor: Visitor, context: any): any { return visitor.visitElement(this, context); } + visit(visitor: Visitor, context: any): any { + return visitor.visitElement(this, context); + } } export class Comment implements Node { constructor(public value: string|null, public sourceSpan: ParseSourceSpan) {} - visit(visitor: Visitor, context: any): any { return visitor.visitComment(this, context); } + visit(visitor: Visitor, context: any): any { + return visitor.visitComment(this, context); + } } export interface Visitor { @@ -85,7 +97,7 @@ export function visitAll(visitor: Visitor, nodes: Node[], context: any = null): const result: any[] = []; const visit = visitor.visit ? - (ast: Node) => visitor.visit !(ast, context) || ast.visit(visitor, context) : + (ast: Node) => visitor.visit!(ast, context) || ast.visit(visitor, context) : (ast: Node) => ast.visit(visitor, context); nodes.forEach(ast => { const astResult = visit(ast); @@ -111,7 +123,9 @@ export class RecursiveVisitor implements Visitor { visitComment(ast: Comment, context: any): any {} visitExpansion(ast: Expansion, context: any): any { - return this.visitChildren(context, visit => { visit(ast.cases); }); + return this.visitChildren(context, visit => { + visit(ast.cases); + }); } visitExpansionCase(ast: ExpansionCase, context: any): any {} @@ -120,7 +134,7 @@ export class RecursiveVisitor implements Visitor { context: any, cb: (visit: (<V extends Node>(children: V[]|undefined) => void)) => void) { let results: any[][] = []; let t = this; - function visit<T extends Node>(children: T[] | undefined) { + function visit<T extends Node>(children: T[]|undefined) { if (children) results.push(visitAll(t, children, context)); } cb(visit); diff --git a/packages/compiler/src/ml_parser/html_parser.ts b/packages/compiler/src/ml_parser/html_parser.ts index 5e788523dbed9..bc1d33282053d 100644 --- a/packages/compiler/src/ml_parser/html_parser.ts +++ b/packages/compiler/src/ml_parser/html_parser.ts @@ -8,12 +8,14 @@ import {getHtmlTagDefinition} from './html_tags'; import {TokenizeOptions} from './lexer'; -import {ParseTreeResult, Parser} from './parser'; +import {Parser, ParseTreeResult} from './parser'; export {ParseTreeResult, TreeError} from './parser'; export class HtmlParser extends Parser { - constructor() { super(getHtmlTagDefinition); } + constructor() { + super(getHtmlTagDefinition); + } parse(source: string, url: string, options?: TokenizeOptions): ParseTreeResult { return super.parse(source, url, options); diff --git a/packages/compiler/src/ml_parser/html_tags.ts b/packages/compiler/src/ml_parser/html_tags.ts index f2dc36a415e97..5e978fdb6368a 100644 --- a/packages/compiler/src/ml_parser/html_tags.ts +++ b/packages/compiler/src/ml_parser/html_tags.ts @@ -18,16 +18,21 @@ export class HtmlTagDefinition implements TagDefinition { ignoreFirstLf: boolean; canSelfClose: boolean = false; - constructor( - {closedByChildren, implicitNamespacePrefix, contentType = TagContentType.PARSABLE_DATA, - closedByParent = false, isVoid = false, ignoreFirstLf = false}: { - closedByChildren?: string[], - closedByParent?: boolean, - implicitNamespacePrefix?: string, - contentType?: TagContentType, - isVoid?: boolean, - ignoreFirstLf?: boolean - } = {}) { + constructor({ + closedByChildren, + implicitNamespacePrefix, + contentType = TagContentType.PARSABLE_DATA, + closedByParent = false, + isVoid = false, + ignoreFirstLf = false + }: { + closedByChildren?: string[], + closedByParent?: boolean, + implicitNamespacePrefix?: string, + contentType?: TagContentType, + isVoid?: boolean, + ignoreFirstLf?: boolean + } = {}) { if (closedByChildren && closedByChildren.length > 0) { closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true); } @@ -43,11 +48,11 @@ export class HtmlTagDefinition implements TagDefinition { } } -let _DEFAULT_TAG_DEFINITION !: HtmlTagDefinition; +let _DEFAULT_TAG_DEFINITION!: HtmlTagDefinition; // see http://www.w3.org/TR/html51/syntax.html#optional-tags // This implementation does not fully conform to the HTML5 spec. -let TAG_DEFINITIONS !: {[key: string]: HtmlTagDefinition}; +let TAG_DEFINITIONS!: {[key: string]: HtmlTagDefinition}; export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition { if (!TAG_DEFINITIONS) { diff --git a/packages/compiler/src/ml_parser/html_whitespaces.ts b/packages/compiler/src/ml_parser/html_whitespaces.ts index b2431c15c2186..9dfb1721072c4 100644 --- a/packages/compiler/src/ml_parser/html_whitespaces.ts +++ b/packages/compiler/src/ml_parser/html_whitespaces.ts @@ -81,11 +81,17 @@ export class WhitespaceVisitor implements html.Visitor { return null; } - visitComment(comment: html.Comment, context: any): any { return comment; } + visitComment(comment: html.Comment, context: any): any { + return comment; + } - visitExpansion(expansion: html.Expansion, context: any): any { return expansion; } + visitExpansion(expansion: html.Expansion, context: any): any { + return expansion; + } - visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return expansionCase; } + visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { + return expansionCase; + } } export function removeWhitespaces(htmlAstWithErrors: ParseTreeResult): ParseTreeResult { diff --git a/packages/compiler/src/ml_parser/icu_ast_expander.ts b/packages/compiler/src/ml_parser/icu_ast_expander.ts index 3e7d5d15ac84f..1285aabb9aa58 100644 --- a/packages/compiler/src/ml_parser/icu_ast_expander.ts +++ b/packages/compiler/src/ml_parser/icu_ast_expander.ts @@ -46,7 +46,9 @@ export class ExpansionResult { } export class ExpansionError extends ParseError { - constructor(span: ParseSourceSpan, errorMsg: string) { super(span, errorMsg); } + constructor(span: ParseSourceSpan, errorMsg: string) { + super(span, errorMsg); + } } /** @@ -64,11 +66,17 @@ class _Expander implements html.Visitor { element.startSourceSpan, element.endSourceSpan); } - visitAttribute(attribute: html.Attribute, context: any): any { return attribute; } + visitAttribute(attribute: html.Attribute, context: any): any { + return attribute; + } - visitText(text: html.Text, context: any): any { return text; } + visitText(text: html.Text, context: any): any { + return text; + } - visitComment(comment: html.Comment, context: any): any { return comment; } + visitComment(comment: html.Comment, context: any): any { + return comment; + } visitExpansion(icu: html.Expansion, context: any): any { this.isExpanded = true; @@ -87,7 +95,7 @@ function _expandPluralForm(ast: html.Expansion, errors: ParseError[]): html.Elem if (PLURAL_CASES.indexOf(c.value) == -1 && !c.value.match(/^=\d+$/)) { errors.push(new ExpansionError( c.valueSourceSpan, - `Plural cases should be "=<number>" or one of ${PLURAL_CASES.join(", ")}`)); + `Plural cases should be "=<number>" or one of ${PLURAL_CASES.join(', ')}`)); } const expansionResult = expandNodes(c.expression); diff --git a/packages/compiler/src/ml_parser/lexer.ts b/packages/compiler/src/ml_parser/lexer.ts index af02426e53116..e499f5994c873 100644 --- a/packages/compiler/src/ml_parser/lexer.ts +++ b/packages/compiler/src/ml_parser/lexer.ts @@ -747,7 +747,7 @@ function isNamedEntityEnd(code: number): boolean { } function isExpansionCaseStart(peek: number): boolean { - return peek === chars.$EQ || chars.isAsciiLetter(peek) || chars.isDigit(peek); + return peek !== chars.$RBRACE; } function compareCharCodeCaseInsensitive(code1: number, code2: number): boolean { @@ -764,7 +764,7 @@ function mergeTextTokens(srcTokens: Token[]): Token[] { for (let i = 0; i < srcTokens.length; i++) { const token = srcTokens[i]; if (lastDstToken && lastDstToken.type == TokenType.TEXT && token.type == TokenType.TEXT) { - lastDstToken.parts[0] ! += token.parts[0]; + lastDstToken.parts[0]! += token.parts[0]; lastDstToken.sourceSpan.end = token.sourceSpan.end; } else { lastDstToken = token; @@ -849,15 +849,27 @@ class PlainCharacterCursor implements CharacterCursor { } } - clone(): PlainCharacterCursor { return new PlainCharacterCursor(this); } + clone(): PlainCharacterCursor { + return new PlainCharacterCursor(this); + } - peek() { return this.state.peek; } - charsLeft() { return this.end - this.state.offset; } - diff(other: this) { return this.state.offset - other.state.offset; } + peek() { + return this.state.peek; + } + charsLeft() { + return this.end - this.state.offset; + } + diff(other: this) { + return this.state.offset - other.state.offset; + } - advance(): void { this.advanceState(this.state); } + advance(): void { + this.advanceState(this.state); + } - init(): void { this.updatePeek(this.state); } + init(): void { + this.updatePeek(this.state); + } getSpan(start?: this, leadingTriviaCodePoints?: number[]): ParseSourceSpan { start = start || this; @@ -880,7 +892,9 @@ class PlainCharacterCursor implements CharacterCursor { return this.input.substring(start.state.offset, this.state.offset); } - charAt(pos: number): number { return this.input.charCodeAt(pos); } + charAt(pos: number): number { + return this.input.charCodeAt(pos); + } protected advanceState(state: CursorState) { if (state.offset >= this.end) { @@ -913,7 +927,7 @@ class EscapedCharacterCursor extends PlainCharacterCursor { super(fileOrCursor); this.internalState = {...fileOrCursor.internalState}; } else { - super(fileOrCursor, range !); + super(fileOrCursor, range!); this.internalState = this.state; } } @@ -929,7 +943,9 @@ class EscapedCharacterCursor extends PlainCharacterCursor { this.processEscapeSequence(); } - clone(): EscapedCharacterCursor { return new EscapedCharacterCursor(this); } + clone(): EscapedCharacterCursor { + return new EscapedCharacterCursor(this); + } getChars(start: this): string { const cursor = start.clone(); diff --git a/packages/compiler/src/ml_parser/parser.ts b/packages/compiler/src/ml_parser/parser.ts index 88aca6d6f3cbd..cbb46723eda8b 100644 --- a/packages/compiler/src/ml_parser/parser.ts +++ b/packages/compiler/src/ml_parser/parser.ts @@ -10,7 +10,7 @@ import {ParseError, ParseSourceSpan} from '../parse_util'; import * as html from './ast'; import * as lex from './lexer'; -import {TagDefinition, getNsPrefix, isNgContainer, mergeNsAndName} from './tags'; +import {getNsPrefix, isNgContainer, mergeNsAndName, TagDefinition} from './tags'; export class TreeError extends ParseError { static create(elementName: string|null, span: ParseSourceSpan, msg: string): TreeError { @@ -43,7 +43,7 @@ export class Parser { class _TreeBuilder { private _index: number = -1; // TODO(issue/24571): remove '!'. - private _peek !: lex.Token; + private _peek!: lex.Token; private _rootNodes: html.Node[] = []; private _errors: TreeError[] = []; @@ -283,7 +283,7 @@ class _TreeBuilder { endTagToken.parts[0], endTagToken.parts[1], this._getParentElement()); if (this._getParentElement()) { - this._getParentElement() !.endSourceSpan = endTagToken.sourceSpan; + this._getParentElement()!.endSourceSpan = endTagToken.sourceSpan; } if (this.getTagDefinition(fullName).isVoid) { @@ -291,8 +291,8 @@ class _TreeBuilder { fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`)); } else if (!this._popElement(fullName)) { - const errMsg = - `Unexpected closing tag "${fullName}". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags`; + const errMsg = `Unexpected closing tag "${ + fullName}". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags`; this._errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg)); } } @@ -316,7 +316,7 @@ class _TreeBuilder { const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]); let end = attrName.sourceSpan.end; let value = ''; - let valueSpan: ParseSourceSpan = undefined !; + let valueSpan: ParseSourceSpan = undefined!; if (this._peek.type === lex.TokenType.ATTR_QUOTE) { this._advance(); } @@ -344,7 +344,7 @@ class _TreeBuilder { * `<ng-container>` elements are skipped as they are not rendered as DOM element. */ private _getParentElementSkippingContainers(): - {parent: html.Element | null, container: html.Element|null} { + {parent: html.Element|null, container: html.Element|null} { let container: html.Element|null = null; for (let i = this._elementStack.length - 1; i >= 0; i--) { diff --git a/packages/compiler/src/ml_parser/tags.ts b/packages/compiler/src/ml_parser/tags.ts index 038f4fe053754..386db35d1ed46 100644 --- a/packages/compiler/src/ml_parser/tags.ts +++ b/packages/compiler/src/ml_parser/tags.ts @@ -23,7 +23,7 @@ export interface TagDefinition { isClosedByChild(name: string): boolean; } -export function splitNsName(elementName: string): [string | null, string] { +export function splitNsName(elementName: string): [string|null, string] { if (elementName[0] != ':') { return [null, elementName]; } @@ -54,7 +54,7 @@ export function isNgTemplate(tagName: string): boolean { export function getNsPrefix(fullName: string): string; export function getNsPrefix(fullName: null): null; -export function getNsPrefix(fullName: string | null): string|null { +export function getNsPrefix(fullName: string|null): string|null { return fullName === null ? null : splitNsName(fullName)[0]; } diff --git a/packages/compiler/src/ml_parser/xml_parser.ts b/packages/compiler/src/ml_parser/xml_parser.ts index dc6dffb0420d0..2f7933f356393 100644 --- a/packages/compiler/src/ml_parser/xml_parser.ts +++ b/packages/compiler/src/ml_parser/xml_parser.ts @@ -7,13 +7,15 @@ */ import {TokenizeOptions} from './lexer'; -import {ParseTreeResult, Parser} from './parser'; +import {Parser, ParseTreeResult} from './parser'; import {getXmlTagDefinition} from './xml_tags'; export {ParseTreeResult, TreeError} from './parser'; export class XmlParser extends Parser { - constructor() { super(getXmlTagDefinition); } + constructor() { + super(getXmlTagDefinition); + } parse(source: string, url: string, options?: TokenizeOptions): ParseTreeResult { return super.parse(source, url, options); diff --git a/packages/compiler/src/ml_parser/xml_tags.ts b/packages/compiler/src/ml_parser/xml_tags.ts index ab52c15dd18bf..75d14a3d8e428 100644 --- a/packages/compiler/src/ml_parser/xml_tags.ts +++ b/packages/compiler/src/ml_parser/xml_tags.ts @@ -11,19 +11,23 @@ import {TagContentType, TagDefinition} from './tags'; export class XmlTagDefinition implements TagDefinition { closedByParent: boolean = false; // TODO(issue/24571): remove '!'. - requiredParents !: {[key: string]: boolean}; + requiredParents!: {[key: string]: boolean}; // TODO(issue/24571): remove '!'. - parentToAdd !: string; + parentToAdd!: string; // TODO(issue/24571): remove '!'. - implicitNamespacePrefix !: string; + implicitNamespacePrefix!: string; contentType: TagContentType = TagContentType.PARSABLE_DATA; isVoid: boolean = false; ignoreFirstLf: boolean = false; canSelfClose: boolean = true; - requireExtraParent(currentParent: string): boolean { return false; } + requireExtraParent(currentParent: string): boolean { + return false; + } - isClosedByChild(name: string): boolean { return false; } + isClosedByChild(name: string): boolean { + return false; + } } const _TAG_DEFINITION = new XmlTagDefinition(); diff --git a/packages/compiler/src/ng_module_compiler.ts b/packages/compiler/src/ng_module_compiler.ts index 6ff46bcb765b7..f26dfc7092a85 100644 --- a/packages/compiler/src/ng_module_compiler.ts +++ b/packages/compiler/src/ng_module_compiler.ts @@ -43,8 +43,8 @@ export class NgModuleCompiler { }); const ngModuleDef = o.importExpr(Identifiers.moduleDef).callFn([o.literalArr(providerDefs)]); - const ngModuleDefFactory = o.fn( - [new o.FnParam(LOG_VAR.name !)], [new o.ReturnStatement(ngModuleDef)], o.INFERRED_TYPE); + const ngModuleDefFactory = + o.fn([new o.FnParam(LOG_VAR.name!)], [new o.ReturnStatement(ngModuleDef)], o.INFERRED_TYPE); const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`; this._createNgModuleFactory( @@ -77,7 +77,7 @@ export class NgModuleCompiler { .set(value) .toDeclStmt( o.importType( - Identifiers.NgModuleFactory, [o.expressionType(ctx.importExpr(reference)) !], + Identifiers.NgModuleFactory, [o.expressionType(ctx.importExpr(reference))!], [o.TypeModifier.Const]), [o.StmtModifier.Final, o.StmtModifier.Exported]); diff --git a/packages/compiler/src/ng_module_resolver.ts b/packages/compiler/src/ng_module_resolver.ts index 6996f376a145e..fa1ca5010b70b 100644 --- a/packages/compiler/src/ng_module_resolver.ts +++ b/packages/compiler/src/ng_module_resolver.ts @@ -7,7 +7,7 @@ */ import {CompileReflector} from './compile_reflector'; -import {NgModule, Type, createNgModule} from './core'; +import {createNgModule, NgModule, Type} from './core'; import {findLast} from './directive_resolver'; import {stringify} from './util'; @@ -18,7 +18,9 @@ import {stringify} from './util'; export class NgModuleResolver { constructor(private _reflector: CompileReflector) {} - isNgModule(type: any) { return this._reflector.annotations(type).some(createNgModule.isTypeOf); } + isNgModule(type: any) { + return this._reflector.annotations(type).some(createNgModule.isTypeOf); + } resolve(type: Type, throwIfNotFound = true): NgModule|null { const ngModuleMeta: NgModule = diff --git a/packages/compiler/src/output/abstract_emitter.ts b/packages/compiler/src/output/abstract_emitter.ts index 3c809fa59016f..0e1463890db71 100644 --- a/packages/compiler/src/output/abstract_emitter.ts +++ b/packages/compiler/src/output/abstract_emitter.ts @@ -28,31 +28,39 @@ class _EmittedLine { } export class EmitterVisitorContext { - static createRoot(): EmitterVisitorContext { return new EmitterVisitorContext(0); } + static createRoot(): EmitterVisitorContext { + return new EmitterVisitorContext(0); + } private _lines: _EmittedLine[]; private _classes: o.ClassStmt[] = []; private _preambleLineCount = 0; - constructor(private _indent: number) { this._lines = [new _EmittedLine(_indent)]; } + constructor(private _indent: number) { + this._lines = [new _EmittedLine(_indent)]; + } /** * @internal strip this from published d.ts files due to * https://github.com/microsoft/TypeScript/issues/36216 */ - private get _currentLine(): _EmittedLine { return this._lines[this._lines.length - 1]; } + private get _currentLine(): _EmittedLine { + return this._lines[this._lines.length - 1]; + } - println(from?: {sourceSpan: ParseSourceSpan | null}|null, lastPart: string = ''): void { + println(from?: {sourceSpan: ParseSourceSpan|null}|null, lastPart: string = ''): void { this.print(from || null, lastPart, true); } - lineIsEmpty(): boolean { return this._currentLine.parts.length === 0; } + lineIsEmpty(): boolean { + return this._currentLine.parts.length === 0; + } lineLength(): number { return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength; } - print(from: {sourceSpan: ParseSourceSpan | null}|null, part: string, newLine: boolean = false) { + print(from: {sourceSpan: ParseSourceSpan|null}|null, part: string, newLine: boolean = false) { if (part.length > 0) { this._currentLine.parts.push(part); this._currentLine.partsLength += part.length; @@ -83,9 +91,13 @@ export class EmitterVisitorContext { } } - pushClass(clazz: o.ClassStmt) { this._classes.push(clazz); } + pushClass(clazz: o.ClassStmt) { + this._classes.push(clazz); + } - popClass(): o.ClassStmt { return this._classes.pop() !; } + popClass(): o.ClassStmt { + return this._classes.pop()!; + } get currentClass(): o.ClassStmt|null { return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null; @@ -135,7 +147,7 @@ export class EmitterVisitorContext { } while (spanIdx < spans.length) { - const span = spans[spanIdx] !; + const span = spans[spanIdx]!; const source = span.start.file; const sourceLine = span.start.line; const sourceCol = span.start.col; @@ -156,7 +168,9 @@ export class EmitterVisitorContext { return map; } - setPreambleLineCount(count: number) { return this._preambleLineCount = count; } + setPreambleLineCount(count: number) { + return this._preambleLineCount = count; + } spanOf(line: number, column: number): ParseSourceSpan|null { const emittedLine = this._lines[line - this._preambleLineCount]; @@ -243,7 +257,9 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex if (stmt.multiline) { ctx.println(stmt, `/* ${stmt.comment} */`); } else { - stmt.comment.split('\n').forEach((line) => { ctx.println(stmt, `// ${line}`); }); + stmt.comment.split('\n').forEach((line) => { + ctx.println(stmt, `// ${line}`); + }); } return null; } @@ -327,7 +343,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex expr.expr.visitExpression(this, ctx); } visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): any { - let varName = ast.name !; + let varName = ast.name!; if (ast.builtin != null) { switch (ast.builtin) { case o.BuiltinVar.Super: @@ -337,10 +353,10 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex varName = 'this'; break; case o.BuiltinVar.CatchError: - varName = CATCH_ERROR_VAR.name !; + varName = CATCH_ERROR_VAR.name!; break; case o.BuiltinVar.CatchStack: - varName = CATCH_STACK_VAR.name !; + varName = CATCH_STACK_VAR.name!; break; default: throw new Error(`Unknown builtin variable ${ast.builtin}`); @@ -388,7 +404,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex ctx.print(ast, '? '); ast.trueCase.visitExpression(this, ctx); ctx.print(ast, ': '); - ast.falseCase !.visitExpression(this, ctx); + ast.falseCase!.visitExpression(this, ctx); ctx.print(ast, `)`); return null; } diff --git a/packages/compiler/src/output/abstract_js_emitter.ts b/packages/compiler/src/output/abstract_js_emitter.ts index fde1fbe80b091..3802f1a98d865 100644 --- a/packages/compiler/src/output/abstract_js_emitter.ts +++ b/packages/compiler/src/output/abstract_js_emitter.ts @@ -11,7 +11,9 @@ import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitor import * as o from './output_ast'; export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor { - constructor() { super(false); } + constructor() { + super(false); + } visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any { ctx.pushClass(stmt); this._visitClassConstructor(stmt, ctx); @@ -101,7 +103,7 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor { visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): string|null { const fnExpr = expr.fn; if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) { - ctx.currentClass !.parent !.visitExpression(this, ctx); + ctx.currentClass!.parent!.visitExpression(this, ctx); ctx.print(expr, `.call(this`); if (expr.args.length > 0) { ctx.print(expr, `, `); diff --git a/packages/compiler/src/output/js_emitter.ts b/packages/compiler/src/output/js_emitter.ts index 16ae3138e74a0..4589676db62b6 100644 --- a/packages/compiler/src/output/js_emitter.ts +++ b/packages/compiler/src/output/js_emitter.ts @@ -51,7 +51,7 @@ class JsEmitterVisitor extends AbstractJsEmitterVisitor { } ctx.print(ast, `${prefix}.`); } - ctx.print(ast, name !); + ctx.print(ast, name!); return null; } visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any { diff --git a/packages/compiler/src/output/output_ast.ts b/packages/compiler/src/output/output_ast.ts index 5e6660af2f73d..d8d313f561ddd 100644 --- a/packages/compiler/src/output/output_ast.ts +++ b/packages/compiler/src/output/output_ast.ts @@ -24,7 +24,9 @@ export abstract class Type { } abstract visitType(visitor: TypeVisitor, context: any): any; - hasModifier(modifier: TypeModifier): boolean { return this.modifiers !.indexOf(modifier) !== -1; } + hasModifier(modifier: TypeModifier): boolean { + return this.modifiers!.indexOf(modifier) !== -1; + } } export enum BuiltinTypeName { @@ -60,7 +62,9 @@ export class ExpressionType extends Type { export class ArrayType extends Type { - constructor(public of : Type, modifiers: TypeModifier[]|null = null) { super(modifiers); } + constructor(public of: Type, modifiers: TypeModifier[]|null = null) { + super(modifiers); + } visitType(visitor: TypeVisitor, context: any): any { return visitor.visitArrayType(this, context); } @@ -73,7 +77,9 @@ export class MapType extends Type { super(modifiers); this.valueType = valueType || null; } - visitType(visitor: TypeVisitor, context: any): any { return visitor.visitMapType(this, context); } + visitType(visitor: TypeVisitor, context: any): any { + return visitor.visitMapType(this, context); + } } export const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic); @@ -113,15 +119,15 @@ export enum BinaryOperator { BiggerEquals } -export function nullSafeIsEquivalent<T extends{isEquivalent(other: T): boolean}>( - base: T | null, other: T | null) { +export function nullSafeIsEquivalent<T extends {isEquivalent(other: T): boolean}>( + base: T|null, other: T|null) { if (base == null || other == null) { return base == other; } return base.isEquivalent(other); } -export function areAllEquivalent<T extends{isEquivalent(other: T): boolean}>( +export function areAllEquivalent<T extends {isEquivalent(other: T): boolean}>( base: T[], other: T[]) { const len = base.length; if (len !== other.length) { @@ -243,7 +249,9 @@ export abstract class Expression { return new CastExpr(this, type, sourceSpan); } - toStmt(): Statement { return new ExpressionStatement(this, null); } + toStmt(): Statement { + return new ExpressionStatement(this, null); + } } export enum BuiltinVar { @@ -272,7 +280,9 @@ export class ReadVarExpr extends Expression { return e instanceof ReadVarExpr && this.name === e.name && this.builtin === e.builtin; } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitReadVarExpr(this, context); @@ -299,7 +309,9 @@ export class TypeofExpr extends Expression { return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr); } - isConstant(): boolean { return this.expr.isConstant(); } + isConstant(): boolean { + return this.expr.isConstant(); + } } export class WrappedNodeExpr<T> extends Expression { @@ -311,7 +323,9 @@ export class WrappedNodeExpr<T> extends Expression { return e instanceof WrappedNodeExpr && this.node === e.node; } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitWrappedNodeExpr(this, context); @@ -330,7 +344,9 @@ export class WriteVarExpr extends Expression { return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitWriteVarExpr(this, context); @@ -340,7 +356,9 @@ export class WriteVarExpr extends Expression { return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan); } - toConstDecl(): DeclareVarStmt { return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]); } + toConstDecl(): DeclareVarStmt { + return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]); + } } @@ -358,7 +376,9 @@ export class WriteKeyExpr extends Expression { this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitWriteKeyExpr(this, context); @@ -380,7 +400,9 @@ export class WritePropExpr extends Expression { this.name === e.name && this.value.isEquivalent(e.value); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitWritePropExpr(this, context); @@ -414,7 +436,9 @@ export class InvokeMethodExpr extends Expression { this.name === e.name && this.builtin === e.builtin && areAllEquivalent(this.args, e.args); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitInvokeMethodExpr(this, context); @@ -434,7 +458,9 @@ export class InvokeFunctionExpr extends Expression { areAllEquivalent(this.args, e.args) && this.pure === e.pure; } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitInvokeFunctionExpr(this, context); @@ -454,7 +480,9 @@ export class InstantiateExpr extends Expression { areAllEquivalent(this.args, e.args); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitInstantiateExpr(this, context); @@ -473,7 +501,9 @@ export class LiteralExpr extends Expression { return e instanceof LiteralExpr && this.value === e.value; } - isConstant() { return true; } + isConstant() { + return true; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitLiteralExpr(this, context); @@ -494,7 +524,9 @@ export class LocalizedString extends Expression { return false; } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitLocalizedString(this, context); @@ -521,8 +553,9 @@ export class LocalizedString extends Expression { metaBlock = `${metaBlock}${ID_SEPARATOR}${this.metaBlock.customId}`; } if (this.metaBlock.legacyIds) { - this.metaBlock.legacyIds.forEach( - legacyId => { metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`; }); + this.metaBlock.legacyIds.forEach(legacyId => { + metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`; + }); } return createCookedRawString(metaBlock, this.messageParts[0]); } @@ -588,7 +621,9 @@ export class ExternalExpr extends Expression { this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime; } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitExternalExpr(this, context); @@ -616,7 +651,9 @@ export class ConditionalExpr extends Expression { this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitConditionalExpr(this, context); @@ -633,7 +670,9 @@ export class NotExpr extends Expression { return e instanceof NotExpr && this.condition.isEquivalent(e.condition); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitNotExpr(this, context); @@ -649,7 +688,9 @@ export class AssertNotNull extends Expression { return e instanceof AssertNotNull && this.condition.isEquivalent(e.condition); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitAssertNotNullExpr(this, context); @@ -665,7 +706,9 @@ export class CastExpr extends Expression { return e instanceof CastExpr && this.value.isEquivalent(e.value); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitCastExpr(this, context); @@ -676,7 +719,9 @@ export class CastExpr extends Expression { export class FnParam { constructor(public name: string, public type: Type|null = null) {} - isEquivalent(param: FnParam): boolean { return this.name === param.name; } + isEquivalent(param: FnParam): boolean { + return this.name === param.name; + } } @@ -692,7 +737,9 @@ export class FunctionExpr extends Expression { areAllEquivalent(this.statements, e.statements); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitFunctionExpr(this, context); @@ -719,7 +766,9 @@ export class BinaryOperatorExpr extends Expression { this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitBinaryOperatorExpr(this, context); @@ -739,7 +788,9 @@ export class ReadPropExpr extends Expression { this.name === e.name; } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitReadPropExpr(this, context); @@ -763,7 +814,9 @@ export class ReadKeyExpr extends Expression { this.index.isEquivalent(e.index); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitReadKeyExpr(this, context); @@ -782,7 +835,9 @@ export class LiteralArrayExpr extends Expression { this.entries = entries; } - isConstant() { return this.entries.every(e => e.isConstant()); } + isConstant() { + return this.entries.every(e => e.isConstant()); + } isEquivalent(e: Expression): boolean { return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries); @@ -813,7 +868,9 @@ export class LiteralMapExpr extends Expression { return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries); } - isConstant() { return this.entries.every(e => e.value.isConstant()); } + isConstant() { + return this.entries.every(e => e.value.isConstant()); + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitLiteralMapExpr(this, context); @@ -829,7 +886,9 @@ export class CommaExpr extends Expression { return e instanceof CommaExpr && areAllEquivalent(this.parts, e.parts); } - isConstant() { return false; } + isConstant() { + return false; + } visitExpression(visitor: ExpressionVisitor, context: any): any { return visitor.visitCommaExpr(this, context); @@ -892,7 +951,9 @@ export abstract class Statement { abstract visitStatement(visitor: StatementVisitor, context: any): any; - hasModifier(modifier: StmtModifier): boolean { return this.modifiers !.indexOf(modifier) !== -1; } + hasModifier(modifier: StmtModifier): boolean { + return this.modifiers!.indexOf(modifier) !== -1; + } } @@ -965,7 +1026,9 @@ export class AbstractClassPart { } this.type = type || null; } - hasModifier(modifier: StmtModifier): boolean { return this.modifiers !.indexOf(modifier) !== -1; } + hasModifier(modifier: StmtModifier): boolean { + return this.modifiers!.indexOf(modifier) !== -1; + } } export class ClassField extends AbstractClassPart { @@ -974,7 +1037,9 @@ export class ClassField extends AbstractClassPart { public initializer?: Expression) { super(type, modifiers); } - isEquivalent(f: ClassField) { return this.name === f.name; } + isEquivalent(f: ClassField) { + return this.name === f.name; + } } @@ -1044,7 +1109,9 @@ export class CommentStmt extends Statement { constructor(public comment: string, public multiline = false, sourceSpan?: ParseSourceSpan|null) { super(null, sourceSpan); } - isEquivalent(stmt: Statement): boolean { return stmt instanceof CommentStmt; } + isEquivalent(stmt: Statement): boolean { + return stmt instanceof CommentStmt; + } visitStatement(visitor: StatementVisitor, context: any): any { return visitor.visitCommentStmt(this, context); } @@ -1060,7 +1127,9 @@ export class JSDocCommentStmt extends Statement { visitStatement(visitor: StatementVisitor, context: any): any { return visitor.visitJSDocCommentStmt(this, context); } - toString(): string { return serializeTags(this.tags); } + toString(): string { + return serializeTags(this.tags); + } } export class TryCatchStmt extends Statement { @@ -1105,11 +1174,17 @@ export interface StatementVisitor { } export class AstTransformer implements StatementVisitor, ExpressionVisitor { - transformExpr(expr: Expression, context: any): Expression { return expr; } + transformExpr(expr: Expression, context: any): Expression { + return expr; + } - transformStmt(stmt: Statement, context: any): Statement { return stmt; } + transformStmt(stmt: Statement, context: any): Statement { + return stmt; + } - visitReadVarExpr(ast: ReadVarExpr, context: any): any { return this.transformExpr(ast, context); } + visitReadVarExpr(ast: ReadVarExpr, context: any): any { + return this.transformExpr(ast, context); + } visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any { return this.transformExpr(ast, context); @@ -1148,7 +1223,7 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { const method = ast.builtin || ast.name; return this.transformExpr( new InvokeMethodExpr( - ast.receiver.visitExpression(this, context), method !, + ast.receiver.visitExpression(this, context), method!, this.visitAllExpressions(ast.args, context), ast.type, ast.sourceSpan), context); } @@ -1169,7 +1244,9 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { context); } - visitLiteralExpr(ast: LiteralExpr, context: any): any { return this.transformExpr(ast, context); } + visitLiteralExpr(ast: LiteralExpr, context: any): any { + return this.transformExpr(ast, context); + } visitLocalizedString(ast: LocalizedString, context: any): any { return this.transformExpr( @@ -1188,7 +1265,7 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { new ConditionalExpr( ast.condition.visitExpression(this, context), ast.trueCase.visitExpression(this, context), - ast.falseCase !.visitExpression(this, context), ast.type, ast.sourceSpan), + ast.falseCase!.visitExpression(this, context), ast.type, ast.sourceSpan), context); } @@ -1284,7 +1361,7 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { } visitDeclareClassStmt(stmt: ClassStmt, context: any): any { - const parent = stmt.parent !.visitExpression(this, context); + const parent = stmt.parent!.visitExpression(this, context); const getters = stmt.getters.map( getter => new ClassGetter( getter.name, this.visitAllStatements(getter.body, context), getter.type, @@ -1341,14 +1418,18 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor { export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor { - visitType(ast: Type, context: any): any { return ast; } + visitType(ast: Type, context: any): any { + return ast; + } visitExpression(ast: Expression, context: any): any { if (ast.type) { ast.type.visitType(this, context); } return ast; } - visitBuiltinType(type: BuiltinType, context: any): any { return this.visitType(type, context); } + visitBuiltinType(type: BuiltinType, context: any): any { + return this.visitType(type, context); + } visitExpressionType(type: ExpressionType, context: any): any { type.value.visitExpression(this, context); if (type.typeParams !== null) { @@ -1356,10 +1437,18 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor } return this.visitType(type, context); } - visitArrayType(type: ArrayType, context: any): any { return this.visitType(type, context); } - visitMapType(type: MapType, context: any): any { return this.visitType(type, context); } - visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any { return ast; } - visitTypeofExpr(ast: TypeofExpr, context: any): any { return this.visitExpression(ast, context); } + visitArrayType(type: ArrayType, context: any): any { + return this.visitType(type, context); + } + visitMapType(type: MapType, context: any): any { + return this.visitType(type, context); + } + visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any { + return ast; + } + visitTypeofExpr(ast: TypeofExpr, context: any): any { + return this.visitExpression(ast, context); + } visitReadVarExpr(ast: ReadVarExpr, context: any): any { return this.visitExpression(ast, context); } @@ -1408,7 +1497,7 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor visitConditionalExpr(ast: ConditionalExpr, context: any): any { ast.condition.visitExpression(this, context); ast.trueCase.visitExpression(this, context); - ast.falseCase !.visitExpression(this, context); + ast.falseCase!.visitExpression(this, context); return this.visitExpression(ast, context); } visitNotExpr(ast: NotExpr, context: any): any { @@ -1482,7 +1571,7 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor return stmt; } visitDeclareClassStmt(stmt: ClassStmt, context: any): any { - stmt.parent !.visitExpression(this, context); + stmt.parent!.visitExpression(this, context); stmt.getters.forEach(getter => this.visitAllStatements(getter.body, context)); if (stmt.constructorMethod) { this.visitAllStatements(stmt.constructorMethod.body, context); @@ -1505,8 +1594,12 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor stmt.error.visitExpression(this, context); return stmt; } - visitCommentStmt(stmt: CommentStmt, context: any): any { return stmt; } - visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any { return stmt; } + visitCommentStmt(stmt: CommentStmt, context: any): any { + return stmt; + } + visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any { + return stmt; + } visitAllStatements(stmts: Statement[], context: any): void { stmts.forEach(stmt => stmt.visitStatement(this, context)); } @@ -1551,7 +1644,7 @@ class _FindExternalReferencesVisitor extends RecursiveAstVisitor { } export function applySourceSpanToStatementIfNeeded( - stmt: Statement, sourceSpan: ParseSourceSpan | null): Statement { + stmt: Statement, sourceSpan: ParseSourceSpan|null): Statement { if (!sourceSpan) { return stmt; } @@ -1560,7 +1653,7 @@ export function applySourceSpanToStatementIfNeeded( } export function applySourceSpanToExpressionIfNeeded( - expr: Expression, sourceSpan: ParseSourceSpan | null): Expression { + expr: Expression, sourceSpan: ParseSourceSpan|null): Expression { if (!sourceSpan) { return expr; } @@ -1569,7 +1662,9 @@ export function applySourceSpanToExpressionIfNeeded( } class _ApplySourceSpanTransformer extends AstTransformer { - constructor(private sourceSpan: ParseSourceSpan) { super(); } + constructor(private sourceSpan: ParseSourceSpan) { + super(); + } private _clone(obj: any): any { const clone = Object.create(obj.constructor.prototype); for (let prop of Object.keys(obj)) { @@ -1596,25 +1691,25 @@ class _ApplySourceSpanTransformer extends AstTransformer { } export function variable( - name: string, type?: Type | null, sourceSpan?: ParseSourceSpan | null): ReadVarExpr { + name: string, type?: Type|null, sourceSpan?: ParseSourceSpan|null): ReadVarExpr { return new ReadVarExpr(name, type, sourceSpan); } export function importExpr( - id: ExternalReference, typeParams: Type[] | null = null, - sourceSpan?: ParseSourceSpan | null): ExternalExpr { + id: ExternalReference, typeParams: Type[]|null = null, + sourceSpan?: ParseSourceSpan|null): ExternalExpr { return new ExternalExpr(id, null, typeParams, sourceSpan); } export function importType( - id: ExternalReference, typeParams: Type[] | null = null, - typeModifiers: TypeModifier[] | null = null): ExpressionType|null { + id: ExternalReference, typeParams: Type[]|null = null, + typeModifiers: TypeModifier[]|null = null): ExpressionType|null { return id != null ? expressionType(importExpr(id, typeParams, null), typeModifiers) : null; } export function expressionType( - expr: Expression, typeModifiers: TypeModifier[] | null = null, - typeParams: Type[] | null = null): ExpressionType { + expr: Expression, typeModifiers: TypeModifier[]|null = null, + typeParams: Type[]|null = null): ExpressionType { return new ExpressionType(expr, typeModifiers, typeParams); } @@ -1623,30 +1718,28 @@ export function typeofExpr(expr: Expression) { } export function literalArr( - values: Expression[], type?: Type | null, - sourceSpan?: ParseSourceSpan | null): LiteralArrayExpr { + values: Expression[], type?: Type|null, sourceSpan?: ParseSourceSpan|null): LiteralArrayExpr { return new LiteralArrayExpr(values, type, sourceSpan); } export function literalMap( values: {key: string, quoted: boolean, value: Expression}[], - type: MapType | null = null): LiteralMapExpr { + type: MapType|null = null): LiteralMapExpr { return new LiteralMapExpr( values.map(e => new LiteralMapEntry(e.key, e.value, e.quoted)), type, null); } -export function not(expr: Expression, sourceSpan?: ParseSourceSpan | null): NotExpr { +export function not(expr: Expression, sourceSpan?: ParseSourceSpan|null): NotExpr { return new NotExpr(expr, sourceSpan); } -export function assertNotNull( - expr: Expression, sourceSpan?: ParseSourceSpan | null): AssertNotNull { +export function assertNotNull(expr: Expression, sourceSpan?: ParseSourceSpan|null): AssertNotNull { return new AssertNotNull(expr, sourceSpan); } export function fn( - params: FnParam[], body: Statement[], type?: Type | null, sourceSpan?: ParseSourceSpan | null, - name?: string | null): FunctionExpr { + params: FnParam[], body: Statement[], type?: Type|null, sourceSpan?: ParseSourceSpan|null, + name?: string|null): FunctionExpr { return new FunctionExpr(params, body, type, sourceSpan, name); } @@ -1655,13 +1748,13 @@ export function ifStmt(condition: Expression, thenClause: Statement[], elseClaus } export function literal( - value: any, type?: Type | null, sourceSpan?: ParseSourceSpan | null): LiteralExpr { + value: any, type?: Type|null, sourceSpan?: ParseSourceSpan|null): LiteralExpr { return new LiteralExpr(value, type, sourceSpan); } export function localizedString( metaBlock: I18nMeta, messageParts: string[], placeholderNames: string[], - expressions: Expression[], sourceSpan?: ParseSourceSpan | null): LocalizedString { + expressions: Expression[], sourceSpan?: ParseSourceSpan|null): LocalizedString { return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan); } @@ -1684,13 +1777,12 @@ export const enum JSDocTagName { */ export type JSDocTag = { // `tagName` is e.g. "param" in an `@param` declaration - tagName: JSDocTagName | string, + tagName: JSDocTagName|string, // Any remaining text on the tag, e.g. the description text?: string, -} | { +}|{ // no `tagName` for plain text documentation that occurs before any `@param` lines - tagName?: undefined, - text: string, + tagName?: undefined, text: string, }; /* diff --git a/packages/compiler/src/output/output_interpreter.ts b/packages/compiler/src/output/output_interpreter.ts index 6fbde79917f9d..c9a33b81558e9 100644 --- a/packages/compiler/src/output/output_interpreter.ts +++ b/packages/compiler/src/output/output_interpreter.ts @@ -15,7 +15,9 @@ export function interpretStatements( const visitor = new StatementInterpreter(reflector); visitor.visitAllStatements(statements, ctx); const result: {[key: string]: any} = {}; - ctx.exports.forEach((exportName) => { result[exportName] = ctx.vars.get(exportName); }); + ctx.exports.forEach((exportName) => { + result[exportName] = ctx.vars.get(exportName); + }); return result; } @@ -63,7 +65,7 @@ function createDynamicClass( _classStmt.methods.forEach(function(method: o.ClassMethod) { const paramNames = method.params.map(param => param.name); // Note: use `function` instead of arrow function to capture `this` - propertyDescriptors[method.name !] = { + propertyDescriptors[method.name!] = { writable: false, configurable: false, value: function(...args: any[]) { @@ -77,7 +79,9 @@ function createDynamicClass( // Note: use `function` instead of arrow function to capture `this` const ctor = function(this: Object, ...args: any[]) { const instanceCtx = new _ExecutionContext(_ctx, this, _classStmt.name, _ctx.vars); - _classStmt.fields.forEach((field) => { (this as any)[field.name] = undefined; }); + _classStmt.fields.forEach((field) => { + (this as any)[field.name] = undefined; + }); _executeFunctionStatements( ctorParamNames, args, _classStmt.constructorMethod.body, instanceCtx, _visitor); }; @@ -88,7 +92,9 @@ function createDynamicClass( class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { constructor(private reflector: CompileReflector) {} - debugAst(ast: o.Expression|o.Statement|o.Type): string { return debugOutputAstAsTypeScript(ast); } + debugAst(ast: o.Expression|o.Statement|o.Type): string { + return debugOutputAstAsTypeScript(ast); + } visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: _ExecutionContext): any { const initialValue = stmt.value ? stmt.value.visitExpression(this, ctx) : undefined; @@ -106,7 +112,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { currCtx.vars.set(expr.name, value); return value; } - currCtx = currCtx.parent !; + currCtx = currCtx.parent!; } throw new Error(`Not declared variable ${expr.name}`); } @@ -117,7 +123,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { throw new Error('Cannot interpret a TypeofExpr'); } visitReadVarExpr(ast: o.ReadVarExpr, ctx: _ExecutionContext): any { - let varName = ast.name !; + let varName = ast.name!; if (ast.builtin != null) { switch (ast.builtin) { case o.BuiltinVar.Super: @@ -139,7 +145,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { if (currCtx.vars.has(varName)) { return currCtx.vars.get(varName); } - currCtx = currCtx.parent !; + currCtx = currCtx.parent!; } throw new Error(`Not declared variable ${varName}`); } @@ -176,7 +182,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { throw new Error(`Unknown builtin method ${expr.builtin}`); } } else { - result = receiver[expr.name !].apply(receiver, args); + result = receiver[expr.name!].apply(receiver, args); } return result; } @@ -184,7 +190,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { const args = this.visitAllExpressions(stmt.args, ctx); const fnExpr = stmt.fn; if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) { - ctx.instance !.constructor.prototype.constructor.apply(ctx.instance, args); + ctx.instance!.constructor.prototype.constructor.apply(ctx.instance, args); return null; } else { const fn = stmt.fn.visitExpression(this, ctx); @@ -227,15 +233,23 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor { visitThrowStmt(stmt: o.ThrowStmt, ctx: _ExecutionContext): any { throw stmt.error.visitExpression(this, ctx); } - visitCommentStmt(stmt: o.CommentStmt, context?: any): any { return null; } - visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, context?: any): any { return null; } + visitCommentStmt(stmt: o.CommentStmt, context?: any): any { + return null; + } + visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, context?: any): any { + return null; + } visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any { const args = this.visitAllExpressions(ast.args, ctx); const clazz = ast.classExpr.visitExpression(this, ctx); return new clazz(...args); } - visitLiteralExpr(ast: o.LiteralExpr, ctx: _ExecutionContext): any { return ast.value; } - visitLocalizedString(ast: o.LocalizedString, context: any): any { return null; } + visitLiteralExpr(ast: o.LiteralExpr, ctx: _ExecutionContext): any { + return ast.value; + } + visitLocalizedString(ast: o.LocalizedString, context: any): any { + return null; + } visitExternalExpr(ast: o.ExternalExpr, ctx: _ExecutionContext): any { return this.reflector.resolveExternalReference(ast.value); } diff --git a/packages/compiler/src/output/output_jit.ts b/packages/compiler/src/output/output_jit.ts index e446a1351bc7a..6e570cfe05bb0 100644 --- a/packages/compiler/src/output/output_jit.ts +++ b/packages/compiler/src/output/output_jit.ts @@ -87,7 +87,9 @@ export class JitEvaluator { * @param args The arguments to pass to the function being executed. * @returns The return value of the executed function. */ - executeFunction(fn: Function, args: any[]) { return fn(...args); } + executeFunction(fn: Function, args: any[]) { + return fn(...args); + } } /** @@ -98,7 +100,9 @@ export class JitEmitterVisitor extends AbstractJsEmitterVisitor { private _evalArgValues: any[] = []; private _evalExportedVars: string[] = []; - constructor(private reflector: CompileReflector) { super(); } + constructor(private reflector: CompileReflector) { + super(); + } createReturnStmt(ctx: EmitterVisitorContext) { const stmt = new o.ReturnStatement(new o.LiteralMapExpr(this._evalExportedVars.map( diff --git a/packages/compiler/src/output/source_map.ts b/packages/compiler/src/output/source_map.ts index 359e5fb071617..739f563b8fbd2 100644 --- a/packages/compiler/src/output/source_map.ts +++ b/packages/compiler/src/output/source_map.ts @@ -23,10 +23,10 @@ type Segment = { export type SourceMap = { version: number, file?: string, - sourceRoot: string, - sources: string[], - sourcesContent: (string | null)[], - mappings: string, + sourceRoot: string, + sources: string[], + sourcesContent: (string|null)[], + mappings: string, }; export class SourceMapGenerator { @@ -75,10 +75,12 @@ export class SourceMapGenerator { } /** - * @internal strip this from published d.ts files due to - * https://github.com/microsoft/TypeScript/issues/36216 - */ - private get currentLine(): Segment[]|null { return this.lines.slice(-1)[0]; } + * @internal strip this from published d.ts files due to + * https://github.com/microsoft/TypeScript/issues/36216 + */ + private get currentLine(): Segment[]|null { + return this.lines.slice(-1)[0]; + } toJSON(): SourceMap|null { if (!this.hasMappings) { @@ -87,7 +89,7 @@ export class SourceMapGenerator { const sourcesIndex = new Map<string, number>(); const sources: string[] = []; - const sourcesContent: (string | null)[] = []; + const sourcesContent: (string|null)[] = []; Array.from(this.sourcesContent.keys()).forEach((url: string, i: number) => { sourcesIndex.set(url, i); @@ -113,14 +115,14 @@ export class SourceMapGenerator { if (segment.sourceUrl != null) { // zero-based index into the “sources” list segAsStr += - toBase64VLQ(sourcesIndex.get(segment.sourceUrl) ! - lastSourceIndex); - lastSourceIndex = sourcesIndex.get(segment.sourceUrl) !; + toBase64VLQ(sourcesIndex.get(segment.sourceUrl)! - lastSourceIndex); + lastSourceIndex = sourcesIndex.get(segment.sourceUrl)!; // the zero-based starting line in the original source - segAsStr += toBase64VLQ(segment.sourceLine0 ! - lastSourceLine0); - lastSourceLine0 = segment.sourceLine0 !; + segAsStr += toBase64VLQ(segment.sourceLine0! - lastSourceLine0); + lastSourceLine0 = segment.sourceLine0!; // the zero-based starting column in the original source - segAsStr += toBase64VLQ(segment.sourceCol0 ! - lastSourceCol0); - lastSourceCol0 = segment.sourceCol0 !; + segAsStr += toBase64VLQ(segment.sourceCol0! - lastSourceCol0); + lastSourceCol0 = segment.sourceCol0!; } return segAsStr; diff --git a/packages/compiler/src/output/ts_emitter.ts b/packages/compiler/src/output/ts_emitter.ts index e82a3258e0850..514d570448962 100644 --- a/packages/compiler/src/output/ts_emitter.ts +++ b/packages/compiler/src/output/ts_emitter.ts @@ -15,8 +15,7 @@ import * as o from './output_ast'; const _debugFilePath = '/debug/lib'; -export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]): - string { +export function debugOutputAstAsTypeScript(ast: o.Statement|o.Expression|o.Type|any[]): string { const converter = new _TsEmitterVisitor(); const ctx = EmitterVisitorContext.createRoot(); const asts: any[] = Array.isArray(ast) ? ast : [ast]; @@ -147,7 +146,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor reexports = []; this.reexports.set(moduleName, reexports); } - reexports.push({name: name !, as: stmt.name}); + reexports.push({name: name!, as: stmt.name}); return null; } } @@ -175,7 +174,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any { ctx.print(ast, `(<`); - ast.type !.visitType(this, ctx); + ast.type!.visitType(this, ctx); ctx.print(ast, `>`); ast.value.visitExpression(this, ctx); ctx.print(ast, `)`); @@ -422,7 +421,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor } ctx.print(null, `${prefix}.`); } - ctx.print(null, name !); + ctx.print(null, name!); if (this.typeExpression > 0) { // If we are in a type expression that refers to a generic type then supply @@ -433,7 +432,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor const suppliedParameters = typeParams || []; if (suppliedParameters.length > 0) { ctx.print(null, `<`); - this.visitAllObjects(type => type.visitType(this, ctx), typeParams !, ctx, ','); + this.visitAllObjects(type => type.visitType(this, ctx), typeParams!, ctx, ','); ctx.print(null, `>`); } } diff --git a/packages/compiler/src/output/value_util.ts b/packages/compiler/src/output/value_util.ts index c043800a6f350..b568069f36e93 100644 --- a/packages/compiler/src/output/value_util.ts +++ b/packages/compiler/src/output/value_util.ts @@ -14,14 +14,23 @@ import * as o from './output_ast'; export const QUOTED_KEYS = '$quoted$'; export function convertValueToOutputAst( - ctx: OutputContext, value: any, type: o.Type | null = null): o.Expression { + ctx: OutputContext, value: any, type: o.Type|null = null): o.Expression { return visitValue(value, new _ValueOutputAstTransformer(ctx), type); } class _ValueOutputAstTransformer implements ValueTransformer { constructor(private ctx: OutputContext) {} visitArray(arr: any[], type: o.Type): o.Expression { - return o.literalArr(arr.map(value => visitValue(value, this, null)), type); + const values: o.Expression[] = []; + // Note Array.map() must not be used to convert the values because it will + // skip over empty elements in arrays constructed using `new Array(length)`, + // resulting in `undefined` elements. This breaks the type guarantee that + // all values in `o.LiteralArrayExpr` are of type `o.Expression`. + // See test case in `value_util_spec.ts`. + for (let i = 0; i < arr.length; ++i) { + values.push(visitValue(arr[i], this, null /* context */)); + } + return o.literalArr(values, type); } visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression { @@ -34,7 +43,9 @@ class _ValueOutputAstTransformer implements ValueTransformer { return new o.LiteralMapExpr(entries, type); } - visitPrimitive(value: any, type: o.Type): o.Expression { return o.literal(value, type); } + visitPrimitive(value: any, type: o.Type): o.Expression { + return o.literal(value, type); + } visitOther(value: any, type: o.Type): o.Expression { if (value instanceof o.Expression) { diff --git a/packages/compiler/src/parse_util.ts b/packages/compiler/src/parse_util.ts index 8935dfc26aaed..e6e7c30758ca2 100644 --- a/packages/compiler/src/parse_util.ts +++ b/packages/compiler/src/parse_util.ts @@ -7,7 +7,6 @@ */ import * as chars from './chars'; import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata'; -import {error} from './util'; export class ParseLocation { constructor( @@ -109,9 +108,6 @@ export class ParseSourceSpan { } } -export const EMPTY_PARSE_LOCATION = new ParseLocation(new ParseSourceFile('', ''), 0, 0, 0); -export const EMPTY_SOURCE_SPAN = new ParseSourceSpan(EMPTY_PARSE_LOCATION, EMPTY_PARSE_LOCATION); - export enum ParseErrorLevel { WARNING, ERROR, diff --git a/packages/compiler/src/pipe_resolver.ts b/packages/compiler/src/pipe_resolver.ts index 4f15e9a3ee12c..c965571e6a082 100644 --- a/packages/compiler/src/pipe_resolver.ts +++ b/packages/compiler/src/pipe_resolver.ts @@ -7,7 +7,7 @@ */ import {CompileReflector} from './compile_reflector'; -import {Pipe, Type, createPipe} from './core'; +import {createPipe, Pipe, Type} from './core'; import {findLast} from './directive_resolver'; import {resolveForwardRef, stringify} from './util'; diff --git a/packages/compiler/src/provider_analyzer.ts b/packages/compiler/src/provider_analyzer.ts index f4b335957a7b7..677dbbaf00127 100644 --- a/packages/compiler/src/provider_analyzer.ts +++ b/packages/compiler/src/provider_analyzer.ts @@ -9,12 +9,14 @@ import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata'; import {CompileReflector} from './compile_reflector'; -import {Identifiers, createTokenForExternalReference} from './identifiers'; +import {createTokenForExternalReference, Identifiers} from './identifiers'; import {ParseError, ParseSourceSpan} from './parse_util'; import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, QueryMatch, ReferenceAst} from './template_parser/template_ast'; export class ProviderError extends ParseError { - constructor(message: string, span: ParseSourceSpan) { super(span, message); } + constructor(message: string, span: ParseSourceSpan) { + super(span, message); + } } export interface QueryWithId { @@ -125,7 +127,9 @@ export class ProviderElementContext { get queryMatches(): QueryMatch[] { const allMatches: QueryMatch[] = []; - this._queriedTokens.forEach((matches: QueryMatch[]) => { allMatches.push(...matches); }); + this._queriedTokens.forEach((matches: QueryMatch[]) => { + allMatches.push(...matches); + }); return allMatches; } @@ -171,9 +175,10 @@ export class ProviderElementContext { requestingProviderType: ProviderAstType, token: CompileTokenMetadata, eager: boolean): ProviderAst|null { const resolvedProvider = this._allProviders.get(tokenReference(token)); - if (!resolvedProvider || ((requestingProviderType === ProviderAstType.Directive || - requestingProviderType === ProviderAstType.PublicService) && - resolvedProvider.providerType === ProviderAstType.PrivateService) || + if (!resolvedProvider || + ((requestingProviderType === ProviderAstType.Directive || + requestingProviderType === ProviderAstType.PublicService) && + resolvedProvider.providerType === ProviderAstType.PrivateService) || ((requestingProviderType === ProviderAstType.PrivateService || requestingProviderType === ProviderAstType.PublicService) && resolvedProvider.providerType === ProviderAstType.Builtin)) { @@ -191,25 +196,25 @@ export class ProviderElementContext { this._seenProviders.set(tokenReference(token), true); const transformedProviders = resolvedProvider.providers.map((provider) => { let transformedUseValue = provider.useValue; - let transformedUseExisting = provider.useExisting !; - let transformedDeps: CompileDiDependencyMetadata[] = undefined !; + let transformedUseExisting = provider.useExisting!; + let transformedDeps: CompileDiDependencyMetadata[] = undefined!; if (provider.useExisting != null) { const existingDiDep = this._getDependency( - resolvedProvider.providerType, {token: provider.useExisting}, eager) !; + resolvedProvider.providerType, {token: provider.useExisting}, eager)!; if (existingDiDep.token != null) { transformedUseExisting = existingDiDep.token; } else { - transformedUseExisting = null !; + transformedUseExisting = null!; transformedUseValue = existingDiDep.value; } } else if (provider.useFactory) { const deps = provider.deps || provider.useFactory.diDeps; transformedDeps = - deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager) !); + deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager)!); } else if (provider.useClass) { const deps = provider.deps || provider.useClass.diDeps; transformedDeps = - deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager) !); + deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager)!); } return _transformProvider(provider, { useExisting: transformedUseExisting, @@ -227,7 +232,7 @@ export class ProviderElementContext { requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata, eager: boolean = false): CompileDiDependencyMetadata|null { if (dep.isAttribute) { - const attrValue = this._attrs[dep.token !.value]; + const attrValue = this._attrs[dep.token!.value]; return {isValue: true, value: attrValue == null ? null : attrValue}; } @@ -248,7 +253,7 @@ export class ProviderElementContext { } if (tokenReference(dep.token) === this.viewContext.reflector.resolveExternalReference(Identifiers.ViewContainerRef)) { - (this as{transformedHasViewContainer: boolean}).transformedHasViewContainer = true; + (this as {transformedHasViewContainer: boolean}).transformedHasViewContainer = true; } } // access the injector @@ -290,8 +295,8 @@ export class ProviderElementContext { // check @Host restriction if (!result) { if (!dep.isHost || this.viewContext.component.isHost || - this.viewContext.component.type.reference === tokenReference(dep.token !) || - this.viewContext.viewProviders.get(tokenReference(dep.token !)) != null) { + this.viewContext.component.type.reference === tokenReference(dep.token!) || + this.viewContext.viewProviders.get(tokenReference(dep.token!)) != null) { result = dep; } else { result = dep.isOptional ? {isValue: true, value: null} : null; @@ -368,15 +373,15 @@ export class NgModuleProviderAnalyzer { this._seenProviders.set(tokenReference(token), true); const transformedProviders = resolvedProvider.providers.map((provider) => { let transformedUseValue = provider.useValue; - let transformedUseExisting = provider.useExisting !; - let transformedDeps: CompileDiDependencyMetadata[] = undefined !; + let transformedUseExisting = provider.useExisting!; + let transformedDeps: CompileDiDependencyMetadata[] = undefined!; if (provider.useExisting != null) { const existingDiDep = this._getDependency({token: provider.useExisting}, eager, resolvedProvider.sourceSpan); if (existingDiDep.token != null) { transformedUseExisting = existingDiDep.token; } else { - transformedUseExisting = null !; + transformedUseExisting = null!; transformedUseValue = existingDiDep.value; } } else if (provider.useFactory) { @@ -478,7 +483,8 @@ function _resolveProviders( let resolvedProvider = targetProvidersByToken.get(tokenReference(provider.token)); if (resolvedProvider != null && !!resolvedProvider.multiProvider !== !!provider.multi) { targetErrors.push(new ProviderError( - `Mixing multi and non multi provider is not possible for token ${tokenName(resolvedProvider.token)}`, + `Mixing multi and non multi provider is not possible for token ${ + tokenName(resolvedProvider.token)}`, sourceSpan)); } if (!resolvedProvider) { diff --git a/packages/compiler/src/render3/r3_ast.ts b/packages/compiler/src/render3/r3_ast.ts index 45ae7dd79d12d..3fa682a768b6a 100644 --- a/packages/compiler/src/render3/r3_ast.ts +++ b/packages/compiler/src/render3/r3_ast.ts @@ -18,19 +18,25 @@ export interface Node { export class Text implements Node { constructor(public value: string, public sourceSpan: ParseSourceSpan) {} - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitText(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitText(this); + } } export class BoundText implements Node { constructor(public value: AST, public sourceSpan: ParseSourceSpan, public i18n?: I18nMeta) {} - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundText(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitBoundText(this); + } } export class TextAttribute implements Node { constructor( public name: string, public value: string, public sourceSpan: ParseSourceSpan, public valueSpan?: ParseSourceSpan, public i18n?: I18nMeta) {} - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitTextAttribute(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitTextAttribute(this); + } } export class BoundAttribute implements Node { @@ -45,7 +51,9 @@ export class BoundAttribute implements Node { prop.valueSpan, i18n); } - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundAttribute(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitBoundAttribute(this); + } } export class BoundEvent implements Node { @@ -62,7 +70,9 @@ export class BoundEvent implements Node { event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan); } - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundEvent(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitBoundEvent(this); + } } export class Element implements Node { @@ -76,7 +86,9 @@ export class Element implements Node { this.sourceSpan = new ParseSourceSpan(sourceSpan.start, endSourceSpan.end); } } - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitElement(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitElement(this); + } } export class Template implements Node { @@ -86,36 +98,46 @@ export class Template implements Node { public children: Node[], public references: Reference[], public variables: Variable[], public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null, public endSourceSpan: ParseSourceSpan|null, public i18n?: I18nMeta) {} - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitTemplate(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitTemplate(this); + } } export class Content implements Node { constructor( public selector: string, public attributes: TextAttribute[], public sourceSpan: ParseSourceSpan, public i18n?: I18nMeta) {} - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitContent(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitContent(this); + } } export class Variable implements Node { constructor( public name: string, public value: string, public sourceSpan: ParseSourceSpan, public valueSpan?: ParseSourceSpan) {} - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitVariable(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitVariable(this); + } } export class Reference implements Node { constructor( public name: string, public value: string, public sourceSpan: ParseSourceSpan, public valueSpan?: ParseSourceSpan) {} - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitReference(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitReference(this); + } } export class Icu implements Node { constructor( public vars: {[name: string]: BoundText}, - public placeholders: {[name: string]: Text | BoundText}, public sourceSpan: ParseSourceSpan, + public placeholders: {[name: string]: Text|BoundText}, public sourceSpan: ParseSourceSpan, public i18n?: I18nMeta) {} - visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitIcu(this); } + visit<Result>(visitor: Visitor<Result>): Result { + return visitor.visitIcu(this); + } } export interface Visitor<Result = any> { @@ -210,16 +232,34 @@ export class TransformVisitor implements Visitor<Node> { return template; } - visitContent(content: Content): Node { return content; } + visitContent(content: Content): Node { + return content; + } - visitVariable(variable: Variable): Node { return variable; } - visitReference(reference: Reference): Node { return reference; } - visitTextAttribute(attribute: TextAttribute): Node { return attribute; } - visitBoundAttribute(attribute: BoundAttribute): Node { return attribute; } - visitBoundEvent(attribute: BoundEvent): Node { return attribute; } - visitText(text: Text): Node { return text; } - visitBoundText(text: BoundText): Node { return text; } - visitIcu(icu: Icu): Node { return icu; } + visitVariable(variable: Variable): Node { + return variable; + } + visitReference(reference: Reference): Node { + return reference; + } + visitTextAttribute(attribute: TextAttribute): Node { + return attribute; + } + visitBoundAttribute(attribute: BoundAttribute): Node { + return attribute; + } + visitBoundEvent(attribute: BoundEvent): Node { + return attribute; + } + visitText(text: Text): Node { + return text; + } + visitBoundText(text: BoundText): Node { + return text; + } + visitIcu(icu: Icu): Node { + return icu; + } } export function visitAll<Result>(visitor: Visitor<Result>, nodes: Node[]): Result[] { diff --git a/packages/compiler/src/render3/r3_factory.ts b/packages/compiler/src/render3/r3_factory.ts index c3ede764a51aa..dfb6fea44b13a 100644 --- a/packages/compiler/src/render3/r3_factory.ts +++ b/packages/compiler/src/render3/r3_factory.ts @@ -89,8 +89,8 @@ export interface R3ExpressionFactoryMetadata extends R3ConstructorFactoryMetadat expression: o.Expression; } -export type R3FactoryMetadata = R3ConstructorFactoryMetadata | R3DelegatedFactoryMetadata | - R3DelegatedFnOrClassMetadata | R3ExpressionFactoryMetadata; +export type R3FactoryMetadata = R3ConstructorFactoryMetadata|R3DelegatedFactoryMetadata| + R3DelegatedFnOrClassMetadata|R3ExpressionFactoryMetadata; export enum R3FactoryTarget { Directive = 0, @@ -141,6 +141,13 @@ export interface R3DependencyMetadata { */ token: o.Expression; + /** + * If an @Attribute decorator is present, this is the literal type of the attribute name, or + * the unknown type if no literal type is available (e.g. the attribute name is an expression). + * Will be null otherwise. + */ + attribute: o.Expression|null; + /** * An enum indicating whether this dependency has special meaning to Angular and needs to be * injected specially. @@ -180,6 +187,7 @@ export interface R3FactoryFn { export function compileFactoryFunction(meta: R3FactoryMetadata): R3FactoryFn { const t = o.variable('t'); const statements: o.Statement[] = []; + let ctorDepsType: o.Type = o.NONE_TYPE; // The type to instantiate via constructor invocation. If there is no delegated factory, meaning // this type is always created by constructor invocation, then this is the type-to-create @@ -197,6 +205,8 @@ export function compileFactoryFunction(meta: R3FactoryMetadata): R3FactoryFn { ctorExpr = new o.InstantiateExpr( typeForCtor, injectDependencies(meta.deps, meta.injectFn, meta.target === R3FactoryTarget.Pipe)); + + ctorDepsType = createCtorDepsType(meta.deps); } } else { const baseFactory = o.variable(`ɵ${meta.name}_BaseFactory`); @@ -269,8 +279,8 @@ export function compileFactoryFunction(meta: R3FactoryMetadata): R3FactoryFn { [new o.FnParam('t', o.DYNAMIC_TYPE)], body, o.INFERRED_TYPE, undefined, `${meta.name}_Factory`), statements, - type: o.expressionType( - o.importExpr(R3.FactoryDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount)])) + type: o.expressionType(o.importExpr( + R3.FactoryDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType])) }; } @@ -319,6 +329,49 @@ function compileInjectDependency( } } +function createCtorDepsType(deps: R3DependencyMetadata[]): o.Type { + let hasTypes = false; + const attributeTypes = deps.map(dep => { + const type = createCtorDepType(dep); + if (type !== null) { + hasTypes = true; + return type; + } else { + return o.literal(null); + } + }); + + if (hasTypes) { + return o.expressionType(o.literalArr(attributeTypes)); + } else { + return o.NONE_TYPE; + } +} + +function createCtorDepType(dep: R3DependencyMetadata): o.LiteralMapExpr|null { + const entries: {key: string, quoted: boolean, value: o.Expression}[] = []; + + if (dep.resolved === R3ResolvedDependencyType.Attribute) { + if (dep.attribute !== null) { + entries.push({key: 'attribute', value: dep.attribute, quoted: false}); + } + } + if (dep.optional) { + entries.push({key: 'optional', value: o.literal(true), quoted: false}); + } + if (dep.host) { + entries.push({key: 'host', value: o.literal(true), quoted: false}); + } + if (dep.self) { + entries.push({key: 'self', value: o.literal(true), quoted: false}); + } + if (dep.skipSelf) { + entries.push({key: 'skipSelf', value: o.literal(true), quoted: false}); + } + + return entries.length > 0 ? o.literalMap(entries) : null; +} + /** * A helper function useful for extracting `R3DependencyMetadata` from a Render2 * `CompileTypeMetadata` instance. @@ -348,6 +401,7 @@ export function dependenciesFromGlobalMetadata( // Construct the dependency. deps.push({ token, + attribute: null, resolved, host: !!dependency.isHost, optional: !!dependency.isOptional, diff --git a/packages/compiler/src/render3/r3_jit.ts b/packages/compiler/src/render3/r3_jit.ts index 6c73d6a6a8085..89b7cc9c0c4c6 100644 --- a/packages/compiler/src/render3/r3_jit.ts +++ b/packages/compiler/src/render3/r3_jit.ts @@ -21,28 +21,44 @@ export class R3JitReflector implements CompileReflector { resolveExternalReference(ref: o.ExternalReference): any { // This reflector only handles @angular/core imports. if (ref.moduleName !== '@angular/core') { - throw new Error( - `Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`); + throw new Error(`Cannot resolve external reference to ${ + ref.moduleName}, only references to @angular/core are supported.`); } - if (!this.context.hasOwnProperty(ref.name !)) { + if (!this.context.hasOwnProperty(ref.name!)) { throw new Error(`No value provided for @angular/core symbol '${ref.name!}'.`); } - return this.context[ref.name !]; + return this.context[ref.name!]; } - parameters(typeOrFunc: any): any[][] { throw new Error('Not implemented.'); } + parameters(typeOrFunc: any): any[][] { + throw new Error('Not implemented.'); + } - annotations(typeOrFunc: any): any[] { throw new Error('Not implemented.'); } + annotations(typeOrFunc: any): any[] { + throw new Error('Not implemented.'); + } - shallowAnnotations(typeOrFunc: any): any[] { throw new Error('Not implemented.'); } + shallowAnnotations(typeOrFunc: any): any[] { + throw new Error('Not implemented.'); + } - tryAnnotations(typeOrFunc: any): any[] { throw new Error('Not implemented.'); } + tryAnnotations(typeOrFunc: any): any[] { + throw new Error('Not implemented.'); + } - propMetadata(typeOrFunc: any): {[key: string]: any[];} { throw new Error('Not implemented.'); } + propMetadata(typeOrFunc: any): {[key: string]: any[];} { + throw new Error('Not implemented.'); + } - hasLifecycleHook(type: any, lcProperty: string): boolean { throw new Error('Not implemented.'); } + hasLifecycleHook(type: any, lcProperty: string): boolean { + throw new Error('Not implemented.'); + } - guards(typeOrFunc: any): {[key: string]: any;} { throw new Error('Not implemented.'); } + guards(typeOrFunc: any): {[key: string]: any;} { + throw new Error('Not implemented.'); + } - componentModuleUrl(type: any, cmpMetadata: any): string { throw new Error('Not implemented.'); } + componentModuleUrl(type: any, cmpMetadata: any): string { + throw new Error('Not implemented.'); + } } diff --git a/packages/compiler/src/render3/r3_module_compiler.ts b/packages/compiler/src/render3/r3_module_compiler.ts index 833a88bc533e4..69379d08b2b3e 100644 --- a/packages/compiler/src/render3/r3_module_compiler.ts +++ b/packages/compiler/src/render3/r3_module_compiler.ts @@ -12,9 +12,9 @@ import {mapLiteral} from '../output/map_util'; import * as o from '../output/output_ast'; import {OutputContext} from '../util'; -import {R3DependencyMetadata, R3FactoryTarget, compileFactoryFunction} from './r3_factory'; +import {compileFactoryFunction, R3DependencyMetadata, R3FactoryTarget} from './r3_factory'; import {Identifiers as R3} from './r3_identifiers'; -import {R3Reference, convertMetaToOutput, jitOnlyGuardedExpression, mapToMapExpression} from './util'; +import {convertMetaToOutput, jitOnlyGuardedExpression, mapToMapExpression, R3Reference} from './util'; export interface R3NgModuleDef { expression: o.Expression; @@ -108,9 +108,7 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { } = meta; const additionalStatements: o.Statement[] = []; - const definitionMap = { - type: internalType - } as{ + const definitionMap = {type: internalType} as { type: o.Expression, bootstrap: o.Expression, declarations: o.Expression, @@ -177,7 +175,7 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { function generateSetNgModuleScopeCall(meta: R3NgModuleMetadata): o.Statement|null { const {adjacentType: moduleType, declarations, imports, exports, containsForwardDecls} = meta; - const scopeMap = {} as{ + const scopeMap = {} as { declarations: o.Expression, imports: o.Expression, exports: o.Expression, @@ -247,7 +245,7 @@ export function compileInjector(meta: R3InjectorMetadata): R3InjectorDef { }); const definitionMap = { factory: result.factory, - } as{factory: o.Expression, providers: o.Expression, imports: o.Expression}; + } as {factory: o.Expression, providers: o.Expression, imports: o.Expression}; if (meta.providers !== null) { definitionMap.providers = meta.providers; @@ -267,7 +265,7 @@ export function compileInjector(meta: R3InjectorMetadata): R3InjectorDef { export function compileNgModuleFromRender2( ctx: OutputContext, ngModule: CompileShallowModuleMetadata, injectableCompiler: InjectableCompiler): void { - const className = identifierName(ngModule.type) !; + const className = identifierName(ngModule.type)!; const rawImports = ngModule.rawImports ? [ngModule.rawImports] : []; const rawExports = ngModule.rawExports ? [ngModule.rawExports] : []; @@ -288,7 +286,8 @@ export function compileNgModuleFromRender2( /* name */ 'ɵinj', /* type */ o.INFERRED_TYPE, /* modifiers */[o.StmtModifier.Static], - /* initializer */ injectorDef, )], + /* initializer */ injectorDef, + )], /* getters */[], /* constructorMethod */ new o.ClassMethod(null, [], []), /* methods */[])); diff --git a/packages/compiler/src/render3/r3_pipe_compiler.ts b/packages/compiler/src/render3/r3_pipe_compiler.ts index 703a001a48a25..25797c6635cbe 100644 --- a/packages/compiler/src/render3/r3_pipe_compiler.ts +++ b/packages/compiler/src/render3/r3_pipe_compiler.ts @@ -10,9 +10,9 @@ import {CompilePipeMetadata, identifierName} from '../compile_metadata'; import {CompileReflector} from '../compile_reflector'; import {DefinitionKind} from '../constant_pool'; import * as o from '../output/output_ast'; -import {OutputContext, error} from '../util'; +import {error, OutputContext} from '../util'; -import {R3DependencyMetadata, R3FactoryTarget, compileFactoryFunction, dependenciesFromGlobalMetadata} from './r3_factory'; +import {compileFactoryFunction, dependenciesFromGlobalMetadata, R3DependencyMetadata, R3FactoryTarget} from './r3_factory'; import {Identifiers as R3} from './r3_identifiers'; import {R3Reference, typeWithParameters, wrapReference} from './util'; diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts index a75d384ebc247..7ab7acd4ab359 100644 --- a/packages/compiler/src/render3/r3_template_transform.ts +++ b/packages/compiler/src/render3/r3_template_transform.ts @@ -52,6 +52,7 @@ export interface Render3ParseResult { errors: ParseError[]; styles: string[]; styleUrls: string[]; + ngContentSelectors: string[]; } export function htmlAstToRender3Ast( @@ -73,6 +74,7 @@ export function htmlAstToRender3Ast( errors: allErrors, styleUrls: transformer.styleUrls, styles: transformer.styles, + ngContentSelectors: transformer.ngContentSelectors, }; } @@ -80,6 +82,7 @@ class HtmlAstToIvyAst implements html.Visitor { errors: ParseError[] = []; styles: string[] = []; styleUrls: string[] = []; + ngContentSelectors: string[] = []; private inI18nBlock: boolean = false; constructor(private bindingParser: BindingParser) {} @@ -189,6 +192,8 @@ class HtmlAstToIvyAst implements html.Visitor { const selector = preparsedElement.selectAttr; const attrs: t.TextAttribute[] = element.attrs.map(attr => this.visitAttribute(attr)); parsedElement = new t.Content(selector, attrs, element.sourceSpan, element.i18n); + + this.ngContentSelectors.push(selector); } else if (isTemplateElement) { // `<ng-template>` const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta); @@ -210,7 +215,7 @@ class HtmlAstToIvyAst implements html.Visitor { // Moreover, if the node is an element, then we need to hoist its attributes to the template // node for matching against content projection selectors. const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta); - const templateAttrs: (t.TextAttribute | t.BoundAttribute)[] = []; + const templateAttrs: (t.TextAttribute|t.BoundAttribute)[] = []; attrs.literal.forEach(attr => templateAttrs.push(attr)); attrs.bound.forEach(attr => templateAttrs.push(attr)); const hoistedAttrs = parsedElement instanceof t.Element ? @@ -255,12 +260,12 @@ class HtmlAstToIvyAst implements html.Visitor { return null; } if (!isI18nRootNode(expansion.i18n)) { - throw new Error( - `Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${expansion.sourceSpan.toString()}. Expected a "Message"`); + throw new Error(`Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${ + expansion.sourceSpan.toString()}. Expected a "Message"`); } const message = expansion.i18n; const vars: {[name: string]: t.BoundText} = {}; - const placeholders: {[name: string]: t.Text | t.BoundText} = {}; + const placeholders: {[name: string]: t.Text|t.BoundText} = {}; // extract VARs from ICUs - we process them separately while // assembling resulting message via goog.getMsg function, since // we need to pass them to top-level goog.getMsg call @@ -279,9 +284,13 @@ class HtmlAstToIvyAst implements html.Visitor { return new t.Icu(vars, placeholders, expansion.sourceSpan, message); } - visitExpansionCase(expansionCase: html.ExpansionCase): null { return null; } + visitExpansionCase(expansionCase: html.ExpansionCase): null { + return null; + } - visitComment(comment: html.Comment): null { return null; } + visitComment(comment: html.Comment): null { + return null; + } // convert view engine `ParsedProperty` to a format suitable for IVY private extractAttributes( @@ -455,18 +464,26 @@ class NonBindableVisitor implements html.Visitor { ast.startSourceSpan, ast.endSourceSpan); } - visitComment(comment: html.Comment): any { return null; } + visitComment(comment: html.Comment): any { + return null; + } visitAttribute(attribute: html.Attribute): t.TextAttribute { return new t.TextAttribute( attribute.name, attribute.value, attribute.sourceSpan, undefined, attribute.i18n); } - visitText(text: html.Text): t.Text { return new t.Text(text.value, text.sourceSpan); } + visitText(text: html.Text): t.Text { + return new t.Text(text.value, text.sourceSpan); + } - visitExpansion(expansion: html.Expansion): any { return null; } + visitExpansion(expansion: html.Expansion): any { + return null; + } - visitExpansionCase(expansionCase: html.ExpansionCase): any { return null; } + visitExpansionCase(expansionCase: html.ExpansionCase): any { + return null; + } } const NON_BINDABLE_VISITOR = new NonBindableVisitor(); diff --git a/packages/compiler/src/render3/util.ts b/packages/compiler/src/render3/util.ts index 5572495dcfffb..a450f8c0f28c0 100644 --- a/packages/compiler/src/render3/util.ts +++ b/packages/compiler/src/render3/util.ts @@ -13,14 +13,13 @@ import {OutputContext} from '../util'; /** * Convert an object map with `Expression` values into a `LiteralMapExpr`. */ -export function mapToMapExpression(map: {[key: string]: o.Expression | undefined}): - o.LiteralMapExpr { +export function mapToMapExpression(map: {[key: string]: o.Expression|undefined}): o.LiteralMapExpr { const result = Object.keys(map).map( key => ({ key, // The assertion here is because really TypeScript doesn't allow us to express that if the // key is present, it will have a value, but this is true in reality. - value: map[key] !, + value: map[key]!, quoted: false, })); return o.literalMap(result); diff --git a/packages/compiler/src/render3/view/api.ts b/packages/compiler/src/render3/view/api.ts index 5b38e0a6a6776..a369af694d80c 100644 --- a/packages/compiler/src/render3/view/api.ts +++ b/packages/compiler/src/render3/view/api.ts @@ -88,7 +88,7 @@ export interface R3DirectiveMetadata { /** * A mapping of input field names to the property names. */ - inputs: {[field: string]: string | [string, string]}; + inputs: {[field: string]: string|[string, string]}; /** * A mapping of output field names to the property names. @@ -129,6 +129,12 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata { * Parsed nodes of the template. */ nodes: t.Node[]; + + /** + * Any ng-content selectors extracted from the template. Contains `null` when an ng-content + * element without selector is present. + */ + ngContentSelectors: string[]; }; /** diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index a481bdb67482b..eb1b14ba6d224 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -20,17 +20,17 @@ import {CssSelector, SelectorMatcher} from '../../selector'; import {ShadowCss} from '../../shadow_css'; import {CONTENT_ATTR, HOST_ATTR} from '../../style_compiler'; import {BindingParser} from '../../template_parser/binding_parser'; -import {OutputContext, error} from '../../util'; +import {error, OutputContext} from '../../util'; import {BoundEvent} from '../r3_ast'; -import {R3FactoryTarget, compileFactoryFunction} from '../r3_factory'; +import {compileFactoryFunction, R3DependencyMetadata, R3FactoryTarget, R3ResolvedDependencyType} from '../r3_factory'; import {Identifiers as R3} from '../r3_identifiers'; import {Render3ParseResult} from '../r3_template_transform'; import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util'; import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './api'; import {MIN_STYLING_BINDING_SLOTS_REQUIRED, StylingBuilder, StylingInstructionCall} from './styling_builder'; -import {BindingScope, TemplateDefinitionBuilder, ValueConverter, makeBindingParser, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn} from './template'; -import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, chainedInstruction, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util'; +import {BindingScope, makeBindingParser, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn, TemplateDefinitionBuilder, ValueConverter} from './template'; +import {asLiteral, chainedInstruction, conditionallyCreateMapObjectLiteral, CONTEXT_NAME, DefinitionMap, getQueryPredicate, RENDER_FLAGS, TEMPORARY_NAME, temporaryAllocator} from './util'; const EMPTY_ARRAY: any[] = []; @@ -65,9 +65,10 @@ function baseDirectiveFields( // e.g. `hostBindings: (rf, ctx) => { ... } definitionMap.set( - 'hostBindings', createHostBindingsFunction( - meta.host, meta.typeSourceSpan, bindingParser, constantPool, - meta.selector || '', meta.name, definitionMap)); + 'hostBindings', + createHostBindingsFunction( + meta.host, meta.typeSourceSpan, bindingParser, constantPool, meta.selector || '', + meta.name, definitionMap)); // e.g 'inputs: {a: 'a'}` definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true)); @@ -85,8 +86,7 @@ function baseDirectiveFields( /** * Add features to the definition map. */ -function addFeatures( - definitionMap: DefinitionMap, meta: R3DirectiveMetadata | R3ComponentMetadata) { +function addFeatures(definitionMap: DefinitionMap, meta: R3DirectiveMetadata|R3ComponentMetadata) { // e.g. `features: [NgOnChangesFeature]` const features: o.Expression[] = []; @@ -124,7 +124,9 @@ export function compileDirectiveFromMetadata( addFeatures(definitionMap, meta); const expression = o.importExpr(R3.defineDirective).callFn([definitionMap.toLiteralMap()]); - const type = createTypeForDef(meta, R3.DirectiveDefWithMeta); + const typeParams = createDirectiveTypeParams(meta); + const type = o.expressionType(o.importExpr(R3.DirectiveDefWithMeta, typeParams)); + return {expression, type}; } @@ -146,10 +148,11 @@ export function compileComponentFromMetadata( const selectorAttributes = firstSelector.getAttrs(); if (selectorAttributes.length) { definitionMap.set( - 'attrs', constantPool.getConstLiteral( - o.literalArr(selectorAttributes.map( - value => value != null ? o.literal(value) : o.literal(undefined))), - /* forceShared */ true)); + 'attrs', + constantPool.getConstLiteral( + o.literalArr(selectorAttributes.map( + value => value != null ? o.literal(value) : o.literal(undefined))), + /* forceShared */ true)); } } @@ -174,7 +177,7 @@ export function compileComponentFromMetadata( const template = meta.template; const templateBuilder = new TemplateDefinitionBuilder( - constantPool, BindingScope.ROOT_SCOPE, 0, templateTypeName, null, null, templateName, + constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, directiveMatcher, directivesUsed, meta.pipes, pipesUsed, R3.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds); @@ -252,7 +255,11 @@ export function compileComponentFromMetadata( } const expression = o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()]); - const type = createTypeForDef(meta, R3.ComponentDefWithMeta); + + + const typeParams = createDirectiveTypeParams(meta); + typeParams.push(stringArrayAsType(meta.template.ngContentSelectors)); + const type = o.expressionType(o.importExpr(R3.ComponentDefWithMeta, typeParams)); return {expression, type}; } @@ -267,7 +274,7 @@ export function compileComponentFromMetadata( export function compileDirectiveFromRender2( outputCtx: OutputContext, directive: CompileDirectiveMetadata, reflector: CompileReflector, bindingParser: BindingParser) { - const name = identifierName(directive.type) !; + const name = identifierName(directive.type)!; name || error(`Cannot resolver the name of ${directive.type}`); const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Directive); @@ -300,7 +307,7 @@ export function compileComponentFromRender2( outputCtx: OutputContext, component: CompileDirectiveMetadata, render3Ast: Render3ParseResult, reflector: CompileReflector, bindingParser: BindingParser, directiveTypeBySel: Map<string, any>, pipeTypeByName: Map<string, any>) { - const name = identifierName(component.type) !; + const name = identifierName(component.type)!; name || error(`Cannot resolver the name of ${component.type}`); const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Component); @@ -311,7 +318,7 @@ export function compileComponentFromRender2( const meta: R3ComponentMetadata = { ...directiveMetadataFromGlobalMetadata(component, outputCtx, reflector), selector: component.selector, - template: {nodes: render3Ast.nodes}, + template: {nodes: render3Ast.nodes, ngContentSelectors: render3Ast.ngContentSelectors}, directives: [], pipes: typeMapToExpressionMap(pipeTypeByName, outputCtx), viewQueries: queriesFromGlobalMetadata(component.viewQueries, outputCtx), @@ -366,7 +373,8 @@ function queriesFromGlobalMetadata( propertyName: query.propertyName, first: query.first, predicate: selectorsFromGlobalMetadata(query.selectors, outputCtx), - descendants: query.descendants, read, + descendants: query.descendants, + read, static: !!query.static }; }); @@ -458,7 +466,7 @@ function stringAsType(str: string): o.Type { return o.expressionType(o.literal(str)); } -function stringMapAsType(map: {[key: string]: string | string[]}): o.Type { +function stringMapAsType(map: {[key: string]: string|string[]}): o.Type { const mapValues = Object.keys(map).map(key => { const value = Array.isArray(map[key]) ? map[key][0] : map[key]; return { @@ -470,24 +478,24 @@ function stringMapAsType(map: {[key: string]: string | string[]}): o.Type { return o.expressionType(o.literalMap(mapValues)); } -function stringArrayAsType(arr: string[]): o.Type { +function stringArrayAsType(arr: ReadonlyArray<string|null>): o.Type { return arr.length > 0 ? o.expressionType(o.literalArr(arr.map(value => o.literal(value)))) : o.NONE_TYPE; } -function createTypeForDef(meta: R3DirectiveMetadata, typeBase: o.ExternalReference): o.Type { +function createDirectiveTypeParams(meta: R3DirectiveMetadata): o.Type[] { // On the type side, remove newlines from the selector as it will need to fit into a TypeScript // string literal, which must be on one line. const selectorForType = meta.selector !== null ? meta.selector.replace(/\n/g, '') : null; - return o.expressionType(o.importExpr(typeBase, [ + return [ typeWithParameters(meta.type.type, meta.typeArgumentCount), selectorForType !== null ? stringAsType(selectorForType) : o.NONE_TYPE, meta.exportAs !== null ? stringArrayAsType(meta.exportAs) : o.NONE_TYPE, stringMapAsType(meta.inputs), stringMapAsType(meta.outputs), stringArrayAsType(meta.queries.map(q => q.propertyName)), - ])); + ]; } // Define and update any view queries @@ -717,7 +725,7 @@ function convertStylingCall( function getBindingNameAndInstruction(binding: ParsedProperty): {bindingName: string, instruction: o.ExternalReference, isAttribute: boolean} { let bindingName = binding.name; - let instruction !: o.ExternalReference; + let instruction!: o.ExternalReference; // Check to see if this is an attr binding or a property binding const attrMatches = bindingName.match(ATTR_REGEX); @@ -811,8 +819,7 @@ export interface ParsedHostBindings { specialAttributes: {styleAttr?: string; classAttr?: string;}; } -export function parseHostBindings(host: {[key: string]: string | o.Expression}): - ParsedHostBindings { +export function parseHostBindings(host: {[key: string]: string|o.Expression}): ParsedHostBindings { const attributes: {[key: string]: o.Expression} = {}; const listeners: {[key: string]: string} = {}; const properties: {[key: string]: string} = {}; @@ -887,5 +894,7 @@ export function verifyHostBindings( function compileStyles(styles: string[], selector: string, hostSelector: string): string[] { const shadowCss = new ShadowCss(); - return styles.map(style => { return shadowCss !.shimCssText(style, selector, hostSelector); }); + return styles.map(style => { + return shadowCss!.shimCssText(style, selector, hostSelector); + }); } diff --git a/packages/compiler/src/render3/view/i18n/context.ts b/packages/compiler/src/render3/view/i18n/context.ts index 2151b19938589..7ca7b64d9f758 100644 --- a/packages/compiler/src/render3/view/i18n/context.ts +++ b/packages/compiler/src/render3/view/i18n/context.ts @@ -46,7 +46,7 @@ export class I18nContext { public placeholders = new Map<string, any[]>(); public isEmitted: boolean = false; - private _registry !: any; + private _registry!: any; private _unresolvedCtxCount: number = 0; constructor( @@ -66,9 +66,15 @@ export class I18nContext { updatePlaceholderMap(this.placeholders, ph, content); } - get icus() { return this._registry.icus; } - get isRoot() { return this.level === 0; } - get isResolved() { return this._unresolvedCtxCount === 0; } + get icus() { + return this._registry.icus; + } + get isRoot() { + return this.level === 0; + } + get isResolved() { + return this._unresolvedCtxCount === 0; + } getSerializedPlaceholders() { const result = new Map<string, any[]>(); @@ -78,7 +84,9 @@ export class I18nContext { } // public API to accumulate i18n-related content - appendBinding(binding: AST) { this.bindings.add(binding); } + appendBinding(binding: AST) { + this.bindings.add(binding); + } appendIcu(name: string, ref: o.Expression) { updatePlaceholderMap(this._registry.icus, name, ref); } @@ -181,7 +189,7 @@ function wrapTag(symbol: string, {index, ctx, isVoid}: any, closed?: boolean): s wrap(symbol, index, ctx, closed); } -function findTemplateFn(ctx: number, templateIndex: number | null) { +function findTemplateFn(ctx: number, templateIndex: number|null) { return (token: any) => typeof token === 'object' && token.type === TagType.TEMPLATE && token.index === templateIndex && token.ctx === ctx; } diff --git a/packages/compiler/src/render3/view/i18n/get_msg_utils.ts b/packages/compiler/src/render3/view/i18n/get_msg_utils.ts index e601904793492..d03fa7660a7bc 100644 --- a/packages/compiler/src/render3/view/i18n/get_msg_utils.ts +++ b/packages/compiler/src/render3/view/i18n/get_msg_utils.ts @@ -47,23 +47,32 @@ export function createGoogleGetMsgStatements( * placeholders in `{$placeholder}` (for plain messages) or `{PLACEHOLDER}` (inside ICUs) format. */ class GetMsgSerializerVisitor implements i18n.Visitor { - private formatPh(value: string): string { return `{$${formatI18nPlaceholderName(value)}}`; } + private formatPh(value: string): string { + return `{$${formatI18nPlaceholderName(value)}}`; + } - visitText(text: i18n.Text): any { return text.value; } + visitText(text: i18n.Text): any { + return text.value; + } visitContainer(container: i18n.Container): any { return container.children.map(child => child.visit(this)).join(''); } - visitIcu(icu: i18n.Icu): any { return serializeIcuNode(icu); } + visitIcu(icu: i18n.Icu): any { + return serializeIcuNode(icu); + } visitTagPlaceholder(ph: i18n.TagPlaceholder): any { return ph.isVoid ? this.formatPh(ph.startName) : - `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`; + `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${ + this.formatPh(ph.closeName)}`; } - visitPlaceholder(ph: i18n.Placeholder): any { return this.formatPh(ph.name); } + visitPlaceholder(ph: i18n.Placeholder): any { + return this.formatPh(ph.name); + } visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { return this.formatPh(ph.name); diff --git a/packages/compiler/src/render3/view/i18n/icu_serializer.ts b/packages/compiler/src/render3/view/i18n/icu_serializer.ts index 8e5aa0b7ffd66..c0f645bf3f8aa 100644 --- a/packages/compiler/src/render3/view/i18n/icu_serializer.ts +++ b/packages/compiler/src/render3/view/i18n/icu_serializer.ts @@ -11,7 +11,9 @@ import * as i18n from '../../../i18n/i18n_ast'; import {formatI18nPlaceholderName} from './util'; class IcuSerializerVisitor implements i18n.Visitor { - visitText(text: i18n.Text): any { return text.value; } + visitText(text: i18n.Text): any { + return text.value; + } visitContainer(container: i18n.Container): any { return container.children.map(child => child.visit(this)).join(''); @@ -27,10 +29,13 @@ class IcuSerializerVisitor implements i18n.Visitor { visitTagPlaceholder(ph: i18n.TagPlaceholder): any { return ph.isVoid ? this.formatPh(ph.startName) : - `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`; + `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${ + this.formatPh(ph.closeName)}`; } - visitPlaceholder(ph: i18n.Placeholder): any { return this.formatPh(ph.name); } + visitPlaceholder(ph: i18n.Placeholder): any { + return this.formatPh(ph.name); + } visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { return this.formatPh(ph.name); diff --git a/packages/compiler/src/render3/view/i18n/localize_utils.ts b/packages/compiler/src/render3/view/i18n/localize_utils.ts index 2e8dc4cdb6915..e3c79c28983e2 100644 --- a/packages/compiler/src/render3/view/i18n/localize_utils.ts +++ b/packages/compiler/src/render3/view/i18n/localize_utils.ts @@ -28,7 +28,9 @@ class MessagePiece { } class LiteralPiece extends MessagePiece {} class PlaceholderPiece extends MessagePiece { - constructor(name: string) { super(formatI18nPlaceholderName(name, /* useCamelCase */ false)); } + constructor(name: string) { + super(formatI18nPlaceholderName(name, /* useCamelCase */ false)); + } } /** diff --git a/packages/compiler/src/render3/view/i18n/meta.ts b/packages/compiler/src/render3/view/i18n/meta.ts index 6269e69e27673..d294b40258ce0 100644 --- a/packages/compiler/src/render3/view/i18n/meta.ts +++ b/packages/compiler/src/render3/view/i18n/meta.ts @@ -8,12 +8,12 @@ import {computeDecimalDigest, computeDigest, decimalDigest} from '../../../i18n/digest'; import * as i18n from '../../../i18n/i18n_ast'; -import {VisitNodeFn, createI18nMessageFactory} from '../../../i18n/i18n_parser'; +import {createI18nMessageFactory, VisitNodeFn} from '../../../i18n/i18n_parser'; import * as html from '../../../ml_parser/ast'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../ml_parser/interpolation_config'; import * as o from '../../../output/output_ast'; -import {I18N_ATTR, I18N_ATTR_PREFIX, hasI18nAttrs, icuFromI18nMessage} from './util'; +import {hasI18nAttrs, I18N_ATTR, I18N_ATTR_PREFIX, icuFromI18nMessage} from './util'; export type I18nMeta = { id?: string, @@ -134,10 +134,18 @@ export class I18nMetaVisitor implements html.Visitor { return expansion; } - visitText(text: html.Text): any { return text; } - visitAttribute(attribute: html.Attribute): any { return attribute; } - visitComment(comment: html.Comment): any { return comment; } - visitExpansionCase(expansionCase: html.ExpansionCase): any { return expansionCase; } + visitText(text: html.Text): any { + return text; + } + visitAttribute(attribute: html.Attribute): any { + return attribute; + } + visitComment(comment: html.Comment): any { + return comment; + } + visitExpansionCase(expansionCase: html.ExpansionCase): any { + return expansionCase; + } /** * Parse the general form `meta` passed into extract the explicit metadata needed to create a diff --git a/packages/compiler/src/render3/view/i18n/util.ts b/packages/compiler/src/render3/view/i18n/util.ts index baa0e2e9f7b9c..06cfd840d76a2 100644 --- a/packages/compiler/src/render3/view/i18n/util.ts +++ b/packages/compiler/src/render3/view/i18n/util.ts @@ -9,6 +9,7 @@ import * as i18n from '../../../i18n/i18n_ast'; import {toPublicName} from '../../../i18n/serializers/xmb'; import * as html from '../../../ml_parser/ast'; import * as o from '../../../output/output_ast'; +import * as t from '../../r3_ast'; /* Closure variables holding messages must be named `MSG_[A-Z0-9]+` */ const CLOSURE_TRANSLATION_PREFIX = 'MSG_'; @@ -41,6 +42,10 @@ export function isSingleI18nIcu(meta?: i18n.I18nMeta): boolean { return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof i18n.Icu; } +export function hasI18nMeta(node: t.Node&{i18n?: i18n.I18nMeta}): boolean { + return !!node.i18n; +} + export function hasI18nAttrs(element: html.Element): boolean { return element.attrs.some((attr: html.Attribute) => isI18nAttribute(attr.name)); } @@ -49,7 +54,7 @@ export function icuFromI18nMessage(message: i18n.Message) { return message.nodes[0] as i18n.IcuPlaceholder; } -export function wrapI18nPlaceholder(content: string | number, contextId: number = 0): string { +export function wrapI18nPlaceholder(content: string|number, contextId: number = 0): string { const blockId = contextId > 0 ? `:${contextId}` : ''; return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`; } @@ -147,7 +152,7 @@ export function formatI18nPlaceholderName(name: string, useCamelCase: boolean = if (/^\d+$/.test(chunks[chunks.length - 1])) { postfix = chunks.pop(); } - let raw = chunks.shift() !.toLowerCase(); + let raw = chunks.shift()!.toLowerCase(); if (chunks.length) { raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join(''); } @@ -170,5 +175,5 @@ export function getTranslationConstPrefix(extra: string): string { */ export function declareI18nVariable(variable: o.ReadVarExpr): o.Statement { return new o.DeclareVarStmt( - variable.name !, undefined, o.INFERRED_TYPE, null, variable.sourceSpan); + variable.name!, undefined, o.INFERRED_TYPE, null, variable.sourceSpan); } diff --git a/packages/compiler/src/render3/view/style_parser.ts b/packages/compiler/src/render3/view/style_parser.ts index f0de8e362ba6c..668df7d27e3df 100644 --- a/packages/compiler/src/render3/view/style_parser.ts +++ b/packages/compiler/src/render3/view/style_parser.ts @@ -110,7 +110,11 @@ export function stripUnnecessaryQuotes(value: string): string { } export function hyphenate(value: string): string { - return value.replace(/[a-z][A-Z]/g, v => { - return v.charAt(0) + '-' + v.charAt(1); - }).toLowerCase(); + return value + .replace( + /[a-z][A-Z]/g, + v => { + return v.charAt(0) + '-' + v.charAt(1); + }) + .toLowerCase(); } diff --git a/packages/compiler/src/render3/view/styling_builder.ts b/packages/compiler/src/render3/view/styling_builder.ts index 2ac2d6114ee93..6860d3e543344 100644 --- a/packages/compiler/src/render3/view/styling_builder.ts +++ b/packages/compiler/src/render3/view/styling_builder.ts @@ -226,7 +226,10 @@ export class StylingBuilder { const entry: BoundStylingEntry = { name: property, sanitize: property ? isStyleSanitizable(property) : true, - unit: unit || bindingUnit, value, sourceSpan, hasOverrideFlag + unit: unit || bindingUnit, + value, + sourceSpan, + hasOverrideFlag }; if (isMapBased) { this._styleMapInput = entry; @@ -385,7 +388,7 @@ export class StylingBuilder { supportsInterpolation: true, sourceSpan: stylingInput.sourceSpan, allocateBindingSlots: totalBindingSlotsRequired, - params: (convertFn: (value: any) => o.Expression | o.Expression[]) => { + params: (convertFn: (value: any) => o.Expression|o.Expression[]) => { const convertResult = convertFn(mapValue); const params = Array.isArray(convertResult) ? convertResult : [convertResult]; return params; diff --git a/packages/compiler/src/render3/view/t2_api.ts b/packages/compiler/src/render3/view/t2_api.ts index ed651075b89a4..c0d2de5b2cf17 100644 --- a/packages/compiler/src/render3/view/t2_api.ts +++ b/packages/compiler/src/render3/view/t2_api.ts @@ -22,7 +22,9 @@ import {BoundAttribute, BoundEvent, Element, Node, Reference, Template, TextAttr /** * A logical target for analysis, which could contain a template or other types of bindings. */ -export interface Target { template?: Node[]; } +export interface Target { + template?: Node[]; +} /** * Metadata regarding a directive that's needed to match it against template elements. This is @@ -44,7 +46,7 @@ export interface DirectiveMeta { * * Goes from property names to field names. */ - inputs: {[property: string]: string | [string, string]}; + inputs: {[property: string]: string|[string, string]}; /** * Set of outputs which this directive claims. @@ -67,7 +69,9 @@ export interface DirectiveMeta { * * The returned `BoundTarget` has an API for extracting information about the processed target. */ -export interface TargetBinder<D extends DirectiveMeta> { bind(target: Target): BoundTarget<D>; } +export interface TargetBinder<D extends DirectiveMeta> { + bind(target: Target): BoundTarget<D>; +} /** * Result of performing the binding operation against a `Target`. diff --git a/packages/compiler/src/render3/view/t2_binder.ts b/packages/compiler/src/render3/view/t2_binder.ts index e56270c972ce5..2f6eba2ce6005 100644 --- a/packages/compiler/src/render3/view/t2_binder.ts +++ b/packages/compiler/src/render3/view/t2_binder.ts @@ -152,7 +152,7 @@ class Scope implements Visitor { lookup(name: string): Reference|Variable|null { if (this.namedEntities.has(name)) { // Found in the local scope. - return this.namedEntities.get(name) !; + return this.namedEntities.get(name)!; } else if (this.parentScope !== undefined) { // Not in the local scope, but there's a parent scope so check there. return this.parentScope.lookup(name); @@ -217,11 +217,17 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor { return {directives, bindings, references}; } - private ingest(template: Node[]): void { template.forEach(node => node.visit(this)); } + private ingest(template: Node[]): void { + template.forEach(node => node.visit(this)); + } - visitElement(element: Element): void { this.visitElementOrTemplate(element.name, element); } + visitElement(element: Element): void { + this.visitElementOrTemplate(element.name, element); + } - visitTemplate(template: Template): void { this.visitElementOrTemplate('ng-template', template); } + visitTemplate(template: Template): void { + this.visitElementOrTemplate('ng-template', template); + } visitElementOrTemplate(elementName: string, node: Element|Template): void { // First, determine the HTML shape of the node for the purpose of directive matching. @@ -269,7 +275,7 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor { }); // Associate attributes/bindings on the node with directives or with the node itself. - type BoundNode = BoundAttribute | BoundEvent | TextAttribute; + type BoundNode = BoundAttribute|BoundEvent|TextAttribute; const setAttributeBinding = (attribute: BoundNode, ioType: keyof Pick<DirectiveMeta, 'inputs'|'outputs'>) => { const dir = directives.find(dir => dir[ioType].hasOwnProperty(attribute.name)); @@ -432,11 +438,17 @@ class TemplateBinder extends RecursiveAstVisitor implements Visitor { // The remaining visitors are concerned with processing AST expressions within template bindings - visitBoundAttribute(attribute: BoundAttribute) { attribute.value.visit(this); } + visitBoundAttribute(attribute: BoundAttribute) { + attribute.value.visit(this); + } - visitBoundEvent(event: BoundEvent) { event.handler.visit(this); } + visitBoundEvent(event: BoundEvent) { + event.handler.visit(this); + } - visitBoundText(text: BoundText) { text.value.visit(this); } + visitBoundText(text: BoundText) { + text.value.visit(this); + } visitPipe(ast: BindingPipe, context: any): any { this.usedPipes.add(ast.name); return super.visitPipe(ast, context); @@ -526,7 +538,9 @@ export class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTar return this.symbols.get(symbol) || null; } - getNestingLevel(template: Template): number { return this.nestingLevel.get(template) || 0; } + getNestingLevel(template: Template): number { + return this.nestingLevel.get(template) || 0; + } getUsedDirectives(): DirectiveT[] { const set = new Set<DirectiveT>(); @@ -534,5 +548,7 @@ export class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTar return Array.from(set.values()); } - getUsedPipes(): string[] { return Array.from(this.usedPipes); } + getUsedPipes(): string[] { + return Array.from(this.usedPipes); + } } diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index 18ad2a784d03f..0b477ef57d32d 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -7,7 +7,7 @@ */ import {flatten, sanitizeIdentifier} from '../../compile_metadata'; -import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding, convertUpdateArguments} from '../../compiler_util/expression_converter'; +import {BindingForm, BuiltinFunctionCall, convertActionBinding, convertPropertyBinding, convertUpdateArguments, LocalResolver} from '../../compiler_util/expression_converter'; import {ConstantPool} from '../../constant_pool'; import * as core from '../../core'; import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast'; @@ -26,7 +26,7 @@ import {ParseError, ParseSourceSpan} from '../../parse_util'; import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry'; import {CssSelector, SelectorMatcher} from '../../selector'; import {BindingParser} from '../../template_parser/binding_parser'; -import {error} from '../../util'; +import {error, partitionArray} from '../../util'; import * as t from '../r3_ast'; import {Identifiers as R3} from '../r3_identifiers'; import {htmlAstToRender3Ast} from '../r3_template_transform'; @@ -36,9 +36,9 @@ import {I18nContext} from './i18n/context'; import {createGoogleGetMsgStatements} from './i18n/get_msg_utils'; import {createLocalizeStatements} from './i18n/localize_utils'; import {I18nMetaVisitor} from './i18n/meta'; -import {I18N_ICU_MAPPING_PREFIX, TRANSLATION_PREFIX, assembleBoundTextPlaceholders, assembleI18nBoundString, declareI18nVariable, getTranslationConstPrefix, i18nFormatPlaceholderNames, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, placeholdersToParams, wrapI18nPlaceholder} from './i18n/util'; +import {assembleBoundTextPlaceholders, assembleI18nBoundString, declareI18nVariable, getTranslationConstPrefix, hasI18nMeta, I18N_ICU_MAPPING_PREFIX, i18nFormatPlaceholderNames, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, placeholdersToParams, TRANSLATION_PREFIX, wrapI18nPlaceholder} from './i18n/util'; import {StylingBuilder, StylingInstruction} from './styling_builder'; -import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, chainedInstruction, getAttrsForDirectiveMatching, getInterpolationArgsLength, invalid, trimTrailingNulls, unsupported} from './util'; +import {asLiteral, chainedInstruction, CONTEXT_NAME, getAttrsForDirectiveMatching, getInterpolationArgsLength, IMPLICIT_REFERENCE, invalid, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, trimTrailingNulls, unsupported} from './util'; @@ -61,8 +61,8 @@ export function renderFlagCheckIfStmt( } export function prepareEventListenerParameters( - eventAst: t.BoundEvent, handlerName: string | null = null, - scope: BindingScope | null = null): o.Expression[] { + eventAst: t.BoundEvent, handlerName: string|null = null, + scope: BindingScope|null = null): o.Expression[] { const {type, name, target, phase, handler} = eventAst; if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) { throw new Error(`Unexpected global target '${target}' defined for '${name}' event. @@ -85,7 +85,7 @@ export function prepareEventListenerParameters( statements.push(...bindingExpr.render3Stmts); const eventName: string = - type === ParsedEventType.Animation ? prepareSyntheticListenerName(name, phase !) : name; + type === ParsedEventType.Animation ? prepareSyntheticListenerName(name, phase!) : name; const fnName = handlerName && sanitizeIdentifier(handlerName); const fnArgs: o.FnParam[] = []; @@ -98,7 +98,7 @@ export function prepareEventListenerParameters( if (target) { params.push( o.literal(false), // `useCapture` flag, defaults to `false` - o.importExpr(GLOBAL_TARGET_RESOLVERS.get(target) !)); + o.importExpr(GLOBAL_TARGET_RESOLVERS.get(target)!)); } return params; } @@ -207,12 +207,12 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver // - this template has parent i18n context // - or the template has i18n meta associated with it, // but it's not initiated by the Element (e.g. <ng-template i18n>) - const initI18nContext = - this.i18nContext || (isI18nRootNode(i18n) && !isSingleI18nIcu(i18n) && - !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n)); + const initI18nContext = this.i18nContext || + (isI18nRootNode(i18n) && !isSingleI18nIcu(i18n) && + !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n)); const selfClosingI18nInstruction = hasTextChildrenOnly(nodes); if (initI18nContext) { - this.i18nStart(null, i18n !, selfClosingI18nInstruction); + this.i18nStart(null, i18n!, selfClosingI18nInstruction); } // This is the initial pass through the nodes of this template. In this pass, we @@ -295,10 +295,14 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver } // LocalResolver - getLocal(name: string): o.Expression|null { return this._bindingScope.get(name); } + getLocal(name: string): o.Expression|null { + return this._bindingScope.get(name); + } // LocalResolver - notifyImplicitReceiverUse(): void { this._bindingScope.notifyImplicitReceiverUse(); } + notifyImplicitReceiverUse(): void { + this._bindingScope.notifyImplicitReceiverUse(); + } private i18nTranslate( message: i18n.Message, params: {[name: string]: o.Expression} = {}, ref?: o.ReadVarExpr, @@ -335,12 +339,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver private i18nAppendBindings(expressions: AST[]) { if (expressions.length > 0) { - expressions.forEach(expression => this.i18n !.appendBinding(expression)); + expressions.forEach(expression => this.i18n!.appendBinding(expression)); } } - private i18nBindProps(props: {[key: string]: t.Text | t.BoundText}): - {[key: string]: o.Expression} { + private i18nBindProps(props: {[key: string]: t.Text|t.BoundText}): {[key: string]: o.Expression} { const bound: {[key: string]: o.Expression} = {}; Object.keys(props).forEach(key => { const prop = props[key]; @@ -351,7 +354,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver this.allocateBindingSlots(value); if (value instanceof Interpolation) { const {strings, expressions} = value; - const {id, bindings} = this.i18n !; + const {id, bindings} = this.i18n!; const label = assembleI18nBoundString(strings, bindings.size, id); this.i18nAppendBindings(expressions); bound[key] = o.literal(label); @@ -424,7 +427,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver void { const index = this.allocateDataSlot(); if (this.i18nContext) { - this.i18n = this.i18nContext.forkChildContext(index, this.templateIndex !, meta); + this.i18n = this.i18nContext.forkChildContext(index, this.templateIndex!, meta); } else { const ref = o.variable(this.constantPool.uniqueName(TRANSLATION_PREFIX)); this.i18n = new I18nContext(index, ref, 0, this.templateIndex, meta); @@ -479,7 +482,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver const i18nAttrArgs: o.Expression[] = []; const bindings: ChainableBindingInstruction[] = []; attrs.forEach(attr => { - const message = attr.i18n !as i18n.Message; + const message = attr.i18n! as i18n.Message; if (attr instanceof t.TextAttribute) { i18nAttrArgs.push(o.literal(attr.name), this.i18nTranslate(message)); } else { @@ -559,7 +562,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver this.creationInstruction(ngContent.sourceSpan, R3.projection, parameters); if (this.i18n) { - this.i18n.appendProjection(ngContent.i18n !, slot); + this.i18n.appendProjection(ngContent.i18n!, slot); } } @@ -571,7 +574,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver const isI18nRootElement: boolean = isI18nRootNode(element.i18n) && !isSingleI18nIcu(element.i18n); - const i18nAttrs: (t.TextAttribute | t.BoundAttribute)[] = []; + const i18nAttrs: (t.TextAttribute|t.BoundAttribute)[] = []; const outputAttrs: t.TextAttribute[] = []; const [namespaceKey, elementName] = splitNsName(element.name); @@ -633,7 +636,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver } if (this.i18n) { - this.i18n.appendElement(element.i18n !, elementIndex); + this.i18n.appendElement(element.i18n!, elementIndex); } // Note that we do not append text node instructions and ICUs inside i18n section, @@ -676,7 +679,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and // listeners, to make sure i18nAttributes instruction targets current element at runtime. if (isI18nRootElement) { - this.i18nStart(element.sourceSpan, element.i18n !, createSelfClosingI18nInstruction); + this.i18nStart(element.sourceSpan, element.i18n!, createSelfClosingI18nInstruction); } } @@ -757,7 +760,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver propertyBindings.push({ name: attrName, sourceSpan: input.sourceSpan, - value: () => this.convertPropertyBinding(value), params + value: () => this.convertPropertyBinding(value), + params }); } } else if (inputType === BindingType.Attribute) { @@ -773,7 +777,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver attributeBindings.push({ name: attrName, sourceSpan: input.sourceSpan, - value: () => this.convertPropertyBinding(boundValue), params + value: () => this.convertPropertyBinding(boundValue), + params }); } } else { @@ -801,7 +806,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver t.visitAll(this, element.children); if (!isI18nRootElement && this.i18n) { - this.i18n.appendElement(element.i18n !, elementIndex, true); + this.i18n.appendElement(element.i18n!, elementIndex, true); } if (!createSelfClosingInstruction) { @@ -823,7 +828,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver const templateIndex = this.allocateDataSlot(); if (this.i18n) { - this.i18n.appendTemplate(template.i18n !, templateIndex); + this.i18n.appendTemplate(template.i18n!, templateIndex); } const tagName = sanitizeIdentifier(template.tagName || ''); @@ -843,11 +848,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver this.matchDirectives(NG_TEMPLATE_TAG_NAME, template); // prepare attributes parameter (including attributes used for directive matching) - // TODO (FW-1942): exclude i18n attributes from the main attribute list and pass them - // as an `i18nAttrs` argument of the `getAttributeExpressions` function below. + const [i18nStaticAttrs, staticAttrs] = partitionArray(template.attributes, hasI18nMeta); const attrsExprs: o.Expression[] = this.getAttributeExpressions( - template.attributes, template.inputs, template.outputs, undefined, template.templateAttrs, - undefined); + staticAttrs, template.inputs, template.outputs, undefined /* styles */, + template.templateAttrs, i18nStaticAttrs); parameters.push(this.addAttrsToConsts(attrsExprs)); // local refs (ex.: <ng-template #foo>) @@ -891,12 +895,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver // Only add normal input/output binding instructions on explicit <ng-template> elements. if (template.tagName === NG_TEMPLATE_TAG_NAME) { - const inputs: t.BoundAttribute[] = []; - const i18nAttrs: (t.TextAttribute | t.BoundAttribute)[] = - template.attributes.filter(attr => !!attr.i18n); - - template.inputs.forEach( - (input: t.BoundAttribute) => (input.i18n ? i18nAttrs : inputs).push(input)); + const [i18nInputs, inputs] = partitionArray(template.inputs, hasI18nMeta); + const i18nAttrs = [...i18nStaticAttrs, ...i18nInputs]; // Add i18n attributes that may act as inputs to directives. If such attributes are present, // generate `i18nAttributes` instruction. Note: we generate it only for explicit <ng-template> @@ -935,7 +935,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver const value = text.value.visit(this._valueConverter); this.allocateBindingSlots(value); if (value instanceof Interpolation) { - this.i18n.appendBoundText(text.i18n !); + this.i18n.appendBoundText(text.i18n!); this.i18nAppendBindings(value.expressions); } return; @@ -975,15 +975,15 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver // to generate i18n context and the necessary instructions if (!this.i18n) { initWasInvoked = true; - this.i18nStart(null, icu.i18n !, true); + this.i18nStart(null, icu.i18n!, true); } - const i18n = this.i18n !; + const i18n = this.i18n!; const vars = this.i18nBindProps(icu.vars); const placeholders = this.i18nBindProps(icu.placeholders); // output ICU directly and keep ICU reference in context - const message = icu.i18n !as i18n.Message; + const message = icu.i18n! as i18n.Message; // we always need post-processing function for ICUs, to make sure that: // - all placeholders in a form of {PLACEHOLDER} are replaced with actual values (note: @@ -1016,13 +1016,21 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver return null; } - private allocateDataSlot() { return this._dataIndex++; } + private allocateDataSlot() { + return this._dataIndex++; + } - getConstCount() { return this._dataIndex; } + getConstCount() { + return this._dataIndex; + } - getVarCount() { return this._pureFunctionSlots; } + getVarCount() { + return this._pureFunctionSlots; + } - getConsts() { return this._constants; } + getConsts() { + return this._constants; + } getNgContentSelectors(): o.Expression|null { return this._ngContentReservedSlots.length ? @@ -1030,7 +1038,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver null; } - private bindingContext() { return `${this._bindingContext++}`; } + private bindingContext() { + return `${this._bindingContext++}`; + } private templatePropertyBindings( templateIndex: number, attrs: (t.BoundAttribute|t.TextAttribute)[]) { @@ -1092,11 +1102,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver calls.push({ sourceSpan: call.sourceSpan, value: () => { - return call - .params( - value => (call.supportsInterpolation && value instanceof Interpolation) ? - this.getUpdateInstructionArguments(value) : - this.convertPropertyBinding(value)) as o.Expression[]; + return call.params( + value => (call.supportsInterpolation && value instanceof Interpolation) ? + this.getUpdateInstructionArguments(value) : + this.convertPropertyBinding(value)) as o.Expression[]; } }); }); @@ -1114,7 +1123,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver } private creationInstructionChain(reference: o.ExternalReference, calls: { - sourceSpan: ParseSourceSpan | null, + sourceSpan: ParseSourceSpan|null, params: () => o.Expression[] }[]) { const span = calls.length ? calls[0].sourceSpan : null; @@ -1228,8 +1237,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver private matchDirectives(elementName: string, elOrTpl: t.Element|t.Template) { if (this.directiveMatcher) { const selector = createCssSelector(elementName, getAttrsForDirectiveMatching(elOrTpl)); - this.directiveMatcher.match( - selector, (cssSelector, staticType) => { this.directives.add(staticType); }); + this.directiveMatcher.match(selector, (cssSelector, staticType) => { + this.directives.add(staticType); + }); } } @@ -1277,7 +1287,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver attrExprs.push(...getNgProjectAsLiteral(ngProjectAsAttr)); } - function addAttrExpr(key: string | number, value?: o.Expression): void { + function addAttrExpr(key: string|number, value?: o.Expression): void { if (typeof key === 'string') { if (!alreadySeen.has(key)) { attrExprs.push(...getAttributeNameLiterals(key)); @@ -1390,7 +1400,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver const eventName: string = outputAst.name; const bindingFnName = outputAst.type === ParsedEventType.Animation ? // synthetic @listener.foo values are treated the exact same as are standard listeners - prepareSyntheticListenerFunctionName(eventName, outputAst.phase !) : + prepareSyntheticListenerFunctionName(eventName, outputAst.phase!) : sanitizeIdentifier(eventName); const handlerName = `${this.templateName}_${tagName}_${bindingFnName}_${index}_listener`; const scope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel); @@ -1492,7 +1502,7 @@ function pureFunctionCallInfo(args: o.Expression[]) { } function instruction( - span: ParseSourceSpan | null, reference: o.ExternalReference, + span: ParseSourceSpan|null, reference: o.ExternalReference, params: o.Expression[]): o.Expression { return o.importExpr(reference, null, span).callFn(params, span); } @@ -1504,7 +1514,7 @@ function generateNextContextExpr(relativeLevelDiff: number): o.Expression { } function getLiteralFactory( - constantPool: ConstantPool, literal: o.LiteralArrayExpr | o.LiteralMapExpr, + constantPool: ConstantPool, literal: o.LiteralArrayExpr|o.LiteralMapExpr, allocateSlots: (numSlots: number) => number): o.Expression { const {literalFactory, literalFactoryArguments} = constantPool.getLiteralFactory(literal); // Allocate 1 slot for the result plus 1 per argument @@ -1570,9 +1580,8 @@ const SHARED_CONTEXT_KEY = '$$shared_ctx$$'; * declaration should always come before the local ref declaration. */ type BindingData = { - retrievalLevel: number; lhs: o.Expression; declareLocalCallback?: DeclareLocalVarCallback; - declare: boolean; - priority: number; + retrievalLevel: number; lhs: o.Expression; + declareLocalCallback?: DeclareLocalVarCallback; declare: boolean; priority: number; localRef: boolean; }; @@ -1580,20 +1589,19 @@ type BindingData = { * The sorting priority of a local variable declaration. Higher numbers * mean the declaration will appear first in the generated code. */ -const enum DeclarationPriority { DEFAULT = 0, CONTEXT = 1, SHARED_CONTEXT = 2 } +const enum DeclarationPriority { + DEFAULT = 0, + CONTEXT = 1, + SHARED_CONTEXT = 2 +} export class BindingScope implements LocalResolver { /** Keeps a map from local variables to their BindingData. */ private map = new Map<string, BindingData>(); private referenceNameIndex = 0; private restoreViewVariable: o.ReadVarExpr|null = null; - private static _ROOT_SCOPE: BindingScope; - - static get ROOT_SCOPE(): BindingScope { - if (!BindingScope._ROOT_SCOPE) { - BindingScope._ROOT_SCOPE = new BindingScope().set(0, '$event', o.variable('$event')); - } - return BindingScope._ROOT_SCOPE; + static createRootScope(): BindingScope { + return new BindingScope().set(0, '$event', o.variable('$event')); } private constructor(public bindingLevel: number = 0, private parent: BindingScope|null = null) {} @@ -1669,7 +1677,9 @@ export class BindingScope implements LocalResolver { } // Implemented as part of LocalResolver. - getLocal(name: string): (o.Expression|null) { return this.get(name); } + getLocal(name: string): (o.Expression|null) { + return this.get(name); + } // Implemented as part of LocalResolver. notifyImplicitReceiverUse(): void { @@ -1677,7 +1687,7 @@ export class BindingScope implements LocalResolver { // Since the implicit receiver is accessed in an embedded view, we need to // ensure that we declare a shared context variable for the current template // in the update variables. - this.map.get(SHARED_CONTEXT_KEY + 0) !.declare = true; + this.map.get(SHARED_CONTEXT_KEY + 0)!.declare = true; } } @@ -1698,7 +1708,7 @@ export class BindingScope implements LocalResolver { this.generateSharedContextVar(retrievalLevel); } // Shared context variables are always generated as "ReadVarExpr". - return this.map.get(bindingKey) !.lhs as o.ReadVarExpr; + return this.map.get(bindingKey)!.lhs as o.ReadVarExpr; } getSharedContextName(retrievalLevel: number): o.ReadVarExpr|null { @@ -1735,7 +1745,7 @@ export class BindingScope implements LocalResolver { } getComponentProperty(name: string): o.Expression { - const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0) !; + const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0)!; componentValue.declare = true; this.maybeRestoreView(0, false); return componentValue.lhs.prop(name); @@ -1748,11 +1758,11 @@ export class BindingScope implements LocalResolver { // 2 - we are looking up a local ref, which requires restoring the view where the local // ref is stored if (this.isListenerScope() && (retrievalLevel < this.bindingLevel || localRefLookup)) { - if (!this.parent !.restoreViewVariable) { + if (!this.parent!.restoreViewVariable) { // parent saves variable to generate a shared `const $s$ = getCurrentView();` instruction - this.parent !.restoreViewVariable = o.variable(this.parent !.freshReferenceName()); + this.parent!.restoreViewVariable = o.variable(this.parent!.freshReferenceName()); } - this.restoreViewVariable = this.parent !.restoreViewVariable; + this.restoreViewVariable = this.parent!.restoreViewVariable; } } @@ -1771,19 +1781,22 @@ export class BindingScope implements LocalResolver { []; } - isListenerScope() { return this.parent && this.parent.bindingLevel === this.bindingLevel; } + isListenerScope() { + return this.parent && this.parent.bindingLevel === this.bindingLevel; + } variableDeclarations(): o.Statement[] { let currentContextLevel = 0; return Array.from(this.map.values()) - .filter(value => value.declare) - .sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority) - .reduce((stmts: o.Statement[], value: BindingData) => { - const levelDiff = this.bindingLevel - value.retrievalLevel; - const currStmts = value.declareLocalCallback !(this, levelDiff - currentContextLevel); - currentContextLevel = levelDiff; - return stmts.concat(currStmts); - }, []) as o.Statement[]; + .filter(value => value.declare) + .sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority) + .reduce((stmts: o.Statement[], value: BindingData) => { + const levelDiff = this.bindingLevel - value.retrievalLevel; + const currStmts = + value.declareLocalCallback!(this, levelDiff - currentContextLevel); + currentContextLevel = levelDiff; + return stmts.concat(currStmts); + }, []) as o.Statement[]; } @@ -1983,8 +1996,13 @@ export interface ParseTemplateOptions { * @param options options to modify how the template is parsed */ export function parseTemplate( - template: string, templateUrl: string, options: ParseTemplateOptions = {}): - {errors?: ParseError[], nodes: t.Node[], styleUrls: string[], styles: string[]} { + template: string, templateUrl: string, options: ParseTemplateOptions = {}): { + errors?: ParseError[], + nodes: t.Node[], + styleUrls: string[], + styles: string[], + ngContentSelectors: string[] +} { const {interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat} = options; const bindingParser = makeBindingParser(interpolationConfig); const htmlParser = new HtmlParser(); @@ -1993,7 +2011,13 @@ export function parseTemplate( {leadingTriviaChars: LEADING_TRIVIA_CHARS, ...options, tokenizeExpansionForms: true}); if (parseResult.errors && parseResult.errors.length > 0) { - return {errors: parseResult.errors, nodes: [], styleUrls: [], styles: []}; + return { + errors: parseResult.errors, + nodes: [], + styleUrls: [], + styles: [], + ngContentSelectors: [] + }; } let rootNodes: html.Node[] = parseResult.rootNodes; @@ -2020,12 +2044,13 @@ export function parseTemplate( } } - const {nodes, errors, styleUrls, styles} = htmlAstToRender3Ast(rootNodes, bindingParser); + const {nodes, errors, styleUrls, styles, ngContentSelectors} = + htmlAstToRender3Ast(rootNodes, bindingParser); if (errors && errors.length > 0) { - return {errors, nodes: [], styleUrls: [], styles: []}; + return {errors, nodes: [], styleUrls: [], styles: [], ngContentSelectors: []}; } - return {nodes, styleUrls, styles}; + return {nodes, styleUrls, styles, ngContentSelectors}; } const elementRegistry = new DomElementSchemaRegistry(); @@ -2114,9 +2139,10 @@ export function getTranslationDeclStmts( const statements: o.Statement[] = [ declareI18nVariable(variable), o.ifStmt( - createClosureModeGuard(), createGoogleGetMsgStatements( - variable, message, closureVar, - i18nFormatPlaceholderNames(params, /* useCamelCase */ true)), + createClosureModeGuard(), + createGoogleGetMsgStatements( + variable, message, closureVar, + i18nFormatPlaceholderNames(params, /* useCamelCase */ true)), createLocalizeStatements( variable, message, i18nFormatPlaceholderNames(params, /* useCamelCase */ false))), ]; diff --git a/packages/compiler/src/render3/view/util.ts b/packages/compiler/src/render3/view/util.ts index 84b9d323bcb01..c762194774404 100644 --- a/packages/compiler/src/render3/view/util.ts +++ b/packages/compiler/src/render3/view/util.ts @@ -69,7 +69,7 @@ export function unsupported(this: void|Function, feature: string): never { throw new Error(`Feature ${feature} is not supported yet`); } -export function invalid<T>(this: t.Visitor, arg: o.Expression | o.Statement | t.Node): never { +export function invalid<T>(this: t.Visitor, arg: o.Expression|o.Statement|t.Node): never { throw new Error( `Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`); } @@ -82,7 +82,7 @@ export function asLiteral(value: any): o.Expression { } export function conditionallyCreateMapObjectLiteral( - keys: {[key: string]: string | string[]}, keepDeclared?: boolean): o.Expression|null { + keys: {[key: string]: string|string[]}, keepDeclared?: boolean): o.Expression|null { if (Object.getOwnPropertyNames(keys).length > 0) { return mapToExpression(keys, keepDeclared); } @@ -90,7 +90,7 @@ export function conditionallyCreateMapObjectLiteral( } function mapToExpression( - map: {[key: string]: string | string[]}, keepDeclared?: boolean): o.Expression { + map: {[key: string]: string|string[]}, keepDeclared?: boolean): o.Expression { return o.literalMap(Object.getOwnPropertyNames(map).map(key => { // canonical syntax: `dirProp: publicProp` // if there is no `:`, use dirProp = elProp @@ -153,7 +153,9 @@ export class DefinitionMap { } } - toLiteralMap(): o.LiteralMapExpr { return o.literalMap(this.values); } + toLiteralMap(): o.LiteralMapExpr { + return o.literalMap(this.values); + } } /** @@ -165,8 +167,8 @@ export class DefinitionMap { * object maps a property name to its (static) value. For any bindings, this map simply maps the * property name to an empty string. */ -export function getAttrsForDirectiveMatching(elOrTpl: t.Element | t.Template): - {[name: string]: string} { +export function getAttrsForDirectiveMatching(elOrTpl: t.Element| + t.Template): {[name: string]: string} { const attributesMap: {[name: string]: string} = {}; @@ -179,8 +181,12 @@ export function getAttrsForDirectiveMatching(elOrTpl: t.Element | t.Template): } }); - elOrTpl.inputs.forEach(i => { attributesMap[i.name] = ''; }); - elOrTpl.outputs.forEach(o => { attributesMap[o.name] = ''; }); + elOrTpl.inputs.forEach(i => { + attributesMap[i.name] = ''; + }); + elOrTpl.outputs.forEach(o => { + attributesMap[o.name] = ''; + }); } return attributesMap; @@ -188,7 +194,7 @@ export function getAttrsForDirectiveMatching(elOrTpl: t.Element | t.Template): /** Returns a call expression to a chained instruction, e.g. `property(params[0])(params[1])`. */ export function chainedInstruction( - reference: o.ExternalReference, calls: o.Expression[][], span?: ParseSourceSpan | null) { + reference: o.ExternalReference, calls: o.Expression[][], span?: ParseSourceSpan|null) { let expression = o.importExpr(reference, null, span) as o.Expression; if (calls.length > 0) { diff --git a/packages/compiler/src/resource_loader.ts b/packages/compiler/src/resource_loader.ts index a0237cd17bb03..1f59cc981e0c9 100644 --- a/packages/compiler/src/resource_loader.ts +++ b/packages/compiler/src/resource_loader.ts @@ -11,5 +11,7 @@ * to load templates. */ export class ResourceLoader { - get(url: string): Promise<string>|string { return ''; } + get(url: string): Promise<string>|string { + return ''; + } } diff --git a/packages/compiler/src/schema/dom_element_schema_registry.ts b/packages/compiler/src/schema/dom_element_schema_registry.ts index e74e90ae4f0db..8705c219573fd 100644 --- a/packages/compiler/src/schema/dom_element_schema_registry.ts +++ b/packages/compiler/src/schema/dom_element_schema_registry.ts @@ -253,7 +253,9 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry { typeNames.split(',').forEach(tag => this._schema[tag.toLowerCase()] = type); const superType = superName && this._schema[superName.toLowerCase()]; if (superType) { - Object.keys(superType).forEach((prop: string) => { type[prop] = superType[prop]; }); + Object.keys(superType).forEach((prop: string) => { + type[prop] = superType[prop]; + }); } properties.forEach((property: string) => { if (property.length > 0) { @@ -350,9 +352,13 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry { return ctx ? ctx : SecurityContext.NONE; } - getMappedPropName(propName: string): string { return _ATTR_TO_PROP[propName] || propName; } + getMappedPropName(propName: string): string { + return _ATTR_TO_PROP[propName] || propName; + } - getDefaultComponentElementName(): string { return 'ng-component'; } + getDefaultComponentElementName(): string { + return 'ng-component'; + } validateProperty(name: string): {error: boolean, msg?: string} { if (name.toLowerCase().startsWith('on')) { @@ -376,7 +382,9 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry { } } - allKnownElementNames(): string[] { return Object.keys(this._schema); } + allKnownElementNames(): string[] { + return Object.keys(this._schema); + } normalizeAnimationStyleProperty(propName: string): string { return dashCaseToCamelCase(propName); @@ -386,7 +394,7 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry { {error: string, value: string} { let unit: string = ''; const strVal = val.toString().trim(); - let errorMsg: string = null !; + let errorMsg: string = null!; if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') { if (typeof val === 'number') { diff --git a/packages/compiler/src/schema/dom_security_schema.ts b/packages/compiler/src/schema/dom_security_schema.ts index 73bff2233e38b..7768f29f194ce 100644 --- a/packages/compiler/src/schema/dom_security_schema.ts +++ b/packages/compiler/src/schema/dom_security_schema.ts @@ -20,7 +20,7 @@ import {SecurityContext} from '../core'; // ================================================================================================= /** Map from tagName|propertyName SecurityContext. Properties applying to all tags use '*'. */ -let _SECURITY_SCHEMA !: {[k: string]: SecurityContext}; +let _SECURITY_SCHEMA!: {[k: string]: SecurityContext}; export function SECURITY_SCHEMA(): {[k: string]: SecurityContext} { if (!_SECURITY_SCHEMA) { diff --git a/packages/compiler/src/selector.ts b/packages/compiler/src/selector.ts index b5e131de5df5c..a2d4aaaff66ca 100644 --- a/packages/compiler/src/selector.ts +++ b/packages/compiler/src/selector.ts @@ -118,9 +118,13 @@ export class CssSelector { this.notSelectors.length === 0; } - hasElementSelector(): boolean { return !!this.element; } + hasElementSelector(): boolean { + return !!this.element; + } - setElement(element: string|null = null) { this.element = element; } + setElement(element: string|null = null) { + this.element = element; + } /** Gets a template string for an element that matches the selector. */ getMatchingElementTemplate(): string { @@ -150,7 +154,9 @@ export class CssSelector { this.attrs.push(name, value && value.toLowerCase() || ''); } - addClassName(name: string) { this.classNames.push(name.toLowerCase()); } + addClassName(name: string) { + this.classNames.push(name.toLowerCase()); + } toString(): string { let res: string = this.element || ''; @@ -189,7 +195,7 @@ export class SelectorMatcher<T = any> { private _listContexts: SelectorListContext[] = []; addSelectables(cssSelectors: CssSelector[], callbackCtxt?: T) { - let listContext: SelectorListContext = null !; + let listContext: SelectorListContext = null!; if (cssSelectors.length > 1) { listContext = new SelectorListContext(cssSelectors); this._listContexts.push(listContext); @@ -284,10 +290,10 @@ export class SelectorMatcher<T = any> { * @param cssSelector A css selector * @param matchedCallback This callback will be called with the object handed into `addSelectable` * @return boolean true if a match was found - */ + */ match(cssSelector: CssSelector, matchedCallback: ((c: CssSelector, a: T) => void)|null): boolean { let result = false; - const element = cssSelector.element !; + const element = cssSelector.element!; const classNames = cssSelector.classNames; const attrs = cssSelector.attrs; @@ -315,7 +321,7 @@ export class SelectorMatcher<T = any> { const name = attrs[i]; const value = attrs[i + 1]; - const terminalValuesMap = this._attrValueMap.get(name) !; + const terminalValuesMap = this._attrValueMap.get(name)!; if (value) { result = this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result; @@ -323,7 +329,7 @@ export class SelectorMatcher<T = any> { result = this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result; - const partialValuesMap = this._attrValuePartialMap.get(name) !; + const partialValuesMap = this._attrValuePartialMap.get(name)!; if (value) { result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result; } @@ -343,7 +349,7 @@ export class SelectorMatcher<T = any> { } let selectables: SelectorContext<T>[] = map.get(name) || []; - const starSelectables: SelectorContext<T>[] = map.get('*') !; + const starSelectables: SelectorContext<T>[] = map.get('*')!; if (starSelectables) { selectables = selectables.concat(starSelectables); } diff --git a/packages/compiler/src/shadow_css.ts b/packages/compiler/src/shadow_css.ts index c65e6883bf016..0ce9bd570c626 100644 --- a/packages/compiler/src/shadow_css.ts +++ b/packages/compiler/src/shadow_css.ts @@ -138,13 +138,13 @@ export class ShadowCss { constructor() {} /* - * Shim some cssText with the given selector. Returns cssText that can - * be included in the document via WebComponents.ShadowCSS.addCssToDocument(css). - * - * When strictStyling is true: - * - selector is the attribute added to all elements inside the host, - * - hostSelector is the attribute added to the host itself. - */ + * Shim some cssText with the given selector. Returns cssText that can + * be included in the document via WebComponents.ShadowCSS.addCssToDocument(css). + * + * When strictStyling is true: + * - selector is the attribute added to all elements inside the host, + * - hostSelector is the attribute added to the host itself. + */ shimCssText(cssText: string, selector: string, hostSelector: string = ''): string { const commentsWithHash = extractCommentsWithHash(cssText); cssText = stripComments(cssText); @@ -172,11 +172,12 @@ export class ShadowCss { * * scopeName menu-item { * - **/ + **/ private _insertPolyfillDirectivesInCssText(cssText: string): string { // Difference with webcomponents.js: does not handle comments - return cssText.replace( - _cssContentNextSelectorRe, function(...m: string[]) { return m[2] + '{'; }); + return cssText.replace(_cssContentNextSelectorRe, function(...m: string[]) { + return m[2] + '{'; + }); } /* @@ -193,7 +194,7 @@ export class ShadowCss { * * scopeName menu-item {...} * - **/ + **/ private _insertPolyfillRulesInCssText(cssText: string): string { // Difference with webcomponents.js: does not handle comments return cssText.replace(_cssContentRuleRe, (...m: string[]) => { @@ -209,7 +210,7 @@ export class ShadowCss { * and converts this to * * scopeName .foo { ... } - */ + */ private _scopeCssText(cssText: string, scopeSelector: string, hostSelector: string): string { const unscopedRules = this._extractUnscopedRulesFromCssText(cssText); // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively @@ -238,7 +239,7 @@ export class ShadowCss { * * menu-item {...} * - **/ + **/ private _extractUnscopedRulesFromCssText(cssText: string): string { // Difference with webcomponents.js: does not handle comments let r = ''; @@ -257,7 +258,7 @@ export class ShadowCss { * to * * .foo<scopeName> > .bar - */ + */ private _convertColonHost(cssText: string): string { return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer); } @@ -276,7 +277,7 @@ export class ShadowCss { * to * * .foo<scopeName> .bar { ... } - */ + */ private _convertColonHostContext(cssText: string): string { return this._convertColonRule( cssText, _cssColonHostContextRe, this._colonHostContextPartReplacer); @@ -315,7 +316,7 @@ export class ShadowCss { /* * Convert combinators like ::shadow and pseudo-elements like ::content * by replacing with space. - */ + */ private _convertShadowDOMSelectors(cssText: string): string { return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText); } @@ -505,7 +506,9 @@ class SafeSelector { return content.replace(/__ph-(\d+)__/g, (ph, index) => this.placeholders[+index]); } - content(): string { return this._content; } + content(): string { + return this._content; + } } const _cssContentNextSelectorRe = diff --git a/packages/compiler/src/style_compiler.ts b/packages/compiler/src/style_compiler.ts index 751a7c3ca8090..e484d8b7468ef 100644 --- a/packages/compiler/src/style_compiler.ts +++ b/packages/compiler/src/style_compiler.ts @@ -66,7 +66,7 @@ export class StyleCompiler { stylesheet.styleUrls.forEach((styleUrl) => { const exprIndex = styleExpressions.length; // Note: This placeholder will be filled later. - styleExpressions.push(null !); + styleExpressions.push(null!); dependencies.push(new StylesCompileDependency( getStylesVarName(null), styleUrl, (value) => styleExpressions[exprIndex] = outputCtx.importExpr(value))); @@ -89,7 +89,7 @@ export class StyleCompiler { } } -function getStylesVarName(component: CompileDirectiveMetadata | null): string { +function getStylesVarName(component: CompileDirectiveMetadata|null): string { let result = `styles`; if (component) { result += `_${identifierName(component.type)}`; diff --git a/packages/compiler/src/summary_resolver.ts b/packages/compiler/src/summary_resolver.ts index 38d7dda3a07f9..8b53145ab98b2 100644 --- a/packages/compiler/src/summary_resolver.ts +++ b/packages/compiler/src/summary_resolver.ts @@ -28,14 +28,28 @@ export abstract class SummaryResolver<T> { export class JitSummaryResolver implements SummaryResolver<Type> { private _summaries = new Map<Type, Summary<Type>>(); - isLibraryFile(): boolean { return false; } - toSummaryFileName(fileName: string): string { return fileName; } - fromSummaryFileName(fileName: string): string { return fileName; } + isLibraryFile(): boolean { + return false; + } + toSummaryFileName(fileName: string): string { + return fileName; + } + fromSummaryFileName(fileName: string): string { + return fileName; + } resolveSummary(reference: Type): Summary<Type>|null { return this._summaries.get(reference) || null; } - getSymbolsOf(): Type[] { return []; } - getImportAs(reference: Type): Type { return reference; } - getKnownModuleName(fileName: string) { return null; } - addSummary(summary: Summary<Type>) { this._summaries.set(summary.symbol, summary); } + getSymbolsOf(): Type[] { + return []; + } + getImportAs(reference: Type): Type { + return reference; + } + getKnownModuleName(fileName: string) { + return null; + } + addSummary(summary: Summary<Type>) { + this._summaries.set(summary.symbol, summary); + } } diff --git a/packages/compiler/src/template_parser/binding_parser.ts b/packages/compiler/src/template_parser/binding_parser.ts index 3b0fa60c6d575..f6dde21f6d4cf 100644 --- a/packages/compiler/src/template_parser/binding_parser.ts +++ b/packages/compiler/src/template_parser/binding_parser.ts @@ -8,7 +8,7 @@ import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata'; import {SecurityContext} from '../core'; -import {ASTWithSource, AbsoluteSourceSpan, BindingPipe, BindingType, BoundElementProperty, EmptyExpr, ParsedEvent, ParsedEventType, ParsedProperty, ParsedPropertyType, ParsedVariable, ParserError, RecursiveAstVisitor, TemplateBinding, VariableBinding} from '../expression_parser/ast'; +import {AbsoluteSourceSpan, ASTWithSource, BindingPipe, BindingType, BoundElementProperty, EmptyExpr, ParsedEvent, ParsedEventType, ParsedProperty, ParsedPropertyType, ParsedVariable, ParserError, RecursiveAstVisitor, TemplateBinding, VariableBinding} from '../expression_parser/ast'; import {Parser} from '../expression_parser/parser'; import {InterpolationConfig} from '../ml_parser/interpolation_config'; import {mergeNsAndName} from '../ml_parser/tags'; @@ -45,9 +45,13 @@ export class BindingParser { } } - get interpolationConfig(): InterpolationConfig { return this._interpolationConfig; } + get interpolationConfig(): InterpolationConfig { + return this._interpolationConfig; + } - getUsedPipes(): CompilePipeSummary[] { return Array.from(this._usedPipes.values()); } + getUsedPipes(): CompilePipeSummary[] { + return Array.from(this._usedPipes.values()); + } createBoundHostProperties(dirMeta: CompileDirectiveSummary, sourceSpan: ParseSourceSpan): ParsedProperty[]|null { @@ -61,7 +65,9 @@ export class BindingParser { boundProps); } else { this._reportError( - `Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, + `Value of the host property binding "${ + propName}" needs to be a string representing an expression but got "${ + expression}" (${typeof expression})`, sourceSpan); } }); @@ -89,7 +95,9 @@ export class BindingParser { this.parseEvent(propName, expression, sourceSpan, sourceSpan, [], targetEvents); } else { this._reportError( - `Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, + `Value of the host listener "${ + propName}" needs to be a string representing an expression but got "${ + expression}" (${typeof expression})`, sourceSpan); } }); @@ -103,7 +111,7 @@ export class BindingParser { try { const ast = this._exprParser.parseInterpolation( - value, sourceInfo, sourceSpan.start.offset, this._interpolationConfig) !; + value, sourceInfo, sourceSpan.start.offset, this._interpolationConfig)!; if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan); this._checkPipes(ast, sourceSpan); return ast; @@ -145,8 +153,9 @@ export class BindingParser { binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined; targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan)); } else if (binding.value) { + const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan); this._parsePropertyAst( - key, binding.value, sourceSpan, undefined, targetMatchableAttrs, targetProps); + key, binding.value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps); } else { targetMatchableAttrs.push([key, '']); this.parseLiteralAttr( @@ -182,8 +191,9 @@ export class BindingParser { this._checkPipes(binding.value, sourceSpan); } }); - bindingsResult.warnings.forEach( - (warning) => { this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); }); + bindingsResult.warnings.forEach((warning) => { + this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); + }); return bindingsResult.templateBindings; } catch (e) { this._reportError(`${e}`, sourceSpan); @@ -256,7 +266,7 @@ export class BindingParser { name: string, ast: ASTWithSource, sourceSpan: ParseSourceSpan, valueSpan: ParseSourceSpan|undefined, targetMatchableAttrs: string[][], targetProps: ParsedProperty[]) { - targetMatchableAttrs.push([name, ast.source !]); + targetMatchableAttrs.push([name, ast.source!]); targetProps.push( new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, valueSpan)); } @@ -274,7 +284,7 @@ export class BindingParser { // states will be applied by angular when the element is attached/detached const ast = this._parseBinding( expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset); - targetMatchableAttrs.push([name, ast.source !]); + targetMatchableAttrs.push([name, ast.source!]); targetProps.push( new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, valueSpan)); } @@ -309,10 +319,10 @@ export class BindingParser { } let unit: string|null = null; - let bindingType: BindingType = undefined !; + let bindingType: BindingType = undefined!; let boundPropertyName: string|null = null; const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR); - let securityContexts: SecurityContext[] = undefined !; + let securityContexts: SecurityContext[] = undefined!; // Check for special cases (prefix style, attr, class) if (parts.length > 1) { @@ -400,13 +410,15 @@ export class BindingParser { default: this._reportError( - `The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, + `The provided animation output phase value "${phase}" for "@${ + eventName}" is not supported (use start or done)`, sourceSpan); break; } } else { this._reportError( - `The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, + `The animation trigger output event (@${ + eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan); } } @@ -415,9 +427,9 @@ export class BindingParser { name: string, expression: string, sourceSpan: ParseSourceSpan, handlerSpan: ParseSourceSpan, targetMatchableAttrs: string[][], targetEvents: ParsedEvent[]) { // long format: 'target: eventName' - const [target, eventName] = splitAtColon(name, [null !, name]); + const [target, eventName] = splitAtColon(name, [null!, name]); const ast = this._parseAction(expression, handlerSpan); - targetMatchableAttrs.push([name !, ast.source !]); + targetMatchableAttrs.push([name!, ast.source!]); targetEvents.push( new ParsedEvent(eventName, target, ParsedEventType.Regular, ast, sourceSpan, handlerSpan)); // Don't detect directives for event names for now, @@ -464,7 +476,7 @@ export class BindingParser { const collector = new PipeCollector(); ast.visit(collector); collector.pipes.forEach((ast, pipeName) => { - const pipeMeta = this.pipesByName !.get(pipeName); + const pipeMeta = this.pipesByName!.get(pipeName); if (!pipeMeta) { this._reportError( `The pipe '${pipeName}' could not be found`, @@ -487,7 +499,7 @@ export class BindingParser { const report = isAttr ? this._schemaRegistry.validateAttribute(propName) : this._schemaRegistry.validateProperty(propName); if (report.error) { - this._reportError(report.msg !, sourceSpan, ParseErrorLevel.ERROR); + this._reportError(report.msg!, sourceSpan, ParseErrorLevel.ERROR); } } } diff --git a/packages/compiler/src/template_parser/template_ast.ts b/packages/compiler/src/template_parser/template_ast.ts index 2a876bcc6ec5e..62be9f756b479 100644 --- a/packages/compiler/src/template_parser/template_ast.ts +++ b/packages/compiler/src/template_parser/template_ast.ts @@ -36,7 +36,9 @@ export interface TemplateAst { export class TextAst implements TemplateAst { constructor( public value: string, public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {} - visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitText(this, context); } + visit(visitor: TemplateAstVisitor, context: any): any { + return visitor.visitText(this, context); + } } /** @@ -56,7 +58,9 @@ export class BoundTextAst implements TemplateAst { */ export class AttrAst implements TemplateAst { constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {} - visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitAttr(this, context); } + visit(visitor: TemplateAstVisitor, context: any): any { + return visitor.visitAttr(this, context); + } } export const enum PropertyBindingType { @@ -319,7 +323,9 @@ export class NullTemplateVisitor implements TemplateAstVisitor { * in an template ast recursively. */ export class RecursiveTemplateAstVisitor extends NullTemplateVisitor implements TemplateAstVisitor { - constructor() { super(); } + constructor() { + super(); + } // Nodes with children visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { @@ -358,7 +364,7 @@ export class RecursiveTemplateAstVisitor extends NullTemplateVisitor implements cb: (visit: (<V extends TemplateAst>(children: V[]|undefined) => void)) => void) { let results: any[][] = []; let t = this; - function visit<T extends TemplateAst>(children: T[] | undefined) { + function visit<T extends TemplateAst>(children: T[]|undefined) { if (children && children.length) results.push(templateVisitAll(t, children, context)); } cb(visit); @@ -373,7 +379,7 @@ export function templateVisitAll( visitor: TemplateAstVisitor, asts: TemplateAst[], context: any = null): any[] { const result: any[] = []; const visit = visitor.visit ? - (ast: TemplateAst) => visitor.visit !(ast, context) || ast.visit(visitor, context) : + (ast: TemplateAst) => visitor.visit!(ast, context) || ast.visit(visitor, context) : (ast: TemplateAst) => ast.visit(visitor, context); asts.forEach(ast => { const astResult = visit(ast); diff --git a/packages/compiler/src/template_parser/template_parser.ts b/packages/compiler/src/template_parser/template_parser.ts index 453a32b29ed8f..2323b8f6838f4 100644 --- a/packages/compiler/src/template_parser/template_parser.ts +++ b/packages/compiler/src/template_parser/template_parser.ts @@ -12,7 +12,7 @@ import {CompilerConfig} from '../config'; import {SchemaMetadata} from '../core'; import {AST, ASTWithSource, EmptyExpr, ParsedEvent, ParsedProperty, ParsedVariable} from '../expression_parser/ast'; import {Parser} from '../expression_parser/parser'; -import {Identifiers, createTokenForExternalReference, createTokenForReference} from '../identifiers'; +import {createTokenForExternalReference, createTokenForReference, Identifiers} from '../identifiers'; import * as html from '../ml_parser/ast'; import {HtmlParser, ParseTreeResult} from '../ml_parser/html_parser'; import {removeWhitespaces, replaceNgsp} from '../ml_parser/html_whitespaces'; @@ -57,7 +57,7 @@ const IDENT_EVENT_IDX = 10; const TEMPLATE_ATTR_PREFIX = '*'; const CLASS_ATTR = 'class'; -let _TEXT_CSS_SELECTOR !: CssSelector; +let _TEXT_CSS_SELECTOR!: CssSelector; function TEXT_CSS_SELECTOR(): CssSelector { if (!_TEXT_CSS_SELECTOR) { _TEXT_CSS_SELECTOR = CssSelector.parse('*')[0]; @@ -84,7 +84,9 @@ export class TemplateParser { private _htmlParser: HtmlParser, private _console: Console, public transforms: t.TemplateAstVisitor[]) {} - public get expressionParser() { return this._exprParser; } + public get expressionParser() { + return this._exprParser; + } parse( component: CompileDirectiveMetadata, template: string|ParseTreeResult, @@ -93,9 +95,9 @@ export class TemplateParser { preserveWhitespaces: boolean): {template: t.TemplateAst[], pipes: CompilePipeSummary[]} { const result = this.tryParse( component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces); - const warnings = result.errors !.filter(error => error.level === ParseErrorLevel.WARNING); + const warnings = result.errors!.filter(error => error.level === ParseErrorLevel.WARNING); - const errors = result.errors !.filter(error => error.level === ParseErrorLevel.ERROR); + const errors = result.errors!.filter(error => error.level === ParseErrorLevel.ERROR); if (warnings.length > 0) { this._console.warn(`Template parse warnings:\n${warnings.join('\n')}`); @@ -106,7 +108,7 @@ export class TemplateParser { throw syntaxError(`Template parse errors:\n${errorString}`, errors); } - return {template: result.templateAst !, pipes: result.usedPipes !}; + return {template: result.templateAst!, pipes: result.usedPipes!}; } tryParse( @@ -114,7 +116,7 @@ export class TemplateParser { directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[], schemas: SchemaMetadata[], templateUrl: string, preserveWhitespaces: boolean): TemplateParseResult { let htmlParseResult = typeof template === 'string' ? - this._htmlParser !.parse(template, templateUrl, { + this._htmlParser!.parse(template, templateUrl, { tokenizeExpansionForms: true, interpolationConfig: this.getInterpolationConfig(component) }) : @@ -139,7 +141,7 @@ export class TemplateParser { const uniqDirectives = removeSummaryDuplicates(directives); const uniqPipes = removeSummaryDuplicates(pipes); const providerViewContext = new ProviderViewContext(this._reflector, component); - let interpolationConfig: InterpolationConfig = undefined !; + let interpolationConfig: InterpolationConfig = undefined!; if (component.template && component.template.interpolation) { interpolationConfig = { start: component.template.interpolation[0], @@ -147,7 +149,7 @@ export class TemplateParser { }; } const bindingParser = new BindingParser( - this._exprParser, interpolationConfig !, this._schemaRegistry, uniqPipes, errors); + this._exprParser, interpolationConfig!, this._schemaRegistry, uniqPipes, errors); const parseVisitor = new TemplateParseVisitor( this._reflector, this._config, providerViewContext, uniqDirectives, bindingParser, this._schemaRegistry, schemas, errors); @@ -164,8 +166,9 @@ export class TemplateParser { } if (this.transforms) { - this.transforms.forEach( - (transform: t.TemplateAstVisitor) => { result = t.templateVisitAll(transform, result); }); + this.transforms.forEach((transform: t.TemplateAstVisitor) => { + result = t.templateVisitAll(transform, result); + }); } return new TemplateParseResult(result, usedPipes, errors); @@ -224,29 +227,35 @@ class TemplateParseVisitor implements html.Visitor { // Note: queries start with id 1 so we can use the number in a Bloom filter! this.contentQueryStartId = providerViewContext.component.viewQueries.length + 1; directives.forEach((directive, index) => { - const selector = CssSelector.parse(directive.selector !); + const selector = CssSelector.parse(directive.selector!); this.selectorMatcher.addSelectables(selector, directive); this.directivesIndex.set(directive, index); }); } - visitExpansion(expansion: html.Expansion, context: any): any { return null; } + visitExpansion(expansion: html.Expansion, context: any): any { + return null; + } - visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return null; } + visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { + return null; + } visitText(text: html.Text, parent: ElementContext): any { - const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR()) !; + const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR())!; const valueNoNgsp = replaceNgsp(text.value); - const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan !); - return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan !) : - new t.TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan !); + const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan!); + return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan!) : + new t.TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan!); } visitAttribute(attribute: html.Attribute, context: any): any { return new t.AttrAst(attribute.name, attribute.value, attribute.sourceSpan); } - visitComment(comment: html.Comment, context: any): any { return null; } + visitComment(comment: html.Comment, context: any): any { + return null; + } visitElement(element: html.Element, parent: ElementContext): any { const queryStartIndex = this.contentQueryStartId; @@ -307,7 +316,7 @@ class TemplateParseVisitor implements html.Visitor { const parsedVariables: ParsedVariable[] = []; const absoluteOffset = (attr.valueSpan || attr.sourceSpan).start.offset; this._bindingParser.parseInlineTemplateBinding( - templateKey !, templateValue !, attr.sourceSpan, absoluteOffset, templateMatchableAttrs, + templateKey!, templateValue!, attr.sourceSpan, absoluteOffset, templateMatchableAttrs, templateElementOrDirectiveProps, parsedVariables); templateElementVars.push(...parsedVariables.map(v => t.VariableAst.fromParsedVariable(v))); } @@ -326,52 +335,51 @@ class TemplateParseVisitor implements html.Visitor { const boundDirectivePropNames = new Set<string>(); const directiveAsts = this._createDirectiveAsts( isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps, - elementOrDirectiveRefs, element.sourceSpan !, references, boundDirectivePropNames); + elementOrDirectiveRefs, element.sourceSpan!, references, boundDirectivePropNames); const elementProps: t.BoundElementPropertyAst[] = this._createElementPropertyAsts( element.name, elementOrDirectiveProps, boundDirectivePropNames); const isViewRoot = parent.isTemplateElement || hasInlineTemplates; const providerContext = new ProviderElementContext( - this.providerViewContext, parent.providerContext !, isViewRoot, directiveAsts, attrs, - references, isTemplateElement, queryStartIndex, element.sourceSpan !); + this.providerViewContext, parent.providerContext!, isViewRoot, directiveAsts, attrs, + references, isTemplateElement, queryStartIndex, element.sourceSpan!); const children: t.TemplateAst[] = html.visitAll( preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, ElementContext.create( isTemplateElement, directiveAsts, - isTemplateElement ? parent.providerContext ! : providerContext)); + isTemplateElement ? parent.providerContext! : providerContext)); providerContext.afterElement(); // Override the actual selector when the `ngProjectAs` attribute is provided const projectionSelector = preparsedElement.projectAs != '' ? CssSelector.parse(preparsedElement.projectAs)[0] : elementCssSelector; - const ngContentIndex = parent.findNgContentIndex(projectionSelector) !; + const ngContentIndex = parent.findNgContentIndex(projectionSelector)!; let parsedElement: t.TemplateAst; if (preparsedElement.type === PreparsedElementType.NG_CONTENT) { // `<ng-content>` element if (element.children && !element.children.every(_isEmptyTextNode)) { - this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan !); + this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan!); } parsedElement = new t.NgContentAst( - this.ngContentCount++, hasInlineTemplates ? null ! : ngContentIndex, - element.sourceSpan !); + this.ngContentCount++, hasInlineTemplates ? null! : ngContentIndex, element.sourceSpan!); } else if (isTemplateElement) { // `<ng-template>` element this._assertAllEventsPublishedByDirectives(directiveAsts, events); this._assertNoComponentsNorElementBindingsOnTemplate( - directiveAsts, elementProps, element.sourceSpan !); + directiveAsts, elementProps, element.sourceSpan!); parsedElement = new t.EmbeddedTemplateAst( attrs, events, references, elementVars, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, - providerContext.queryMatches, children, hasInlineTemplates ? null ! : ngContentIndex, - element.sourceSpan !); + providerContext.queryMatches, children, hasInlineTemplates ? null! : ngContentIndex, + element.sourceSpan!); } else { // element other than `<ng-content>` and `<ng-template>` this._assertElementExists(matchElement, element); - this._assertOnlyOneComponent(directiveAsts, element.sourceSpan !); + this._assertOnlyOneComponent(directiveAsts, element.sourceSpan!); const ngContentIndex = hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector); @@ -389,22 +397,22 @@ class TemplateParseVisitor implements html.Visitor { const {directives} = this._parseDirectives(this.selectorMatcher, templateSelector); const templateBoundDirectivePropNames = new Set<string>(); const templateDirectiveAsts = this._createDirectiveAsts( - true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan !, [], + true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan!, [], templateBoundDirectivePropNames); const templateElementProps: t.BoundElementPropertyAst[] = this._createElementPropertyAsts( elName, templateElementOrDirectiveProps, templateBoundDirectivePropNames); this._assertNoComponentsNorElementBindingsOnTemplate( - templateDirectiveAsts, templateElementProps, element.sourceSpan !); + templateDirectiveAsts, templateElementProps, element.sourceSpan!); const templateProviderContext = new ProviderElementContext( - this.providerViewContext, parent.providerContext !, parent.isTemplateElement, - templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan !); + this.providerViewContext, parent.providerContext!, parent.isTemplateElement, + templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan!); templateProviderContext.afterElement(); parsedElement = new t.EmbeddedTemplateAst( [], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts, templateProviderContext.transformProviders, templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches, - [parsedElement], ngContentIndex, element.sourceSpan !); + [parsedElement], ngContentIndex, element.sourceSpan!); } return parsedElement; @@ -538,7 +546,7 @@ class TemplateParseVisitor implements html.Visitor { let matchElement = false; selectorMatcher.match(elementCssSelector, (selector, directive) => { - directives[this.directivesIndex.get(directive) !] = directive; + directives[this.directivesIndex.get(directive)!] = directive; matchElement = matchElement || selector.hasElementSelector(); }); @@ -554,7 +562,7 @@ class TemplateParseVisitor implements html.Visitor { elementSourceSpan: ParseSourceSpan, targetReferences: t.ReferenceAst[], targetBoundDirectivePropNames: Set<string>): t.DirectiveAst[] { const matchedReferences = new Set<string>(); - let component: CompileDirectiveSummary = null !; + let component: CompileDirectiveSummary = null!; const directiveAsts = directives.map((directive) => { const sourceSpan = new ParseSourceSpan( @@ -566,15 +574,14 @@ class TemplateParseVisitor implements html.Visitor { } const directiveProperties: t.BoundDirectivePropertyAst[] = []; const boundProperties = - this._bindingParser.createDirectiveHostPropertyAsts(directive, elementName, sourceSpan) !; + this._bindingParser.createDirectiveHostPropertyAsts(directive, elementName, sourceSpan)!; let hostProperties = boundProperties.map(prop => t.BoundElementPropertyAst.fromBoundProperty(prop)); // Note: We need to check the host properties here as well, // as we don't know the element name in the DirectiveWrapperCompiler yet. hostProperties = this._checkPropertiesInSchema(elementName, hostProperties); - const parsedEvents = - this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan) !; + const parsedEvents = this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan)!; this._createDirectivePropertyAsts( directive.inputs, props, directiveProperties, targetBoundDirectivePropNames); elementOrDirectiveRefs.forEach((elOrDirRef) => { @@ -602,7 +609,7 @@ class TemplateParseVisitor implements html.Visitor { elOrDirRef.sourceSpan); } } else if (!component) { - let refToken: CompileTokenMetadata = null !; + let refToken: CompileTokenMetadata = null!; if (isTemplateElement) { refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef); } @@ -663,7 +670,7 @@ class TemplateParseVisitor implements html.Visitor { private _findComponentDirectiveNames(directives: t.DirectiveAst[]): string[] { return this._findComponentDirectives(directives) - .map(directive => identifierName(directive.directive.type) !); + .map(directive => identifierName(directive.directive.type)!); } private _assertOnlyOneComponent(directives: t.DirectiveAst[], sourceSpan: ParseSourceSpan) { @@ -691,16 +698,16 @@ class TemplateParseVisitor implements html.Visitor { if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) { let errorMsg = `'${elName}' is not a known element:\n`; - errorMsg += - `1. If '${elName}' is an Angular component, then verify that it is part of this module.\n`; + errorMsg += `1. If '${ + elName}' is an Angular component, then verify that it is part of this module.\n`; if (elName.indexOf('-') > -1) { - errorMsg += - `2. If '${elName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`; + errorMsg += `2. If '${ + elName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`; } else { errorMsg += `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } - this._reportError(errorMsg, element.sourceSpan !); + this._reportError(errorMsg, element.sourceSpan!); } } @@ -714,7 +721,8 @@ class TemplateParseVisitor implements html.Visitor { } elementProps.forEach(prop => { this._reportError( - `Property binding ${prop.name} not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations".`, + `Property binding ${ + prop.name} not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations".`, sourceSpan); }); } @@ -733,7 +741,9 @@ class TemplateParseVisitor implements html.Visitor { events.forEach(event => { if (event.target != null || !allDirectiveEvents.has(event.name)) { this._reportError( - `Event binding ${event.fullName} not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations".`, + `Event binding ${ + event + .fullName} not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations".`, event.sourceSpan); } }); @@ -746,16 +756,20 @@ class TemplateParseVisitor implements html.Visitor { return boundProps.filter((boundProp) => { if (boundProp.type === t.PropertyBindingType.Property && !this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) { - let errorMsg = - `Can't bind to '${boundProp.name}' since it isn't a known property of '${elementName}'.`; + let errorMsg = `Can't bind to '${boundProp.name}' since it isn't a known property of '${ + elementName}'.`; if (elementName.startsWith('ng-')) { errorMsg += - `\n1. If '${boundProp.name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` + + `\n1. If '${ + boundProp + .name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` + `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } else if (elementName.indexOf('-') > -1) { errorMsg += - `\n1. If '${elementName}' is an Angular component and it has '${boundProp.name}' input, then verify that it is part of this module.` + - `\n2. If '${elementName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` + + `\n1. If '${elementName}' is an Angular component and it has '${ + boundProp.name}' input, then verify that it is part of this module.` + + `\n2. If '${ + elementName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` + `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; } this._reportError(errorMsg, boundProp.sourceSpan); @@ -791,20 +805,26 @@ class NonBindableVisitor implements html.Visitor { ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, [], children, ngContentIndex, ast.sourceSpan, ast.endSourceSpan); } - visitComment(comment: html.Comment, context: any): any { return null; } + visitComment(comment: html.Comment, context: any): any { + return null; + } visitAttribute(attribute: html.Attribute, context: any): t.AttrAst { return new t.AttrAst(attribute.name, attribute.value, attribute.sourceSpan); } visitText(text: html.Text, parent: ElementContext): t.TextAst { - const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR()) !; - return new t.TextAst(text.value, ngContentIndex, text.sourceSpan !); + const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR())!; + return new t.TextAst(text.value, ngContentIndex, text.sourceSpan!); } - visitExpansion(expansion: html.Expansion, context: any): any { return expansion; } + visitExpansion(expansion: html.Expansion, context: any): any { + return expansion; + } - visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return expansionCase; } + visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { + return expansionCase; + } } /** @@ -824,7 +844,7 @@ class ElementOrDirectiveRef { } /** Splits a raw, potentially comma-delimited `exportAs` value into an array of names. */ -function splitExportAs(exportAs: string | null): string[] { +function splitExportAs(exportAs: string|null): string[] { return exportAs ? exportAs.split(',').map(e => e.trim()) : []; } @@ -837,7 +857,7 @@ class ElementContext { isTemplateElement: boolean, directives: t.DirectiveAst[], providerContext: ProviderElementContext): ElementContext { const matcher = new SelectorMatcher(); - let wildcardNgContentIndex: number = null !; + let wildcardNgContentIndex: number = null!; const component = directives.find(directive => directive.directive.isComponent); if (component) { const ngContentSelectors = component.directive.template !.ngContentSelectors; @@ -859,8 +879,9 @@ class ElementContext { findNgContentIndex(selector: CssSelector): number|null { const ngContentIndices: number[] = []; - this._ngContentIndexMatcher.match( - selector, (selector, ngContentIndex) => { ngContentIndices.push(ngContentIndex); }); + this._ngContentIndexMatcher.match(selector, (selector, ngContentIndex) => { + ngContentIndices.push(ngContentIndex); + }); ngContentIndices.sort(); if (this._wildcardNgContentIndex != null) { ngContentIndices.push(this._wildcardNgContentIndex); @@ -897,7 +918,7 @@ function _isEmptyTextNode(node: html.Node): boolean { return node instanceof html.Text && node.value.trim().length == 0; } -export function removeSummaryDuplicates<T extends{type: CompileTypeMetadata}>(items: T[]): T[] { +export function removeSummaryDuplicates<T extends {type: CompileTypeMetadata}>(items: T[]): T[] { const map = new Map<any, T>(); items.forEach((item) => { diff --git a/packages/compiler/src/template_parser/template_preparser.ts b/packages/compiler/src/template_parser/template_preparser.ts index d76d53b2314e8..515806cceb7a5 100644 --- a/packages/compiler/src/template_parser/template_preparser.ts +++ b/packages/compiler/src/template_parser/template_preparser.ts @@ -20,9 +20,9 @@ const NG_NON_BINDABLE_ATTR = 'ngNonBindable'; const NG_PROJECT_AS = 'ngProjectAs'; export function preparseElement(ast: html.Element): PreparsedElement { - let selectAttr: string = null !; - let hrefAttr: string = null !; - let relAttr: string = null !; + let selectAttr: string = null!; + let hrefAttr: string = null!; + let relAttr: string = null!; let nonBindable = false; let projectAs = ''; ast.attrs.forEach(attr => { diff --git a/packages/compiler/src/url_resolver.ts b/packages/compiler/src/url_resolver.ts index 3f819309c0c6f..daf54b89168df 100644 --- a/packages/compiler/src/url_resolver.ts +++ b/packages/compiler/src/url_resolver.ts @@ -33,9 +33,13 @@ export function createOfflineCompileUrlResolver(): UrlResolver { * Attacker-controlled data introduced by a template could expose your * application to XSS risks. For more detail, see the [Security Guide](http://g.co/ng/security). */ -export interface UrlResolver { resolve(baseUrl: string, url: string): string; } +export interface UrlResolver { + resolve(baseUrl: string, url: string): string; +} -export interface UrlResolverCtor { new (packagePrefix?: string|null): UrlResolver; } +export interface UrlResolverCtor { + new(packagePrefix?: string|null): UrlResolver; +} export const UrlResolver: UrlResolverCtor = class UrlResolverImpl { constructor(private _packagePrefix: string|null = null) {} @@ -242,16 +246,16 @@ enum _ComponentIndex { * arbitrary strings may still look like path names. */ function _split(uri: string): Array<string|any> { - return uri.match(_splitRe) !; + return uri.match(_splitRe)!; } /** - * Removes dot segments in given path component, as described in - * RFC 3986, section 5.2.4. - * - * @param path A non-empty path component. - * @return Path component with removed dot segments. - */ + * Removes dot segments in given path component, as described in + * RFC 3986, section 5.2.4. + * + * @param path A non-empty path component. + * @return Path component with removed dot segments. + */ function _removeDotSegments(path: string): string { if (path == '/') return '/'; diff --git a/packages/compiler/src/util.ts b/packages/compiler/src/util.ts index 789219e115234..82b87ef97daee 100644 --- a/packages/compiler/src/util.ts +++ b/packages/compiler/src/util.ts @@ -52,8 +52,8 @@ export function isDefined(val: any): boolean { return val !== null && val !== undefined; } -export function noUndefined<T>(val: T | undefined): T { - return val === undefined ? null ! : val; +export function noUndefined<T>(val: T|undefined): T { + return val === undefined ? null! : val; } export interface ValueVisitor { @@ -69,14 +69,20 @@ export class ValueTransformer implements ValueVisitor { } visitStringMap(map: {[key: string]: any}, context: any): any { const result: {[key: string]: any} = {}; - Object.keys(map).forEach(key => { result[key] = visitValue(map[key], this, context); }); + Object.keys(map).forEach(key => { + result[key] = visitValue(map[key], this, context); + }); return result; } - visitPrimitive(value: any, context: any): any { return value; } - visitOther(value: any, context: any): any { return value; } + visitPrimitive(value: any, context: any): any { + return value; + } + visitOther(value: any, context: any): any { + return value; + } } -export type SyncAsync<T> = T | Promise<T>; +export type SyncAsync<T> = T|Promise<T>; export const SyncAsync = { assertSync: <T>(value: SyncAsync<T>): T => { @@ -86,7 +92,9 @@ export const SyncAsync = { return value; }, then: <T, R>(value: SyncAsync<T>, cb: (value: T) => R | Promise<R>| SyncAsync<R>): - SyncAsync<R> => { return isPromise(value) ? value.then(cb) : cb(value);}, + SyncAsync<R> => { + return isPromise(value) ? value.then(cb) : cb(value); + }, all: <T>(syncAsyncValues: SyncAsync<T>[]): SyncAsync<T[]> => { return syncAsyncValues.some(isPromise) ? Promise.all(syncAsyncValues) : syncAsyncValues as T[]; } @@ -259,7 +267,25 @@ export function newArray<T>(size: number, value: T): T[]; export function newArray<T>(size: number, value?: T): T[] { const list: T[] = []; for (let i = 0; i < size; i++) { - list.push(value !); + list.push(value!); } return list; +} + +/** + * Partitions a given array into 2 arrays, based on a boolean value returned by the condition + * function. + * + * @param arr Input array that should be partitioned + * @param conditionFn Condition function that is called for each item in a given array and returns a + * boolean value. + */ +export function partitionArray<T>( + arr: T[], conditionFn: <K extends T>(value: K) => boolean): [T[], T[]] { + const truthy: T[] = []; + const falsy: T[] = []; + arr.forEach(item => { + (conditionFn(item) ? truthy : falsy).push(item); + }); + return [truthy, falsy]; } \ No newline at end of file diff --git a/packages/compiler/src/view_compiler/provider_compiler.ts b/packages/compiler/src/view_compiler/provider_compiler.ts index baa833cd53b02..8dbc2b182fcf9 100644 --- a/packages/compiler/src/view_compiler/provider_compiler.ts +++ b/packages/compiler/src/view_compiler/provider_compiler.ts @@ -9,7 +9,7 @@ import {CompileDiDependencyMetadata, CompileEntryComponentMetadata, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompileReflector} from '../compile_reflector'; import {DepFlags, NodeFlags} from '../core'; -import {Identifiers, createTokenForExternalReference} from '../identifiers'; +import {createTokenForExternalReference, Identifiers} from '../identifiers'; import {LifecycleHooks} from '../lifecycle_reflector'; import * as o from '../output/output_ast'; import {convertValueToOutputAst} from '../output/value_util'; @@ -45,7 +45,8 @@ export function providerDef(ctx: OutputContext, providerAst: ProviderAst): { singleProviderDef(ctx, flags, providerAst.providerType, providerAst.providers[0]); return { providerExpr, - flags: providerFlags, depsExpr, + flags: providerFlags, + depsExpr, tokenExpr: tokenExpr(ctx, providerAst.token), }; } @@ -96,9 +97,9 @@ function singleProviderDef( let providerExpr: o.Expression; let deps: CompileDiDependencyMetadata[]; if (providerType === ProviderAstType.Directive || providerType === ProviderAstType.Component) { - providerExpr = ctx.importExpr(providerMeta.useClass !.reference); + providerExpr = ctx.importExpr(providerMeta.useClass!.reference); flags |= NodeFlags.TypeDirective; - deps = providerMeta.deps || providerMeta.useClass !.diDeps; + deps = providerMeta.deps || providerMeta.useClass!.diDeps; } else { if (providerMeta.useClass) { providerExpr = ctx.importExpr(providerMeta.useClass.reference); @@ -130,7 +131,7 @@ function tokenExpr(ctx: OutputContext, tokenMeta: CompileTokenMetadata): o.Expre export function depDef(ctx: OutputContext, dep: CompileDiDependencyMetadata): o.Expression { // Note: the following fields have already been normalized out by provider_analyzer: // - isAttribute, isHost - const expr = dep.isValue ? convertValueToOutputAst(ctx, dep.value) : tokenExpr(ctx, dep.token !); + const expr = dep.isValue ? convertValueToOutputAst(ctx, dep.value) : tokenExpr(ctx, dep.token!); let flags = DepFlags.None; if (dep.isSkipSelf) { flags |= DepFlags.SkipSelf; diff --git a/packages/compiler/src/view_compiler/type_check_compiler.ts b/packages/compiler/src/view_compiler/type_check_compiler.ts index 02a3b9ec1aeac..7d06d5ee209bf 100644 --- a/packages/compiler/src/view_compiler/type_check_compiler.ts +++ b/packages/compiler/src/view_compiler/type_check_compiler.ts @@ -10,11 +10,11 @@ import {AotCompilerOptions} from '../aot/compiler_options'; import {StaticReflector} from '../aot/static_reflector'; import {StaticSymbol} from '../aot/static_symbol'; import {CompileDirectiveMetadata, CompilePipeSummary} from '../compile_metadata'; -import {BindingForm, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter'; +import {BindingForm, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins, EventHandlerVars, LocalResolver} from '../compiler_util/expression_converter'; import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast'; import * as o from '../output/output_ast'; import {ParseSourceSpan} from '../parse_util'; -import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast'; +import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, templateVisitAll, TextAst, VariableAst} from '../template_parser/template_ast'; import {OutputContext} from '../util'; @@ -40,7 +40,7 @@ export class TypeCheckCompiler { usedPipes.forEach(p => pipes.set(p.name, p.type.reference)); let embeddedViewCount = 0; const viewBuilderFactory = - (parent: ViewBuilder | null, guards: GuardExpression[]): ViewBuilder => { + (parent: ViewBuilder|null, guards: GuardExpression[]): ViewBuilder => { const embeddedViewIndex = embeddedViewCount++; return new ViewBuilder( this.options, this.reflector, externalReferenceVars, parent, component.type.reference, @@ -66,7 +66,7 @@ interface ViewBuilderFactory { // Note: This is used as key in Map and should therefore be // unique per value. -type OutputVarType = o.BuiltinTypeName | StaticSymbol; +type OutputVarType = o.BuiltinTypeName|StaticSymbol; interface Expression { context: OutputVarType; @@ -247,10 +247,12 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { directives: DirectiveAst[], references: ReferenceAst[], }) { - ast.directives.forEach((dirAst) => { this.visitDirective(dirAst); }); + ast.directives.forEach((dirAst) => { + this.visitDirective(dirAst); + }); ast.references.forEach((ref) => { - let outputVarType: OutputVarType = null !; + let outputVarType: OutputVarType = null!; // Note: The old view compiler used to use an `any` type // for directives exposed via `exportAs`. // We keep this behaivor behind a flag for now. @@ -331,8 +333,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { // for arrays. return this.options.fullTemplateTypeCheck ? arr : arr.cast(o.DYNAMIC_TYPE); }, - createLiteralMapConverter: - (keys: {key: string, quoted: boolean}[]) => (values: o.Expression[]) => { + createLiteralMapConverter: (keys: {key: string, quoted: boolean}[]) => + (values: o.Expression[]) => { const entries = keys.map((k, i) => ({ key: k.key, value: values[i], diff --git a/packages/compiler/src/view_compiler/view_compiler.ts b/packages/compiler/src/view_compiler/view_compiler.ts index a0ede7a73e067..8a52d633eadad 100644 --- a/packages/compiler/src/view_compiler/view_compiler.ts +++ b/packages/compiler/src/view_compiler/view_compiler.ts @@ -8,7 +8,7 @@ import {CompileDirectiveMetadata, CompilePipeSummary, CompileQueryMetadata, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata'; import {CompileReflector} from '../compile_reflector'; -import {BindingForm, BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter'; +import {BindingForm, BuiltinConverter, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins, EventHandlerVars, LocalResolver} from '../compiler_util/expression_converter'; import {ArgumentType, BindingFlags, ChangeDetectionStrategy, NodeFlags, QueryBindingType, QueryValueType, ViewFlags} from '../core'; import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast'; import {Identifiers} from '../identifiers'; @@ -17,7 +17,7 @@ import {isNgContainer} from '../ml_parser/tags'; import * as o from '../output/output_ast'; import {convertValueToOutputAst} from '../output/value_util'; import {ParseSourceSpan} from '../parse_util'; -import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast'; +import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, templateVisitAll, TextAst, VariableAst} from '../template_parser/template_ast'; import {OutputContext} from '../util'; import {componentFactoryResolverProviderDef, depDef, lifecycleHookToNodeFlag, providerDef} from './provider_compiler'; @@ -38,7 +38,7 @@ export class ViewCompiler { styles: o.Expression, usedPipes: CompilePipeSummary[]): ViewCompileResult { let embeddedViewCount = 0; - let renderComponentVarName: string = undefined !; + let renderComponentVarName: string = undefined!; if (!component.isHost) { const template = component.template !; const customRenderData: o.LiteralMapEntry[] = []; @@ -48,7 +48,7 @@ export class ViewCompiler { } const renderComponentVar = o.variable(rendererTypeName(component.type.reference)); - renderComponentVarName = renderComponentVar.name !; + renderComponentVarName = renderComponentVar.name!; outputCtx.statements.push( renderComponentVar .set(o.importExpr(Identifiers.createRendererType2).callFn([new o.LiteralMapExpr([ @@ -61,7 +61,7 @@ export class ViewCompiler { [o.StmtModifier.Final, o.StmtModifier.Exported])); } - const viewBuilderFactory = (parent: ViewBuilder | null): ViewBuilder => { + const viewBuilderFactory = (parent: ViewBuilder|null): ViewBuilder => { const embeddedViewIndex = embeddedViewCount++; return new ViewBuilder( this._reflector, outputCtx, parent, component, embeddedViewIndex, usedPipes, @@ -101,7 +101,9 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { private nodes: (() => { sourceSpan: ParseSourceSpan | null, nodeDef: o.Expression, - nodeFlags: NodeFlags, updateDirectives?: UpdateExpression[], updateRenderer?: UpdateExpression[] + nodeFlags: NodeFlags, + updateDirectives?: UpdateExpression[], + updateRenderer?: UpdateExpression[] })[] = []; private purePipeNodeIndices: {[pipeName: string]: number} = Object.create(null); // Need Object.create so that we don't have builtin values... @@ -121,7 +123,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { // to be able to introduce the new view compiler without too many errors. this.compType = this.embeddedViewIndex > 0 ? o.DYNAMIC_TYPE : - o.expressionType(outputCtx.importExpr(this.component.type.reference)) !; + o.expressionType(outputCtx.importExpr(this.component.type.reference))!; this.viewName = viewClassName(this.component.type.reference, this.embeddedViewIndex); } @@ -181,7 +183,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { viewFlags |= ViewFlags.OnPush; } const viewFactory = new o.DeclareFunctionStmt( - this.viewName, [new o.FnParam(LOG_VAR.name !)], + this.viewName, [new o.FnParam(LOG_VAR.name!)], [new o.ReturnStatement(o.importExpr(Identifiers.viewDef).callFn([ o.literal(viewFlags), o.literalArr(nodeDefExprs), @@ -199,13 +201,13 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { let updateFn: o.Expression; if (updateStmts.length > 0) { const preStmts: o.Statement[] = []; - if (!this.component.isHost && o.findReadVarNames(updateStmts).has(COMP_VAR.name !)) { + if (!this.component.isHost && o.findReadVarNames(updateStmts).has(COMP_VAR.name!)) { preStmts.push(COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(this.compType)); } updateFn = o.fn( [ - new o.FnParam(CHECK_VAR.name !, o.INFERRED_TYPE), - new o.FnParam(VIEW_VAR.name !, o.INFERRED_TYPE) + new o.FnParam(CHECK_VAR.name!, o.INFERRED_TYPE), + new o.FnParam(VIEW_VAR.name!, o.INFERRED_TYPE) ], [...preStmts, ...updateStmts], o.INFERRED_TYPE); } else { @@ -219,9 +221,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { this.nodes.push(() => ({ sourceSpan: ast.sourceSpan, nodeFlags: NodeFlags.TypeNgContent, - nodeDef: o.importExpr(Identifiers.ngContentDef).callFn([ - o.literal(ast.ngContentIndex), o.literal(ast.index) - ]) + nodeDef: o.importExpr(Identifiers.ngContentDef) + .callFn([o.literal(ast.ngContentIndex), o.literal(ast.index)]) })); } @@ -242,7 +243,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { visitBoundText(ast: BoundTextAst, context: any): any { const nodeIndex = this.nodes.length; // reserve the space in the nodeDefs array - this.nodes.push(null !); + this.nodes.push(null!); const astWithSource = <ASTWithSource>ast.value; const inter = <Interpolation>astWithSource.ast; @@ -270,7 +271,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { const nodeIndex = this.nodes.length; // reserve the space in the nodeDefs array - this.nodes.push(null !); + this.nodes.push(null!); const {flags, queryMatchesExpr, hostEvents} = this._visitElementOrTemplate(nodeIndex, ast); @@ -301,7 +302,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { visitElement(ast: ElementAst, context: any): any { const nodeIndex = this.nodes.length; // reserve the space in the nodeDefs array so we can add children - this.nodes.push(null !); + this.nodes.push(null!); // Using a null element name creates an anchor. const elName: string|null = isNgContainer(ast.name) ? null : ast.name; @@ -382,7 +383,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { queryMatches: QueryMatch[] }): { flags: NodeFlags, - usedEvents: [string | null, string][], + usedEvents: [string|null, string][], queryMatchesExpr: o.Expression, hostBindings: {context: o.Expression, inputAst: BoundElementPropertyAst, dirAst: DirectiveAst}[], @@ -409,7 +410,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { this._visitComponentFactoryResolverProvider(ast.directives); ast.providers.forEach(providerAst => { - let dirAst: DirectiveAst = undefined !; + let dirAst: DirectiveAst = undefined!; ast.directives.forEach(localDirAst => { if (localDirAst.directive.type.reference === tokenReference(providerAst.token)) { dirAst = localDirAst; @@ -427,7 +428,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { let queryMatchExprs: o.Expression[] = []; ast.queryMatches.forEach((match) => { - let valueType: QueryValueType = undefined !; + let valueType: QueryValueType = undefined!; if (tokenReference(match.value) === this.reflector.resolveExternalReference(Identifiers.ElementRef)) { valueType = QueryValueType.ElementRef; @@ -445,7 +446,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { } }); ast.references.forEach((ref) => { - let valueType: QueryValueType = undefined !; + let valueType: QueryValueType = undefined!; if (!ref.value) { valueType = QueryValueType.RenderElement; } else if ( @@ -459,7 +460,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { } }); ast.outputs.forEach((outputAst) => { - hostEvents.push({context: COMP_VAR, eventAst: outputAst, dirAst: null !}); + hostEvents.push({context: COMP_VAR, eventAst: outputAst, dirAst: null!}); }); return { @@ -480,7 +481,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { } { const nodeIndex = this.nodes.length; // reserve the space in the nodeDefs array so we can add children - this.nodes.push(null !); + this.nodes.push(null!); dirAst.directive.queries.forEach((query, queryIndex) => { const queryId = dirAst.contentQueryStartId + queryIndex; @@ -554,7 +555,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { })); const hostEvents = dirAst.hostEvents.map((hostEventAst) => ({ context: dirContextExpr, - eventAst: hostEventAst, dirAst, + eventAst: hostEventAst, + dirAst, })); // Check index is the same as the node index during compilation @@ -727,7 +729,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { private _createPipeConverter(expression: UpdateExpression, name: string, argCount: number): BuiltinConverter { - const pipe = this.usedPipes.find((pipeSummary) => pipeSummary.name === name) !; + const pipe = this.usedPipes.find((pipeSummary) => pipeSummary.name === name)!; if (pipe.pure) { const checkIndex = this.nodes.length; this.nodes.push(() => ({ @@ -803,13 +805,12 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { context: expression.context, value: convertPropertyBindingBuiltins( { - createLiteralArrayConverter: (argCount: number) => this._createLiteralArrayConverter( - expression.sourceSpan, argCount), - createLiteralMapConverter: - (keys: {key: string, quoted: boolean}[]) => - this._createLiteralMapConverter(expression.sourceSpan, keys), + createLiteralArrayConverter: (argCount: number) => + this._createLiteralArrayConverter(expression.sourceSpan, argCount), + createLiteralMapConverter: (keys: {key: string, quoted: boolean}[]) => + this._createLiteralMapConverter(expression.sourceSpan, keys), createPipeConverter: (name: string, argCount: number) => - this._createPipeConverter(expression, name, argCount) + this._createPipeConverter(expression, name, argCount) }, expression.value) }; @@ -848,7 +849,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { return {updateRendererStmts, updateDirectivesStmts, nodeDefExprs}; function createUpdateStatements( - nodeIndex: number, sourceSpan: ParseSourceSpan | null, expressions: UpdateExpression[], + nodeIndex: number, sourceSpan: ParseSourceSpan|null, expressions: UpdateExpression[], allowEmptyExprs: boolean): o.Statement[] { const updateStmts: o.Statement[] = []; const exprs = expressions.map(({sourceSpan, context, value}) => { @@ -892,14 +893,14 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver { if (handleEventStmts.length > 0) { const preStmts: o.Statement[] = [ALLOW_DEFAULT_VAR.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE)]; - if (!this.component.isHost && o.findReadVarNames(handleEventStmts).has(COMP_VAR.name !)) { + if (!this.component.isHost && o.findReadVarNames(handleEventStmts).has(COMP_VAR.name!)) { preStmts.push(COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(this.compType)); } handleEventFn = o.fn( [ - new o.FnParam(VIEW_VAR.name !, o.INFERRED_TYPE), - new o.FnParam(EVENT_NAME_VAR.name !, o.INFERRED_TYPE), - new o.FnParam(EventHandlerVars.event.name !, o.INFERRED_TYPE) + new o.FnParam(VIEW_VAR.name!, o.INFERRED_TYPE), + new o.FnParam(EVENT_NAME_VAR.name!, o.INFERRED_TYPE), + new o.FnParam(EventHandlerVars.event.name!, o.INFERRED_TYPE) ], [...preStmts, ...handleEventStmts, new o.ReturnStatement(ALLOW_DEFAULT_VAR)], o.INFERRED_TYPE); @@ -975,7 +976,9 @@ function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveA function fixedAttrsDef(elementAst: ElementAst): o.Expression { const mapResult: {[key: string]: string} = Object.create(null); - elementAst.attrs.forEach(attrAst => { mapResult[attrAst.name] = attrAst.value; }); + elementAst.attrs.forEach(attrAst => { + mapResult[attrAst.name] = attrAst.value; + }); elementAst.directives.forEach(dirAst => { Object.keys(dirAst.directive.hostAttributes).forEach(name => { const value = dirAst.directive.hostAttributes[name]; @@ -1014,7 +1017,7 @@ function callUnwrapValue(nodeIndex: number, bindingIdx: number, expr: o.Expressi } function elementEventNameAndTarget( - eventAst: BoundEventAst, dirAst: DirectiveAst | null): {name: string, target: string | null} { + eventAst: BoundEventAst, dirAst: DirectiveAst|null): {name: string, target: string|null} { if (eventAst.isAnimation) { return { name: `@${eventAst.name}.${eventAst.phase}`, @@ -1037,6 +1040,6 @@ function calcStaticDynamicQueryFlags(query: CompileQueryMetadata) { return flags; } -export function elementEventFullName(target: string | null, name: string): string { +export function elementEventFullName(target: string|null, name: string): string { return target ? `${target}:${name}` : name; } diff --git a/packages/compiler/test/aot/compiler_spec.ts b/packages/compiler/test/aot/compiler_spec.ts index fd30a6d1a95c6..3084b9d3905b6 100644 --- a/packages/compiler/test/aot/compiler_spec.ts +++ b/packages/compiler/test/aot/compiler_spec.ts @@ -13,7 +13,7 @@ import {extractSourceMap, originalPositionFor} from '@angular/compiler/testing/s import {NodeFlags} from '@angular/core/src/view/index'; import * as ts from 'typescript'; -import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockDirectory, MockMetadataBundlerHost, arrayToMockDir, compile, expectNoDiagnostics, isInBazel, settings, setup, toMockFileArray} from './test_util'; +import {arrayToMockDir, compile, EmittingCompilerHost, expectNoDiagnostics, isInBazel, MockAotCompilerHost, MockCompilerHost, MockDirectory, MockMetadataBundlerHost, settings, setup, toMockFileArray} from './test_util'; describe('compiler (unbundled Angular)', () => { let angularFiles = setup(); @@ -53,11 +53,11 @@ describe('compiler (unbundled Angular)', () => { function compileApp(): GeneratedFile { const {genFiles} = compile([rootDir, angularFiles]); return genFiles.find( - genFile => genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts')) !; + genFile => genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'))!; } function findLineAndColumn( - file: string, token: string): {line: number | null, column: number | null} { + file: string, token: string): {line: number|null, column: number|null} { const index = file.indexOf(token); if (index === -1) { return {line: null, column: null}; @@ -84,7 +84,9 @@ describe('compiler (unbundled Angular)', () => { describe('inline templates', () => { const ngUrl = `${componentPath}.AppComponent.html`; - function templateDecorator(template: string) { return `template: \`${template}\`,`; } + function templateDecorator(template: string) { + return `template: \`${template}\`,`; + } declareTests({ngUrl, templateDecorator}); }); @@ -125,7 +127,7 @@ describe('compiler (unbundled Angular)', () => { const genFile = compileApp(); const genSource = toTypeScript(genFile); - const sourceMap = extractSourceMap(genSource) !; + const sourceMap = extractSourceMap(genSource)!; expect(sourceMap.file).toEqual(genFile.genFileUrl); // Note: the generated file also contains code that is not mapped to @@ -146,7 +148,7 @@ describe('compiler (unbundled Angular)', () => { const genFile = compileApp(); const genSource = toTypeScript(genFile); - const sourceMap = extractSourceMap(genSource) !; + const sourceMap = extractSourceMap(genSource)!; expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `'span'`))) .toEqual({line: 2, column: 3, source: ngUrl}); }); @@ -158,7 +160,7 @@ describe('compiler (unbundled Angular)', () => { const genFile = compileApp(); const genSource = toTypeScript(genFile); - const sourceMap = extractSourceMap(genSource) !; + const sourceMap = extractSourceMap(genSource)!; expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`))) .toEqual({line: 2, column: 9, source: ngUrl}); }); @@ -170,7 +172,7 @@ describe('compiler (unbundled Angular)', () => { const genFile = compileApp(); const genSource = toTypeScript(genFile); - const sourceMap = extractSourceMap(genSource) !; + const sourceMap = extractSourceMap(genSource)!; expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`))) .toEqual({line: 2, column: 9, source: ngUrl}); }); @@ -180,7 +182,7 @@ describe('compiler (unbundled Angular)', () => { const genFile = compileApp(); const genSource = toTypeScript(genFile); - const sourceMap = extractSourceMap(genSource) !; + const sourceMap = extractSourceMap(genSource)!; expect(originalPositionFor(sourceMap, {line: 1, column: 0})) .toEqual({line: 1, column: 0, source: ngFactoryPath}); }); @@ -205,7 +207,6 @@ describe('compiler (unbundled Angular)', () => { compile([FILES, angularFiles]); expect(warnSpy).toHaveBeenCalledWith( `Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v6.x`); - }); it('should error if not all arguments of an @Injectable class can be resolved if strictInjectionParameters is true', @@ -279,7 +280,7 @@ describe('compiler (unbundled Angular)', () => { }; compile([FILES, angularFiles], { postCompile: program => { - const factorySource = program.getSourceFile('/app/app.ngfactory.ts') !; + const factorySource = program.getSourceFile('/app/app.ngfactory.ts')!; expect(factorySource.text).not.toContain('\'/app/app.ngfactory\''); } }); @@ -321,7 +322,7 @@ describe('compiler (unbundled Angular)', () => { const genFilePreamble = '/* Hello world! */'; const {genFiles} = compile([FILES, angularFiles]); const genFile = - genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts')) !; + genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'))!; const genSource = toTypeScript(genFile, genFilePreamble); expect(genSource.startsWith(genFilePreamble)).toBe(true); }); @@ -445,9 +446,9 @@ describe('compiler (unbundled Angular)', () => { } }; const {genFiles} = compile([FILES, angularFiles]); - const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts') !; + const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts')!; const genSource = toTypeScript(genFile); - const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, ''); + const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource)![0].replace(/\s*/g, ''); // selector expect(createComponentFactoryCall).toContain('my-comp'); // inputs @@ -476,10 +477,9 @@ describe('compiler (unbundled Angular)', () => { }; const {genFiles} = compile([FILES, angularFiles]); const genFile = - genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts')) !; + genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'))!; const genSource = toTypeScript(genFile); expect(genSource).not.toContain('check('); - }); }); @@ -492,7 +492,6 @@ describe('compiler (unbundled Angular)', () => { inheritanceWithSummariesSpecs(() => angularSummaryFiles); describe('external symbol re-exports enabled', () => { - it('should not reexport type symbols mentioned in constructors', () => { const libInput: MockDirectory = { 'lib': { @@ -520,7 +519,7 @@ describe('compiler (unbundled Angular)', () => { const {genFiles: appGenFiles} = compile( [appInput, libOutDir, angularSummaryFiles], {useSummaries: true, createExternalSymbolFactoryReexports: true}); - const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts') !; + const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts')!; const appNgFactoryTs = toTypeScript(appNgFactory); expect(appNgFactoryTs).not.toContain('AType'); expect(appNgFactoryTs).toContain('AValue'); @@ -570,7 +569,7 @@ describe('compiler (unbundled Angular)', () => { const {genFiles: appGenFiles} = compile( [appInput, libOutDir, angularSummaryFiles], {useSummaries: true, createExternalSymbolFactoryReexports: true}); - const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts') !; + const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts')!; const appNgFactoryTs = toTypeScript(appNgFactory); // metadata of ctor calls is preserved, so we reexport the argument @@ -614,7 +613,7 @@ describe('compiler (unbundled Angular)', () => { const {genFiles: appGenFiles} = compile( [appInput, libOutDir, angularSummaryFiles], {useSummaries: true, createExternalSymbolFactoryReexports: true}); - const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts') !; + const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts')!; const appNgFactoryTs = toTypeScript(appNgFactory); // we don't need to reexport exported symbols via the .ngfactory @@ -707,7 +706,7 @@ describe('compiler (unbundled Angular)', () => { compile([libInput, getAngularSummaryFiles()], {useSummaries: true}); const {genFiles} = compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true}); - const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts') !; + const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts')!; const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy; expect(toTypeScript(mainNgFactory)) .toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`); @@ -761,7 +760,7 @@ describe('compiler (unbundled Angular)', () => { const {genFiles} = compile( [lib1OutDir, lib2OutDir, appInput, getAngularSummaryFiles()], {useSummaries: true}); - const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts') !; + const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts')!; const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy; const mainNgFactorySource = toTypeScript(mainNgFactory); expect(mainNgFactorySource).toContain(`import * as i2 from '/lib1/base';`); diff --git a/packages/compiler/test/aot/jit_summaries_spec.ts b/packages/compiler/test/aot/jit_summaries_spec.ts index 08bbdd27df737..52ca5d290d73a 100644 --- a/packages/compiler/test/aot/jit_summaries_spec.ts +++ b/packages/compiler/test/aot/jit_summaries_spec.ts @@ -8,7 +8,7 @@ import {AotCompiler, AotCompilerHost, AotCompilerOptions, CompileSummaryKind, GeneratedFile, toTypeScript} from '@angular/compiler'; -import {MockDirectory, compile, setup} from './test_util'; +import {compile, MockDirectory, setup} from './test_util'; describe('aot summaries for jit', () => { let angularFiles = setup(); @@ -19,7 +19,7 @@ describe('aot summaries for jit', () => { }); function compileApp( - rootDir: MockDirectory, options: {useSummaries?: boolean}& AotCompilerOptions = {}): + rootDir: MockDirectory, options: {useSummaries?: boolean}&AotCompilerOptions = {}): {genFiles: GeneratedFile[], outDir: MockDirectory} { return compile( [rootDir, options.useSummaries ? angularSummaryFiles : angularFiles], @@ -42,7 +42,7 @@ describe('aot summaries for jit', () => { const rootDir = {'app': appDir}; const genFile = - compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !; + compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts')!; const genSource = toTypeScript(genFile); expect(genSource).toContain(`import * as i0 from '/app/app.module'`); @@ -71,7 +71,7 @@ describe('aot summaries for jit', () => { const rootDir = {'app': appDir}; const genFile = - compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !; + compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts')!; const genSource = toTypeScript(genFile); expect(genSource).toContain(`import * as i0 from '/app/app.module'`); @@ -100,7 +100,7 @@ describe('aot summaries for jit', () => { const rootDir = {'app': appDir}; const genFile = - compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !; + compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts')!; const genSource = toTypeScript(genFile); expect(genSource).toContain(`import * as i0 from '/app/app.module'`); @@ -126,7 +126,7 @@ describe('aot summaries for jit', () => { const rootDir = {'app': appDir}; const genFile = - compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !; + compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts')!; const genSource = toTypeScript(genFile); expect(genSource).toContain(`import * as i0 from '/app/app.module'`); @@ -165,7 +165,7 @@ describe('aot summaries for jit', () => { const rootDir = {'app': appDir}; const genFile = - compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !; + compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts')!; const genSource = toTypeScript(genFile); expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/); @@ -199,7 +199,7 @@ describe('aot summaries for jit', () => { const rootDir = {'app': appDir}; const genFile = - compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !; + compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts')!; const genSource = toTypeScript(genFile); expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/); @@ -226,7 +226,7 @@ describe('aot summaries for jit', () => { const rootDir = {'app': appDir}; const genFile = - compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !; + compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts')!; const genSource = toTypeScript(genFile); expect(genSource).toMatch( @@ -248,7 +248,7 @@ describe('aot summaries for jit', () => { const rootDir = {'app': appDir}; const genFile = - compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts') !; + compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts')!; const genSource = toTypeScript(genFile); expect(genSource).toMatch( @@ -301,10 +301,9 @@ describe('aot summaries for jit', () => { createExternalSymbolFactoryReexports: true, }); - const lib2ModuleNgSummary = - lib2Gen.find(f => f.genFileUrl === '/lib2/module.ngsummary.ts') !; + const lib2ModuleNgSummary = lib2Gen.find(f => f.genFileUrl === '/lib2/module.ngsummary.ts')!; const lib2ReexportNgSummary = - lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts') !; + lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts')!; // ngsummaries should add reexports for imported NgModules from a direct dependency expect(toTypeScript(lib2ModuleNgSummary)) @@ -336,10 +335,9 @@ describe('aot summaries for jit', () => { useSummaries: true, createExternalSymbolFactoryReexports: true }).genFiles; - const lib3ModuleNgSummary = - lib3Gen.find(f => f.genFileUrl === '/lib3/module.ngsummary.ts') !; + const lib3ModuleNgSummary = lib3Gen.find(f => f.genFileUrl === '/lib3/module.ngsummary.ts')!; const lib3ReexportNgSummary = - lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts') !; + lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts')!; // ngsummary.ts files should use the reexported values from direct and deep deps const lib3ModuleNgSummarySource = toTypeScript(lib3ModuleNgSummary); @@ -398,9 +396,9 @@ describe('aot summaries for jit', () => { const {outDir: lib2Out, genFiles: lib2Gen} = compileApp(lib2In, {useSummaries: true}); - const lib2ModuleNgSummary = lib2Gen.find(f => f.genFileUrl === '/lib2/module.ngsummary.ts') !; + const lib2ModuleNgSummary = lib2Gen.find(f => f.genFileUrl === '/lib2/module.ngsummary.ts')!; const lib2ReexportNgSummary = - lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts') !; + lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts')!; // ngsummaries should not add reexports by default for imported NgModules from a direct // dependency @@ -435,9 +433,9 @@ describe('aot summaries for jit', () => { }; const lib3Gen = compileApp(lib3In, {useSummaries: true}).genFiles; - const lib3ModuleNgSummary = lib3Gen.find(f => f.genFileUrl === '/lib3/module.ngsummary.ts') !; + const lib3ModuleNgSummary = lib3Gen.find(f => f.genFileUrl === '/lib3/module.ngsummary.ts')!; const lib3ReexportNgSummary = - lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts') !; + lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts')!; // ngsummary.ts files should use the external symbols which are manually re-exported from // "lib2" from their original symbol location. With re-exported external symbols this would diff --git a/packages/compiler/test/aot/regression_spec.ts b/packages/compiler/test/aot/regression_spec.ts index 8c768559a133a..d7e74946db371 100644 --- a/packages/compiler/test/aot/regression_spec.ts +++ b/packages/compiler/test/aot/regression_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {MockDirectory, compile, expectNoDiagnostics, setup} from './test_util'; +import {compile, expectNoDiagnostics, MockDirectory, setup} from './test_util'; describe('regressions', () => { let angularFiles = setup(); diff --git a/packages/compiler/test/aot/static_reflector_spec.ts b/packages/compiler/test/aot/static_reflector_spec.ts index ced6e99bf1525..5f382c0e17ddf 100644 --- a/packages/compiler/test/aot/static_reflector_spec.ts +++ b/packages/compiler/test/aot/static_reflector_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, core as compilerCore} from '@angular/compiler'; +import {core as compilerCore, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler'; import {CollectorOptions, METADATA_VERSION} from '@angular/compiler-cli'; import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec'; @@ -358,7 +358,7 @@ describe('StaticReflector', () => { it('should record data about the error in the exception', () => { let threw = false; try { - const metadata = host.getMetadataFor('/tmp/src/invalid-metadata.ts') !; + const metadata = host.getMetadataFor('/tmp/src/invalid-metadata.ts')!; expect(metadata).toBeDefined(); const moduleMetadata: any = metadata[0]['metadata']; expect(moduleMetadata).toBeDefined(); @@ -1334,10 +1334,9 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = { 'decorators': [{ '__symbolic': 'call', 'expression': {'__symbolic': 'reference', 'name': 'Directive', 'module': '@angular/core'}, - 'arguments': [{ - 'selector': '[ngFor][ngForOf]', - 'inputs': ['ngForTrackBy', 'ngForOf', 'ngForTemplate'] - }] + 'arguments': [ + {'selector': '[ngFor][ngForOf]', 'inputs': ['ngForTrackBy', 'ngForOf', 'ngForTemplate']} + ] }], 'members': { '__ctor__': [{ @@ -1345,11 +1344,8 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = { 'parameters': [ {'__symbolic': 'reference', 'module': '@angular/core', 'name': 'ViewContainerRef'}, {'__symbolic': 'reference', 'module': '@angular/core', 'name': 'TemplateRef'}, - {'__symbolic': 'reference', 'module': '@angular/core', 'name': 'IterableDiffers'}, { - '__symbolic': 'reference', - 'module': '@angular/core', - 'name': 'ChangeDetectorRef' - } + {'__symbolic': 'reference', 'module': '@angular/core', 'name': 'IterableDiffers'}, + {'__symbolic': 'reference', 'module': '@angular/core', 'name': 'ChangeDetectorRef'} ] }] } @@ -1387,8 +1383,7 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = { '__symbolic': 'property', 'decorators': [{ '__symbolic': 'call', - 'expression': - {'__symbolic': 'reference', 'name': 'Input', 'module': '@angular/core'} + 'expression': {'__symbolic': 'reference', 'name': 'Input', 'module': '@angular/core'} }] }], 'onMouseOver': [{ diff --git a/packages/compiler/test/aot/static_symbol_resolver_spec.ts b/packages/compiler/test/aot/static_symbol_resolver_spec.ts index 112adcc680cd6..3e0edd7d70cae 100644 --- a/packages/compiler/test/aot/static_symbol_resolver_spec.ts +++ b/packages/compiler/test/aot/static_symbol_resolver_spec.ts @@ -19,7 +19,9 @@ describe('StaticSymbolResolver', () => { let symbolResolver: StaticSymbolResolver; let symbolCache: StaticSymbolCache; - beforeEach(() => { symbolCache = new StaticSymbolCache(); }); + beforeEach(() => { + symbolCache = new StaticSymbolCache(); + }); function init( testData: {[key: string]: any} = DEFAULT_TEST_DATA, summaries: Summary<StaticSymbol>[] = [], @@ -36,7 +38,8 @@ describe('StaticSymbolResolver', () => { () => symbolResolver.resolveSymbol( symbolResolver.getSymbolByModule('src/version-error', 'e'))) .toThrow(new Error( - `Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected ${METADATA_VERSION}`)); + `Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected ${ + METADATA_VERSION}`)); }); it('should throw an exception for version 2 metadata', () => { @@ -159,7 +162,6 @@ describe('StaticSymbolResolver', () => { }); describe('importAs', () => { - it('should calculate importAs relationship for non source files without summaries', () => { init( { @@ -241,7 +243,6 @@ describe('StaticSymbolResolver', () => { expect(symbolResolver.getImportAs(symbolCache.get('/test2.d.ts', 'a', ['someMember']))) .toBe(symbolCache.get('/test3.d.ts', 'b', ['someMember'])); }); - }); it('should replace references by StaticSymbols', () => { @@ -345,10 +346,9 @@ describe('StaticSymbolResolver', () => { __symbolic: 'class', arity: 1, members: { - __ctor__: [{ - __symbolic: 'constructor', - parameters: [symbolCache.get('/test.d.ts', 'AParam')] - }] + __ctor__: [ + {__symbolic: 'constructor', parameters: [symbolCache.get('/test.d.ts', 'AParam')]} + ] } } } @@ -423,7 +423,6 @@ describe('StaticSymbolResolver', () => { expect(symbol.name).toEqual('One'); expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts'); }); - }); export class MockSummaryResolver implements SummaryResolver<StaticSymbol> { @@ -431,9 +430,11 @@ export class MockSummaryResolver implements SummaryResolver<StaticSymbol> { symbol: StaticSymbol, importAs: StaticSymbol }[] = []) {} - addSummary(summary: Summary<StaticSymbol>) { this.summaries.push(summary); } + addSummary(summary: Summary<StaticSymbol>) { + this.summaries.push(summary); + } resolveSummary(reference: StaticSymbol): Summary<StaticSymbol> { - return this.summaries.find(summary => summary.symbol === reference) !; + return this.summaries.find(summary => summary.symbol === reference)!; } getSymbolsOf(filePath: string): StaticSymbol[]|null { const symbols = this.summaries.filter(summary => summary.symbol.filePath === filePath) @@ -442,12 +443,20 @@ export class MockSummaryResolver implements SummaryResolver<StaticSymbol> { } getImportAs(symbol: StaticSymbol): StaticSymbol { const entry = this.importAs.find(entry => entry.symbol === symbol); - return entry ? entry.importAs : undefined !; + return entry ? entry.importAs : undefined!; + } + getKnownModuleName(fileName: string): string|null { + return null; + } + isLibraryFile(filePath: string): boolean { + return filePath.endsWith('.d.ts'); + } + toSummaryFileName(filePath: string): string { + return filePath.replace(/(\.d)?\.ts$/, '.d.ts'); + } + fromSummaryFileName(filePath: string): string { + return filePath; } - getKnownModuleName(fileName: string): string|null { return null; } - isLibraryFile(filePath: string): boolean { return filePath.endsWith('.d.ts'); } - toSummaryFileName(filePath: string): string { return filePath.replace(/(\.d)?\.ts$/, '.d.ts'); } - fromSummaryFileName(filePath: string): string { return filePath; } } export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost { @@ -459,7 +468,9 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost { // In tests, assume that symbols are not re-exported moduleNameToFileName(modulePath: string, containingFile?: string): string { - function splitPath(path: string): string[] { return path.split(/\/|\\/g); } + function splitPath(path: string): string[] { + return path.split(/\/|\\/g); + } function resolvePath(pathParts: string[]): string { const result: string[] = []; @@ -490,7 +501,7 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost { } if (modulePath.indexOf('.') === 0) { - const baseName = pathTo(containingFile !, modulePath); + const baseName = pathTo(containingFile!, modulePath); const tsName = baseName + '.ts'; if (this._getMetadataFor(tsName)) { return tsName; @@ -498,14 +509,18 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost { return baseName + '.d.ts'; } if (modulePath == 'unresolved') { - return undefined !; + return undefined!; } return '/tmp/' + modulePath + '.d.ts'; } - getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); } + getMetadataFor(moduleId: string): any { + return this._getMetadataFor(moduleId); + } - getOutputName(filePath: string): string { return filePath; } + getOutputName(filePath: string): string { + return filePath; + } private _getMetadataFor(filePath: string): any { if (this.data[filePath] && filePath.match(TS_EXT)) { @@ -515,13 +530,13 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost { filePath, this.data[filePath], ts.ScriptTarget.ES5, /* setParentNodes */ true); const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics; if (diagnostics && diagnostics.length) { - const errors = - diagnostics - .map(d => { - const {line, character} = ts.getLineAndCharacterOfPosition(d.file !, d.start !); - return `(${line}:${character}): ${d.messageText}`; - }) - .join('\n'); + const errors = diagnostics + .map(d => { + const {line, character} = + ts.getLineAndCharacterOfPosition(d.file!, d.start!); + return `(${line}:${character}): ${d.messageText}`; + }) + .join('\n'); throw Error(`Error encountered during parse of file ${filePath}\n${errors}`); } return [this.collector.getMetadata(sf)]; diff --git a/packages/compiler/test/aot/summary_resolver_spec.ts b/packages/compiler/test/aot/summary_resolver_spec.ts index d518319c2d5b0..b4e416b7e25cd 100644 --- a/packages/compiler/test/aot/summary_resolver_spec.ts +++ b/packages/compiler/test/aot/summary_resolver_spec.ts @@ -23,7 +23,9 @@ const EXT = /(\.d)?\.ts$/; let symbolCache: StaticSymbolCache; let host: MockAotSummaryResolverHost; - beforeEach(() => { symbolCache = new StaticSymbolCache(); }); + beforeEach(() => { + symbolCache = new StaticSymbolCache(); + }); function init(summaries: {[filePath: string]: string} = {}) { host = new MockAotSummaryResolverHost(summaries); @@ -121,11 +123,17 @@ export class MockAotSummaryResolverHost implements AotSummaryResolverHost { return sourceFileName.replace(EXT, '') + '.d.ts'; } - fromSummaryFileName(filePath: string): string { return filePath; } + fromSummaryFileName(filePath: string): string { + return filePath; + } - isSourceFile(filePath: string) { return !filePath.endsWith('.d.ts'); } + isSourceFile(filePath: string) { + return !filePath.endsWith('.d.ts'); + } - loadSummary(filePath: string): string { return this.summaries[filePath]; } + loadSummary(filePath: string): string { + return this.summaries[filePath]; + } } export function createMockOutputContext(): OutputContext { diff --git a/packages/compiler/test/aot/summary_serializer_spec.ts b/packages/compiler/test/aot/summary_serializer_spec.ts index 3410c609217fa..fb9bc5711157e 100644 --- a/packages/compiler/test/aot/summary_serializer_spec.ts +++ b/packages/compiler/test/aot/summary_serializer_spec.ts @@ -12,7 +12,7 @@ import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/ao import {summaryFileName} from '@angular/compiler/src/aot/util'; import {MockStaticSymbolResolverHost} from './static_symbol_resolver_spec'; -import {MockAotSummaryResolverHost, createMockOutputContext} from './summary_resolver_spec'; +import {createMockOutputContext, MockAotSummaryResolverHost} from './summary_resolver_spec'; { @@ -22,7 +22,9 @@ import {MockAotSummaryResolverHost, createMockOutputContext} from './summary_res let symbolCache: StaticSymbolCache; let host: MockAotSummaryResolverHost; - beforeEach(() => { symbolCache = new StaticSymbolCache(); }); + beforeEach(() => { + symbolCache = new StaticSymbolCache(); + }); function init( summaries: {[filePath: string]: string} = {}, metadata: {[key: string]: any} = {}) { @@ -101,7 +103,7 @@ import {MockAotSummaryResolverHost, createMockOutputContext} from './summary_res members: {aMethod: {__symbolic: 'function'}}, statics: {aStatic: true} }); - expect(summaries[1].type !.type.reference) + expect(summaries[1].type!.type.reference) .toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService')); }); @@ -274,7 +276,7 @@ import {MockAotSummaryResolverHost, createMockOutputContext} from './summary_res '/tmp/external_svc.d.ts', 'SomeService')]); // SomService is a transitive dep, but should have been serialized as well. expect(summaries[2].symbol).toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService')); - expect(summaries[2].type !.type.reference) + expect(summaries[2].type!.type.reference) .toBe(symbolCache.get('/tmp/external_svc.d.ts', 'SomeService')); // there was no summary for non_summary, but it should have // been serialized as well. @@ -387,7 +389,6 @@ import {MockAotSummaryResolverHost, createMockOutputContext} from './summary_res describe('symbol re-exports enabled', () => { - it('should not create "importAs" names for ctor arguments which are types of reexported classes in libraries', () => { init(); diff --git a/packages/compiler/test/aot/test_util.ts b/packages/compiler/test/aot/test_util.ts index f516d6f300416..112deff7e0dc2 100644 --- a/packages/compiler/test/aot/test_util.ts +++ b/packages/compiler/test/aot/test_util.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler, toTypeScript} from '@angular/compiler'; +import {AotCompilerHost, AotCompilerOptions, createAotCompiler, GeneratedFile, toTypeScript} from '@angular/compiler'; import {MetadataBundlerHost} from '@angular/compiler-cli/src/metadata/bundler'; import {MetadataCollector} from '@angular/compiler-cli/src/metadata/collector'; import {ModuleMetadata} from '@angular/compiler-cli/src/metadata/index'; @@ -15,7 +15,9 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; -export interface MetadataProvider { getMetadata(source: ts.SourceFile): ModuleMetadata|undefined; } +export interface MetadataProvider { + getMetadata(source: ts.SourceFile): ModuleMetadata|undefined; +} let nodeModulesPath: string; let angularSourcePath: string; @@ -23,13 +25,13 @@ let rootPath: string; calcPathsOnDisc(); -export type MockFileOrDirectory = string | MockDirectory; +export type MockFileOrDirectory = string|MockDirectory; export type MockDirectory = { - [name: string]: MockFileOrDirectory | undefined; + [name: string]: MockFileOrDirectory|undefined; }; -export function isDirectory(data: MockFileOrDirectory | undefined): data is MockDirectory { +export function isDirectory(data: MockFileOrDirectory|undefined): data is MockDirectory { return typeof data !== 'string'; } @@ -119,9 +121,13 @@ export class EmittingCompilerHost implements ts.CompilerHost { return Array.from(this.writtenFiles).map(f => ({name: f[0], content: f[1]})); } - public get scripts(): string[] { return this.scriptNames; } + public get scripts(): string[] { + return this.scriptNames; + } - public get written(): Map<string, string> { return this.writtenFiles; } + public get written(): Map<string, string> { + return this.writtenFiles; + } public effectiveName(fileName: string): string { const prefix = '@angular/'; @@ -154,7 +160,9 @@ export class EmittingCompilerHost implements ts.CompilerHost { (fs.existsSync(directoryName) && fs.statSync(directoryName).isDirectory()); } - getCurrentDirectory(): string { return this.root; } + getCurrentDirectory(): string { + return this.root; + } getDirectories(dir: string): string[] { const result = open(dir, this.options.mockData); @@ -179,7 +187,9 @@ export class EmittingCompilerHost implements ts.CompilerHost { throw new Error(`File not found '${fileName}'.`); } - getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'lib.d.ts'; + } writeFile: ts.WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean, @@ -197,8 +207,12 @@ export class EmittingCompilerHost implements ts.CompilerHost { getCanonicalFileName(fileName: string): string { return fileName; } - useCaseSensitiveFileNames(): boolean { return false; } - getNewLine(): string { return '\n'; } + useCaseSensitiveFileNames(): boolean { + return false; + } + getNewLine(): string { + return '\n'; + } private getAddedDirectories(): Set<string> { let result = this.cachedAddedDirectories; @@ -247,7 +261,9 @@ export class MockCompilerHost implements ts.CompilerHost { this.sourceFiles.delete(fileName); } - assumeFileExists(fileName: string) { this.assumeExists.add(fileName); } + assumeFileExists(fileName: string) { + this.assumeExists.add(fileName); + } remove(files: string[]) { // Remove the files from the list of scripts. @@ -274,11 +290,17 @@ export class MockCompilerHost implements ts.CompilerHost { return false; } - readFile(fileName: string): string { return this.getFileContent(fileName) !; } + readFile(fileName: string): string { + return this.getFileContent(fileName)!; + } - trace(s: string): void { this.traces.push(s); } + trace(s: string): void { + this.traces.push(s); + } - getCurrentDirectory(): string { return '/'; } + getCurrentDirectory(): string { + return '/'; + } getDirectories(dir: string): string[] { const effectiveName = this.getEffectiveName(dir); @@ -303,10 +325,12 @@ export class MockCompilerHost implements ts.CompilerHost { this.sourceFiles.set(fileName, result); } } - return result !; + return result!; } - getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'lib.d.ts'; + } writeFile: ts.WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean) => { @@ -317,8 +341,12 @@ export class MockCompilerHost implements ts.CompilerHost { getCanonicalFileName(fileName: string): string { return fileName; } - useCaseSensitiveFileNames(): boolean { return false; } - getNewLine(): string { return '\n'; } + useCaseSensitiveFileNames(): boolean { + return false; + } + getNewLine(): string { + return '\n'; + } // Private methods private getFileContent(fileName: string): string|undefined { @@ -373,9 +401,13 @@ export class MockAotCompilerHost implements AotCompilerHost { }; } - hideMetadata() { this.metadataVisible = false; } + hideMetadata() { + this.metadataVisible = false; + } - tsFilesOnly() { this.dtsAreSource = false; } + tsFilesOnly() { + this.dtsAreSource = false; + } // StaticSymbolResolverHost getMetadataFor(modulePath: string): {[key: string]: any}[]|undefined { @@ -414,7 +446,9 @@ export class MockAotCompilerHost implements AotCompilerHost { return resolved ? resolved.resolvedFileName : null; } - getOutputName(filePath: string) { return filePath; } + getOutputName(filePath: string) { + return filePath; + } resourceNameToFileName(resourceName: string, containingFile: string) { // Note: we convert package paths into relative paths to be compatible with the the @@ -428,16 +462,22 @@ export class MockAotCompilerHost implements AotCompilerHost { } // AotSummaryResolverHost - loadSummary(filePath: string): string|null { return this.tsHost.readFile(filePath); } + loadSummary(filePath: string): string|null { + return this.tsHost.readFile(filePath); + } isSourceFile(sourceFilePath: string): boolean { return !GENERATED_FILES.test(sourceFilePath) && (this.dtsAreSource || !DTS.test(sourceFilePath)); } - toSummaryFileName(filePath: string): string { return filePath.replace(EXT, '') + '.d.ts'; } + toSummaryFileName(filePath: string): string { + return filePath.replace(EXT, '') + '.d.ts'; + } - fromSummaryFileName(filePath: string): string { return filePath; } + fromSummaryFileName(filePath: string): string { + return filePath; + } // AotCompilerHost fileNameToModuleName(importedFile: string, containingFile: string): string { @@ -464,7 +504,7 @@ export class MockMetadataBundlerHost implements MetadataBundlerHost { } } -function find(fileName: string, data: MockFileOrDirectory | undefined): MockFileOrDirectory| +function find(fileName: string, data: MockFileOrDirectory|undefined): MockFileOrDirectory| undefined { if (!data) return undefined; const names = fileName.split('/'); @@ -479,7 +519,7 @@ function find(fileName: string, data: MockFileOrDirectory | undefined): MockFile return current; } -function open(fileName: string, data: MockFileOrDirectory | undefined): string|undefined { +function open(fileName: string, data: MockFileOrDirectory|undefined): string|undefined { let result = find(fileName, data); if (typeof result === 'string') { return result; @@ -487,7 +527,7 @@ function open(fileName: string, data: MockFileOrDirectory | undefined): string|u return undefined; } -function directoryExists(dirname: string, data: MockFileOrDirectory | undefined): boolean { +function directoryExists(dirname: string, data: MockFileOrDirectory|undefined): boolean { let result = find(dirname, data); return !!result && typeof result !== 'string'; } @@ -497,7 +537,7 @@ export type MockFileArray = { content: string }[]; -export type MockData = MockDirectory | Map<string, string>| (MockDirectory | Map<string, string>)[]; +export type MockData = MockDirectory|Map<string, string>|(MockDirectory|Map<string, string>)[]; export function toMockFileArray(data: MockData, target: MockFileArray = []): MockFileArray { if (data instanceof Map) { @@ -512,7 +552,7 @@ export function toMockFileArray(data: MockData, target: MockFileArray = []): Moc function mockDirToFileArray(dir: MockDirectory, path: string, target: MockFileArray) { Object.keys(dir).forEach((localFileName) => { - const value = dir[localFileName] !; + const value = dir[localFileName]!; const fileName = `${path}/${localFileName}`; if (typeof value === 'string') { target.push({fileName, content: value}); @@ -523,12 +563,16 @@ function mockDirToFileArray(dir: MockDirectory, path: string, target: MockFileAr } function mapToMockFileArray(files: Map<string, string>, target: MockFileArray) { - files.forEach((content, fileName) => { target.push({fileName, content}); }); + files.forEach((content, fileName) => { + target.push({fileName, content}); + }); } export function arrayToMockMap(arr: MockFileArray): Map<string, string> { const map = new Map<string, string>(); - arr.forEach(({fileName, content}) => { map.set(fileName, content); }); + arr.forEach(({fileName, content}) => { + map.set(fileName, content); + }); return map; } @@ -594,8 +638,8 @@ function readBazelWrittenFilesFrom( map.set(path.posix.join('/node_modules/@angular', packageName, 'index.d.ts'), content); } } catch (e) { - console.error( - `Consider adding //packages/${packageName} as a data dependency in the BUILD.bazel rule for the failing test`); + console.error(`Consider adding //packages/${ + packageName} as a data dependency in the BUILD.bazel rule for the failing test`); throw e; } } @@ -606,8 +650,8 @@ export function isInBazel(): boolean { export function setup(options: { compileAngular: boolean, - compileFakeCore?: boolean, - compileAnimations: boolean, compileCommon?: boolean + compileFakeCore?: boolean, compileAnimations: boolean, + compileCommon?: boolean } = { compileAngular: true, compileAnimations: true, @@ -687,7 +731,9 @@ export function expectNoDiagnostics(program: ts.Program) { return ''; } - function chars(len: number, ch: string): string { return newArray(len, ch).join(''); } + function chars(len: number, ch: string): string { + return newArray(len, ch).join(''); + } function lineNoOf(offset: number, text: string): number { let result = 1; @@ -699,8 +745,8 @@ export function expectNoDiagnostics(program: ts.Program) { function lineInfo(diagnostic: ts.Diagnostic): string { if (diagnostic.file) { - const start = diagnostic.start !; - let end = diagnostic.start ! + diagnostic.length !; + const start = diagnostic.start!; + let end = diagnostic.start! + diagnostic.length!; const source = diagnostic.file.text; let lineStart = start; let lineEnd = end; @@ -726,8 +772,8 @@ export function expectNoDiagnostics(program: ts.Program) { 'Errors from TypeScript:\n' + diagnostics .map( - d => - `${fileInfo(d)}${ts.flattenDiagnosticMessageText(d.messageText, '\n')}${lineInfo(d)}`) + d => `${fileInfo(d)}${ts.flattenDiagnosticMessageText(d.messageText, '\n')}${ + lineInfo(d)}`) .join(' \n')); } } @@ -758,7 +804,7 @@ export function compile( useSummaries?: boolean, preCompile?: (program: ts.Program) => void, postCompile?: (program: ts.Program) => void, - }& AotCompilerOptions = {}, + }&AotCompilerOptions = {}, tsOptions: ts.CompilerOptions = {}): {genFiles: GeneratedFile[], outDir: MockDirectory} { // when using summaries, always emit so the next step can use the results. const emit = options.emit || options.useSummaries; @@ -777,7 +823,9 @@ export function compile( const tsSettings = {...settings, ...tsOptions}; const program = ts.createProgram([...host.scriptNames], tsSettings, host); preCompile(program); - const {compiler, reflector} = createAotCompiler(aotHost, options, (err) => { throw err; }); + const {compiler, reflector} = createAotCompiler(aotHost, options, (err) => { + throw err; + }); const analyzedModules = compiler.analyzeModulesSync(program.getSourceFiles().map(sf => sf.fileName)); const genFiles = compiler.emitAllImpls(analyzedModules); diff --git a/packages/compiler/test/compiler_facade_interface_spec.ts b/packages/compiler/test/compiler_facade_interface_spec.ts index dcb97afd31932..fd58bc9d91c6e 100644 --- a/packages/compiler/test/compiler_facade_interface_spec.ts +++ b/packages/compiler/test/compiler_facade_interface_spec.ts @@ -26,87 +26,87 @@ import * as compiler from '../src/compiler_facade_interface'; */ const coreExportedCompilerFacade1: core.ExportedCompilerFacade = - null !as compiler.ExportedCompilerFacade; + null! as compiler.ExportedCompilerFacade; const compilerExportedCompilerFacade2: compiler.ExportedCompilerFacade = - null !as core.ExportedCompilerFacade; + null! as core.ExportedCompilerFacade; -const coreCompilerFacade: core.CompilerFacade = null !as compiler.CompilerFacade; -const compilerCompilerFacade: compiler.CompilerFacade = null !as core.CompilerFacade; +const coreCompilerFacade: core.CompilerFacade = null! as compiler.CompilerFacade; +const compilerCompilerFacade: compiler.CompilerFacade = null! as core.CompilerFacade; -const coreCoreEnvironment: core.CoreEnvironment = null !as compiler.CoreEnvironment; -const compilerCoreEnvironment: compiler.CoreEnvironment = null !as core.CoreEnvironment; +const coreCoreEnvironment: core.CoreEnvironment = null! as compiler.CoreEnvironment; +const compilerCoreEnvironment: compiler.CoreEnvironment = null! as core.CoreEnvironment; -const coreResourceLoader: core.ResourceLoader = null !as compiler.ResourceLoader; -const compilerResourceLoader: compiler.ResourceLoader = null !as core.ResourceLoader; +const coreResourceLoader: core.ResourceLoader = null! as compiler.ResourceLoader; +const compilerResourceLoader: compiler.ResourceLoader = null! as core.ResourceLoader; -const coreStringMap: core.StringMap = null !as compiler.StringMap; -const compilerStringMap: compiler.StringMap = null !as core.StringMap; +const coreStringMap: core.StringMap = null! as compiler.StringMap; +const compilerStringMap: compiler.StringMap = null! as core.StringMap; -const coreProvider: core.Provider = null !as compiler.Provider; -const compilerProvider: compiler.Provider = null !as core.Provider; +const coreProvider: core.Provider = null! as compiler.Provider; +const compilerProvider: compiler.Provider = null! as core.Provider; const coreR3ResolvedDependencyType: core.R3ResolvedDependencyType = - null !as compiler.R3ResolvedDependencyType; + null! as compiler.R3ResolvedDependencyType; const compilerR3ResolvedDependencyType: compiler.R3ResolvedDependencyType = - null !as core.R3ResolvedDependencyType; + null! as core.R3ResolvedDependencyType; const coreR3ResolvedDependencyType2: R3ResolvedDependencyType = - null !as core.R3ResolvedDependencyType; + null! as core.R3ResolvedDependencyType; const compilerR3ResolvedDependencyType2: R3ResolvedDependencyType = - null !as core.R3ResolvedDependencyType; + null! as core.R3ResolvedDependencyType; const coreR3ResolvedDependencyType3: core.R3ResolvedDependencyType = - null !as R3ResolvedDependencyType; + null! as R3ResolvedDependencyType; const compilerR3ResolvedDependencyType3: compiler.R3ResolvedDependencyType = - null !as R3ResolvedDependencyType; + null! as R3ResolvedDependencyType; -const coreR3FactoryTarget: core.R3FactoryTarget = null !as compiler.R3FactoryTarget; -const compilerR3FactoryTarget: compiler.R3FactoryTarget = null !as core.R3FactoryTarget; +const coreR3FactoryTarget: core.R3FactoryTarget = null! as compiler.R3FactoryTarget; +const compilerR3FactoryTarget: compiler.R3FactoryTarget = null! as core.R3FactoryTarget; -const coreR3FactoryTarget2: R3FactoryTarget = null !as core.R3FactoryTarget; -const compilerR3FactoryTarget2: R3FactoryTarget = null !as core.R3FactoryTarget; +const coreR3FactoryTarget2: R3FactoryTarget = null! as core.R3FactoryTarget; +const compilerR3FactoryTarget2: R3FactoryTarget = null! as core.R3FactoryTarget; -const coreR3FactoryTarget3: core.R3FactoryTarget = null !as R3FactoryTarget; -const compilerR3FactoryTarget3: compiler.R3FactoryTarget = null !as R3FactoryTarget; +const coreR3FactoryTarget3: core.R3FactoryTarget = null! as R3FactoryTarget; +const compilerR3FactoryTarget3: compiler.R3FactoryTarget = null! as R3FactoryTarget; const coreR3DependencyMetadataFacade: core.R3DependencyMetadataFacade = - null !as compiler.R3DependencyMetadataFacade; + null! as compiler.R3DependencyMetadataFacade; const compilerR3DependencyMetadataFacade: compiler.R3DependencyMetadataFacade = - null !as core.R3DependencyMetadataFacade; + null! as core.R3DependencyMetadataFacade; -const coreR3PipeMetadataFacade: core.R3PipeMetadataFacade = null !as compiler.R3PipeMetadataFacade; +const coreR3PipeMetadataFacade: core.R3PipeMetadataFacade = null! as compiler.R3PipeMetadataFacade; const compilerR3PipeMetadataFacade: compiler.R3PipeMetadataFacade = - null !as core.R3PipeMetadataFacade; + null! as core.R3PipeMetadataFacade; const coreR3InjectableMetadataFacade: core.R3InjectableMetadataFacade = - null !as compiler.R3InjectableMetadataFacade; + null! as compiler.R3InjectableMetadataFacade; const compilerR3InjectableMetadataFacade: compiler.R3InjectableMetadataFacade = - null !as core.R3InjectableMetadataFacade; + null! as core.R3InjectableMetadataFacade; const coreR3NgModuleMetadataFacade: core.R3NgModuleMetadataFacade = - null !as compiler.R3NgModuleMetadataFacade; + null! as compiler.R3NgModuleMetadataFacade; const compilerR3NgModuleMetadataFacade: compiler.R3NgModuleMetadataFacade = - null !as core.R3NgModuleMetadataFacade; + null! as core.R3NgModuleMetadataFacade; const coreR3InjectorMetadataFacade: core.R3InjectorMetadataFacade = - null !as compiler.R3InjectorMetadataFacade; + null! as compiler.R3InjectorMetadataFacade; const compilerR3InjectorMetadataFacade: compiler.R3InjectorMetadataFacade = - null !as core.R3InjectorMetadataFacade; + null! as core.R3InjectorMetadataFacade; const coreR3DirectiveMetadataFacade: core.R3DirectiveMetadataFacade = - null !as compiler.R3DirectiveMetadataFacade; + null! as compiler.R3DirectiveMetadataFacade; const compilerR3DirectiveMetadataFacade: compiler.R3DirectiveMetadataFacade = - null !as core.R3DirectiveMetadataFacade; + null! as core.R3DirectiveMetadataFacade; const coreR3ComponentMetadataFacade: core.R3ComponentMetadataFacade = - null !as compiler.R3ComponentMetadataFacade; + null! as compiler.R3ComponentMetadataFacade; const compilerR3ComponentMetadataFacade: compiler.R3ComponentMetadataFacade = - null !as core.R3ComponentMetadataFacade; + null! as core.R3ComponentMetadataFacade; -const coreViewEncapsulation: core.ViewEncapsulation = null !as compiler.ViewEncapsulation; -const compilerViewEncapsulation: compiler.ViewEncapsulation = null !as core.ViewEncapsulation; +const coreViewEncapsulation: core.ViewEncapsulation = null! as compiler.ViewEncapsulation; +const compilerViewEncapsulation: compiler.ViewEncapsulation = null! as core.ViewEncapsulation; const coreR3QueryMetadataFacade: core.R3QueryMetadataFacade = - null !as compiler.R3QueryMetadataFacade; + null! as compiler.R3QueryMetadataFacade; const compilerR3QueryMetadataFacade: compiler.R3QueryMetadataFacade = - null !as core.R3QueryMetadataFacade; + null! as core.R3QueryMetadataFacade; diff --git a/packages/compiler/test/config_spec.ts b/packages/compiler/test/config_spec.ts index 459e80cf60b11..5126941759011 100644 --- a/packages/compiler/test/config_spec.ts +++ b/packages/compiler/test/config_spec.ts @@ -19,7 +19,9 @@ import {CompilerConfig, preserveWhitespacesDefault} from '../src/config'; describe('preserveWhitespacesDefault', () => { it('should return the default `false` setting when no preserveWhitespacesOption are provided', - () => { expect(preserveWhitespacesDefault(null)).toEqual(false); }); + () => { + expect(preserveWhitespacesDefault(null)).toEqual(false); + }); it('should return the preserveWhitespacesOption when provided as a parameter', () => { expect(preserveWhitespacesDefault(true)).toEqual(true); expect(preserveWhitespacesDefault(false)).toEqual(false); diff --git a/packages/compiler/test/core_spec.ts b/packages/compiler/test/core_spec.ts index f5864584cc5ca..e198db2e8df42 100644 --- a/packages/compiler/test/core_spec.ts +++ b/packages/compiler/test/core_spec.ts @@ -190,7 +190,9 @@ import * as core from '@angular/core'; function compareRuntimeShape(a: any, b: any) { const keys = metadataKeys(a); expect(keys).toEqual(metadataKeys(b)); - keys.forEach(key => { expect(a[key]).toBe(b[key]); }); + keys.forEach(key => { + expect(a[key]).toBe(b[key]); + }); // Need to check 'ngMetadataName' separately, as this is // on the prototype in @angular/core, but a regular property in @angular/compiler. expect(a.ngMetadataName).toBe(b.ngMetadataName); diff --git a/packages/compiler/test/css_parser/css_lexer_spec.ts b/packages/compiler/test/css_parser/css_lexer_spec.ts index 64a44d6c64003..96835c809aceb 100644 --- a/packages/compiler/test/css_parser/css_lexer_spec.ts +++ b/packages/compiler/test/css_parser/css_lexer_spec.ts @@ -7,370 +7,381 @@ */ import {describe, expect, it} from '../../../core/testing/src/testing_internal'; -import {CssLexer, CssLexerMode, CssToken, CssTokenType, cssScannerError, getRawMessage, getToken} from '../../src/css_parser/css_lexer'; +import {CssLexer, CssLexerMode, cssScannerError, CssToken, CssTokenType, getRawMessage, getToken} from '../../src/css_parser/css_lexer'; (function() { - function tokenize( - code: string, trackComments: boolean = false, - mode: CssLexerMode = CssLexerMode.ALL): CssToken[] { - const scanner = new CssLexer().scan(code, trackComments); - scanner.setMode(mode); - - const tokens: CssToken[] = []; - let output = scanner.scan(); - while (output != null) { - const error = output.error; - if (error != null) { - throw cssScannerError(getToken(error), getRawMessage(error)); - } - tokens.push(output.token); - output = scanner.scan(); +function tokenize( + code: string, trackComments: boolean = false, + mode: CssLexerMode = CssLexerMode.ALL): CssToken[] { + const scanner = new CssLexer().scan(code, trackComments); + scanner.setMode(mode); + + const tokens: CssToken[] = []; + let output = scanner.scan(); + while (output != null) { + const error = output.error; + if (error != null) { + throw cssScannerError(getToken(error), getRawMessage(error)); } - - return tokens; + tokens.push(output.token); + output = scanner.scan(); } - describe('CssLexer', () => { - it('should lex newline characters as whitespace when whitespace mode is on', () => { - const newlines = ['\n', '\r\n', '\r', '\f']; - newlines.forEach((line) => { - const token = tokenize(line, false, CssLexerMode.ALL_TRACK_WS)[0]; - expect(token.type).toEqual(CssTokenType.Whitespace); - }); - }); + return tokens; +} - it('should combined newline characters as one newline token when whitespace mode is on', () => { - const newlines = ['\n', '\r\n', '\r', '\f'].join(''); - const tokens = tokenize(newlines, false, CssLexerMode.ALL_TRACK_WS); - expect(tokens.length).toEqual(1); - expect(tokens[0].type).toEqual(CssTokenType.Whitespace); +describe('CssLexer', () => { + it('should lex newline characters as whitespace when whitespace mode is on', () => { + const newlines = ['\n', '\r\n', '\r', '\f']; + newlines.forEach((line) => { + const token = tokenize(line, false, CssLexerMode.ALL_TRACK_WS)[0]; + expect(token.type).toEqual(CssTokenType.Whitespace); }); + }); - it('should not consider whitespace or newline values at all when whitespace mode is off', - () => { - const newlines = ['\n', '\r\n', '\r', '\f'].join(''); - const tokens = tokenize(newlines); - expect(tokens.length).toEqual(0); - }); + it('should combined newline characters as one newline token when whitespace mode is on', () => { + const newlines = ['\n', '\r\n', '\r', '\f'].join(''); + const tokens = tokenize(newlines, false, CssLexerMode.ALL_TRACK_WS); + expect(tokens.length).toEqual(1); + expect(tokens[0].type).toEqual(CssTokenType.Whitespace); + }); - it('should lex simple selectors and their inner properties', () => { - const cssCode = '\n' + - ' .selector { my-prop: my-value; }\n'; - const tokens = tokenize(cssCode); + it('should not consider whitespace or newline values at all when whitespace mode is off', () => { + const newlines = ['\n', '\r\n', '\r', '\f'].join(''); + const tokens = tokenize(newlines); + expect(tokens.length).toEqual(0); + }); - expect(tokens[0].type).toEqual(CssTokenType.Character); - expect(tokens[0].strValue).toEqual('.'); + it('should lex simple selectors and their inner properties', () => { + const cssCode = '\n' + + ' .selector { my-prop: my-value; }\n'; + const tokens = tokenize(cssCode); - expect(tokens[1].type).toEqual(CssTokenType.Identifier); - expect(tokens[1].strValue).toEqual('selector'); + expect(tokens[0].type).toEqual(CssTokenType.Character); + expect(tokens[0].strValue).toEqual('.'); - expect(tokens[2].type).toEqual(CssTokenType.Character); - expect(tokens[2].strValue).toEqual('{'); + expect(tokens[1].type).toEqual(CssTokenType.Identifier); + expect(tokens[1].strValue).toEqual('selector'); - expect(tokens[3].type).toEqual(CssTokenType.Identifier); - expect(tokens[3].strValue).toEqual('my-prop'); + expect(tokens[2].type).toEqual(CssTokenType.Character); + expect(tokens[2].strValue).toEqual('{'); - expect(tokens[4].type).toEqual(CssTokenType.Character); - expect(tokens[4].strValue).toEqual(':'); + expect(tokens[3].type).toEqual(CssTokenType.Identifier); + expect(tokens[3].strValue).toEqual('my-prop'); - expect(tokens[5].type).toEqual(CssTokenType.Identifier); - expect(tokens[5].strValue).toEqual('my-value'); + expect(tokens[4].type).toEqual(CssTokenType.Character); + expect(tokens[4].strValue).toEqual(':'); - expect(tokens[6].type).toEqual(CssTokenType.Character); - expect(tokens[6].strValue).toEqual(';'); + expect(tokens[5].type).toEqual(CssTokenType.Identifier); + expect(tokens[5].strValue).toEqual('my-value'); - expect(tokens[7].type).toEqual(CssTokenType.Character); - expect(tokens[7].strValue).toEqual('}'); - }); + expect(tokens[6].type).toEqual(CssTokenType.Character); + expect(tokens[6].strValue).toEqual(';'); - it('should capture the column and line values for each token', () => { - const cssCode = '#id {\n' + - ' prop:value;\n' + - '}'; - - const tokens = tokenize(cssCode); - - // # - expect(tokens[0].type).toEqual(CssTokenType.Character); - expect(tokens[0].column).toEqual(0); - expect(tokens[0].line).toEqual(0); - - // id - expect(tokens[1].type).toEqual(CssTokenType.Identifier); - expect(tokens[1].column).toEqual(1); - expect(tokens[1].line).toEqual(0); - - // { - expect(tokens[2].type).toEqual(CssTokenType.Character); - expect(tokens[2].column).toEqual(4); - expect(tokens[2].line).toEqual(0); - - // prop - expect(tokens[3].type).toEqual(CssTokenType.Identifier); - expect(tokens[3].column).toEqual(2); - expect(tokens[3].line).toEqual(1); - - // : - expect(tokens[4].type).toEqual(CssTokenType.Character); - expect(tokens[4].column).toEqual(6); - expect(tokens[4].line).toEqual(1); - - // value - expect(tokens[5].type).toEqual(CssTokenType.Identifier); - expect(tokens[5].column).toEqual(7); - expect(tokens[5].line).toEqual(1); - - // ; - expect(tokens[6].type).toEqual(CssTokenType.Character); - expect(tokens[6].column).toEqual(12); - expect(tokens[6].line).toEqual(1); - - // } - expect(tokens[7].type).toEqual(CssTokenType.Character); - expect(tokens[7].column).toEqual(0); - expect(tokens[7].line).toEqual(2); - }); + expect(tokens[7].type).toEqual(CssTokenType.Character); + expect(tokens[7].strValue).toEqual('}'); + }); - it('should lex quoted strings and escape accordingly', () => { - const cssCode = 'prop: \'some { value } \\\' that is quoted\''; - const tokens = tokenize(cssCode); + it('should capture the column and line values for each token', () => { + const cssCode = '#id {\n' + + ' prop:value;\n' + + '}'; + + const tokens = tokenize(cssCode); + + // # + expect(tokens[0].type).toEqual(CssTokenType.Character); + expect(tokens[0].column).toEqual(0); + expect(tokens[0].line).toEqual(0); + + // id + expect(tokens[1].type).toEqual(CssTokenType.Identifier); + expect(tokens[1].column).toEqual(1); + expect(tokens[1].line).toEqual(0); + + // { + expect(tokens[2].type).toEqual(CssTokenType.Character); + expect(tokens[2].column).toEqual(4); + expect(tokens[2].line).toEqual(0); + + // prop + expect(tokens[3].type).toEqual(CssTokenType.Identifier); + expect(tokens[3].column).toEqual(2); + expect(tokens[3].line).toEqual(1); + + // : + expect(tokens[4].type).toEqual(CssTokenType.Character); + expect(tokens[4].column).toEqual(6); + expect(tokens[4].line).toEqual(1); + + // value + expect(tokens[5].type).toEqual(CssTokenType.Identifier); + expect(tokens[5].column).toEqual(7); + expect(tokens[5].line).toEqual(1); + + // ; + expect(tokens[6].type).toEqual(CssTokenType.Character); + expect(tokens[6].column).toEqual(12); + expect(tokens[6].line).toEqual(1); + + // } + expect(tokens[7].type).toEqual(CssTokenType.Character); + expect(tokens[7].column).toEqual(0); + expect(tokens[7].line).toEqual(2); + }); - expect(tokens[0].type).toEqual(CssTokenType.Identifier); - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[2].type).toEqual(CssTokenType.String); - expect(tokens[2].strValue).toEqual('\'some { value } \\\' that is quoted\''); - }); + it('should lex quoted strings and escape accordingly', () => { + const cssCode = 'prop: \'some { value } \\\' that is quoted\''; + const tokens = tokenize(cssCode); - it('should treat attribute operators as regular characters', () => { - tokenize('^|~+*').forEach((token) => { expect(token.type).toEqual(CssTokenType.Character); }); + expect(tokens[0].type).toEqual(CssTokenType.Identifier); + expect(tokens[1].type).toEqual(CssTokenType.Character); + expect(tokens[2].type).toEqual(CssTokenType.String); + expect(tokens[2].strValue).toEqual('\'some { value } \\\' that is quoted\''); + }); + + it('should treat attribute operators as regular characters', () => { + tokenize('^|~+*').forEach((token) => { + expect(token.type).toEqual(CssTokenType.Character); }); + }); - it('should lex numbers properly and set them as numbers', () => { - const cssCode = '0 1 -2 3.0 -4.001'; - const tokens = tokenize(cssCode); + it('should lex numbers properly and set them as numbers', () => { + const cssCode = '0 1 -2 3.0 -4.001'; + const tokens = tokenize(cssCode); - expect(tokens[0].type).toEqual(CssTokenType.Number); - expect(tokens[0].strValue).toEqual('0'); + expect(tokens[0].type).toEqual(CssTokenType.Number); + expect(tokens[0].strValue).toEqual('0'); - expect(tokens[1].type).toEqual(CssTokenType.Number); - expect(tokens[1].strValue).toEqual('1'); + expect(tokens[1].type).toEqual(CssTokenType.Number); + expect(tokens[1].strValue).toEqual('1'); - expect(tokens[2].type).toEqual(CssTokenType.Number); - expect(tokens[2].strValue).toEqual('-2'); + expect(tokens[2].type).toEqual(CssTokenType.Number); + expect(tokens[2].strValue).toEqual('-2'); - expect(tokens[3].type).toEqual(CssTokenType.Number); - expect(tokens[3].strValue).toEqual('3.0'); + expect(tokens[3].type).toEqual(CssTokenType.Number); + expect(tokens[3].strValue).toEqual('3.0'); - expect(tokens[4].type).toEqual(CssTokenType.Number); - expect(tokens[4].strValue).toEqual('-4.001'); - }); - - it('should lex @keywords', () => { - const cssCode = '@import()@something'; - const tokens = tokenize(cssCode); + expect(tokens[4].type).toEqual(CssTokenType.Number); + expect(tokens[4].strValue).toEqual('-4.001'); + }); - expect(tokens[0].type).toEqual(CssTokenType.AtKeyword); - expect(tokens[0].strValue).toEqual('@import'); + it('should lex @keywords', () => { + const cssCode = '@import()@something'; + const tokens = tokenize(cssCode); - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[1].strValue).toEqual('('); + expect(tokens[0].type).toEqual(CssTokenType.AtKeyword); + expect(tokens[0].strValue).toEqual('@import'); - expect(tokens[2].type).toEqual(CssTokenType.Character); - expect(tokens[2].strValue).toEqual(')'); + expect(tokens[1].type).toEqual(CssTokenType.Character); + expect(tokens[1].strValue).toEqual('('); - expect(tokens[3].type).toEqual(CssTokenType.AtKeyword); - expect(tokens[3].strValue).toEqual('@something'); - }); + expect(tokens[2].type).toEqual(CssTokenType.Character); + expect(tokens[2].strValue).toEqual(')'); - it('should still lex a number even if it has a dimension suffix', () => { - const cssCode = '40% is 40 percent'; - const tokens = tokenize(cssCode); + expect(tokens[3].type).toEqual(CssTokenType.AtKeyword); + expect(tokens[3].strValue).toEqual('@something'); + }); - expect(tokens[0].type).toEqual(CssTokenType.Number); - expect(tokens[0].strValue).toEqual('40'); + it('should still lex a number even if it has a dimension suffix', () => { + const cssCode = '40% is 40 percent'; + const tokens = tokenize(cssCode); - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[1].strValue).toEqual('%'); + expect(tokens[0].type).toEqual(CssTokenType.Number); + expect(tokens[0].strValue).toEqual('40'); - expect(tokens[2].type).toEqual(CssTokenType.Identifier); - expect(tokens[2].strValue).toEqual('is'); + expect(tokens[1].type).toEqual(CssTokenType.Character); + expect(tokens[1].strValue).toEqual('%'); - expect(tokens[3].type).toEqual(CssTokenType.Number); - expect(tokens[3].strValue).toEqual('40'); - }); + expect(tokens[2].type).toEqual(CssTokenType.Identifier); + expect(tokens[2].strValue).toEqual('is'); - it('should allow escaped character and unicode character-strings in CSS selectors', () => { - const cssCode = '\\123456 .some\\thing \{\}'; - const tokens = tokenize(cssCode); + expect(tokens[3].type).toEqual(CssTokenType.Number); + expect(tokens[3].strValue).toEqual('40'); + }); - expect(tokens[0].type).toEqual(CssTokenType.Identifier); - expect(tokens[0].strValue).toEqual('\\123456'); + it('should allow escaped character and unicode character-strings in CSS selectors', () => { + const cssCode = '\\123456 .some\\thing \{\}'; + const tokens = tokenize(cssCode); - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[2].type).toEqual(CssTokenType.Identifier); - expect(tokens[2].strValue).toEqual('some\\thing'); - }); + expect(tokens[0].type).toEqual(CssTokenType.Identifier); + expect(tokens[0].strValue).toEqual('\\123456'); - it('should distinguish identifiers and numbers from special characters', () => { - const cssCode = 'one*two=-4+three-4-equals_value$'; - const tokens = tokenize(cssCode); + expect(tokens[1].type).toEqual(CssTokenType.Character); + expect(tokens[2].type).toEqual(CssTokenType.Identifier); + expect(tokens[2].strValue).toEqual('some\\thing'); + }); - expect(tokens[0].type).toEqual(CssTokenType.Identifier); - expect(tokens[0].strValue).toEqual('one'); + it('should distinguish identifiers and numbers from special characters', () => { + const cssCode = 'one*two=-4+three-4-equals_value$'; + const tokens = tokenize(cssCode); - expect(tokens[1].type).toEqual(CssTokenType.Character); - expect(tokens[1].strValue).toEqual('*'); + expect(tokens[0].type).toEqual(CssTokenType.Identifier); + expect(tokens[0].strValue).toEqual('one'); - expect(tokens[2].type).toEqual(CssTokenType.Identifier); - expect(tokens[2].strValue).toEqual('two'); + expect(tokens[1].type).toEqual(CssTokenType.Character); + expect(tokens[1].strValue).toEqual('*'); - expect(tokens[3].type).toEqual(CssTokenType.Character); - expect(tokens[3].strValue).toEqual('='); + expect(tokens[2].type).toEqual(CssTokenType.Identifier); + expect(tokens[2].strValue).toEqual('two'); - expect(tokens[4].type).toEqual(CssTokenType.Number); - expect(tokens[4].strValue).toEqual('-4'); + expect(tokens[3].type).toEqual(CssTokenType.Character); + expect(tokens[3].strValue).toEqual('='); - expect(tokens[5].type).toEqual(CssTokenType.Character); - expect(tokens[5].strValue).toEqual('+'); + expect(tokens[4].type).toEqual(CssTokenType.Number); + expect(tokens[4].strValue).toEqual('-4'); - expect(tokens[6].type).toEqual(CssTokenType.Identifier); - expect(tokens[6].strValue).toEqual('three-4-equals_value'); + expect(tokens[5].type).toEqual(CssTokenType.Character); + expect(tokens[5].strValue).toEqual('+'); - expect(tokens[7].type).toEqual(CssTokenType.Character); - expect(tokens[7].strValue).toEqual('$'); - }); + expect(tokens[6].type).toEqual(CssTokenType.Identifier); + expect(tokens[6].strValue).toEqual('three-4-equals_value'); - it('should filter out comments and whitespace by default', () => { - const cssCode = '.selector /* comment */ { /* value */ }'; - const tokens = tokenize(cssCode); + expect(tokens[7].type).toEqual(CssTokenType.Character); + expect(tokens[7].strValue).toEqual('$'); + }); - expect(tokens[0].strValue).toEqual('.'); - expect(tokens[1].strValue).toEqual('selector'); - expect(tokens[2].strValue).toEqual('{'); - expect(tokens[3].strValue).toEqual('}'); - }); + it('should filter out comments and whitespace by default', () => { + const cssCode = '.selector /* comment */ { /* value */ }'; + const tokens = tokenize(cssCode); - it('should track comments when the flag is set to true', () => { - const cssCode = '.selector /* comment */ { /* value */ }'; - const trackComments = true; - const tokens = tokenize(cssCode, trackComments, CssLexerMode.ALL_TRACK_WS); + expect(tokens[0].strValue).toEqual('.'); + expect(tokens[1].strValue).toEqual('selector'); + expect(tokens[2].strValue).toEqual('{'); + expect(tokens[3].strValue).toEqual('}'); + }); - expect(tokens[0].strValue).toEqual('.'); - expect(tokens[1].strValue).toEqual('selector'); - expect(tokens[2].strValue).toEqual(' '); + it('should track comments when the flag is set to true', () => { + const cssCode = '.selector /* comment */ { /* value */ }'; + const trackComments = true; + const tokens = tokenize(cssCode, trackComments, CssLexerMode.ALL_TRACK_WS); - expect(tokens[3].type).toEqual(CssTokenType.Comment); - expect(tokens[3].strValue).toEqual('/* comment */'); + expect(tokens[0].strValue).toEqual('.'); + expect(tokens[1].strValue).toEqual('selector'); + expect(tokens[2].strValue).toEqual(' '); - expect(tokens[4].strValue).toEqual(' '); - expect(tokens[5].strValue).toEqual('{'); - expect(tokens[6].strValue).toEqual(' '); + expect(tokens[3].type).toEqual(CssTokenType.Comment); + expect(tokens[3].strValue).toEqual('/* comment */'); - expect(tokens[7].type).toEqual(CssTokenType.Comment); - expect(tokens[7].strValue).toEqual('/* value */'); - }); + expect(tokens[4].strValue).toEqual(' '); + expect(tokens[5].strValue).toEqual('{'); + expect(tokens[6].strValue).toEqual(' '); - describe('Selector Mode', () => { - it('should throw an error if a selector is being parsed while in the wrong mode', () => { - const cssCode = '.class > tag'; + expect(tokens[7].type).toEqual(CssTokenType.Comment); + expect(tokens[7].strValue).toEqual('/* value */'); + }); - let capturedMessage: string|null = null; - try { - tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK); - } catch (e) { - capturedMessage = getRawMessage(e); - } + describe('Selector Mode', () => { + it('should throw an error if a selector is being parsed while in the wrong mode', () => { + const cssCode = '.class > tag'; - expect(capturedMessage).toMatch(/Unexpected character \[\>\] at column 0:7 in expression/g); + let capturedMessage: string|null = null; + try { + tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK); + } catch (e) { + capturedMessage = getRawMessage(e); + } - capturedMessage = null; - try { - tokenize(cssCode, false, CssLexerMode.SELECTOR); - } catch (e) { - capturedMessage = getRawMessage(e); - } + expect(capturedMessage).toMatch(/Unexpected character \[\>\] at column 0:7 in expression/g); - expect(capturedMessage).toEqual(null); - }); - }); + capturedMessage = null; + try { + tokenize(cssCode, false, CssLexerMode.SELECTOR); + } catch (e) { + capturedMessage = getRawMessage(e); + } - describe('Attribute Mode', () => { - it('should consider attribute selectors as valid input and throw when an invalid modifier is used', - () => { - function tokenizeAttr(modifier: string) { - const cssCode = 'value' + modifier + '=\'something\''; - return tokenize(cssCode, false, CssLexerMode.ATTRIBUTE_SELECTOR); - } - - expect(tokenizeAttr('*').length).toEqual(4); - expect(tokenizeAttr('|').length).toEqual(4); - expect(tokenizeAttr('^').length).toEqual(4); - expect(tokenizeAttr('$').length).toEqual(4); - expect(tokenizeAttr('~').length).toEqual(4); - expect(tokenizeAttr('').length).toEqual(3); - - expect(() => { tokenizeAttr('+'); }).toThrow(); - }); + expect(capturedMessage).toEqual(null); }); + }); - describe('Media Query Mode', () => { - it('should validate media queries with a reduced subset of valid characters', () => { - function tokenizeQuery(code: string) { - return tokenize(code, false, CssLexerMode.MEDIA_QUERY); - } - - // the reason why the numbers are so high is because MediaQueries keep - // track of the whitespace values - expect(tokenizeQuery('(prop: value)').length).toEqual(5); - expect(tokenizeQuery('(prop: value) and (prop2: value2)').length).toEqual(11); - expect(tokenizeQuery('tv and (prop: value)').length).toEqual(7); - expect(tokenizeQuery('print and ((prop: value) or (prop2: value2))').length).toEqual(15); - expect(tokenizeQuery('(content: \'something $ crazy inside &\')').length).toEqual(5); + describe('Attribute Mode', () => { + it('should consider attribute selectors as valid input and throw when an invalid modifier is used', + () => { + function tokenizeAttr(modifier: string) { + const cssCode = 'value' + modifier + '=\'something\''; + return tokenize(cssCode, false, CssLexerMode.ATTRIBUTE_SELECTOR); + } + + expect(tokenizeAttr('*').length).toEqual(4); + expect(tokenizeAttr('|').length).toEqual(4); + expect(tokenizeAttr('^').length).toEqual(4); + expect(tokenizeAttr('$').length).toEqual(4); + expect(tokenizeAttr('~').length).toEqual(4); + expect(tokenizeAttr('').length).toEqual(3); + + expect(() => { + tokenizeAttr('+'); + }).toThrow(); + }); + }); - expect(() => { tokenizeQuery('(max-height: 10 + 20)'); }).toThrow(); + describe('Media Query Mode', () => { + it('should validate media queries with a reduced subset of valid characters', () => { + function tokenizeQuery(code: string) { + return tokenize(code, false, CssLexerMode.MEDIA_QUERY); + } - expect(() => { tokenizeQuery('(max-height: fifty < 100)'); }).toThrow(); - }); + // the reason why the numbers are so high is because MediaQueries keep + // track of the whitespace values + expect(tokenizeQuery('(prop: value)').length).toEqual(5); + expect(tokenizeQuery('(prop: value) and (prop2: value2)').length).toEqual(11); + expect(tokenizeQuery('tv and (prop: value)').length).toEqual(7); + expect(tokenizeQuery('print and ((prop: value) or (prop2: value2))').length).toEqual(15); + expect(tokenizeQuery('(content: \'something $ crazy inside &\')').length).toEqual(5); + + expect(() => { + tokenizeQuery('(max-height: 10 + 20)'); + }).toThrow(); + + expect(() => { + tokenizeQuery('(max-height: fifty < 100)'); + }).toThrow(); }); + }); - describe('Pseudo Selector Mode', () => { - it('should validate pseudo selector identifiers with a reduced subset of valid characters', - () => { - function tokenizePseudo(code: string, withArgs = false): CssToken[] { - const mode = withArgs ? CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS : - CssLexerMode.PSEUDO_SELECTOR; - return tokenize(code, false, mode); - } - - expect(tokenizePseudo('hover').length).toEqual(1); - expect(tokenizePseudo('focus').length).toEqual(1); - expect(tokenizePseudo('lang(en-us)', true).length).toEqual(4); - - expect(() => { tokenizePseudo('lang(something:broken)', true); }).toThrow(); - - expect(() => { tokenizePseudo('not(.selector)', true); }).toThrow(); - }); - }); + describe('Pseudo Selector Mode', () => { + it('should validate pseudo selector identifiers with a reduced subset of valid characters', + () => { + function tokenizePseudo(code: string, withArgs = false): CssToken[] { + const mode = withArgs ? CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS : + CssLexerMode.PSEUDO_SELECTOR; + return tokenize(code, false, mode); + } + + expect(tokenizePseudo('hover').length).toEqual(1); + expect(tokenizePseudo('focus').length).toEqual(1); + expect(tokenizePseudo('lang(en-us)', true).length).toEqual(4); + + expect(() => { + tokenizePseudo('lang(something:broken)', true); + }).toThrow(); + + expect(() => { + tokenizePseudo('not(.selector)', true); + }).toThrow(); + }); + }); - describe( - 'Style Block Mode', () => { - it('should style blocks with a reduced subset of valid characters', - () => { - function tokenizeStyles(code: string) { - return tokenize(code, false, CssLexerMode.STYLE_BLOCK); - } + describe( + 'Style Block Mode', () => { + it( + 'should style blocks with a reduced subset of valid characters', () => { + function tokenizeStyles(code: string) { + return tokenize(code, false, CssLexerMode.STYLE_BLOCK); + } - expect(tokenizeStyles(` + expect(tokenizeStyles(` key: value; prop: 100; style: value3!important; `).length).toEqual(14); - expect(() => tokenizeStyles(` key$: value; `)).toThrow(); - expect(() => tokenizeStyles(` key: value$; `)).toThrow(); - expect(() => tokenizeStyles(` key: value + 10; `)).toThrow(); - expect(() => tokenizeStyles(` key: &value; `)).toThrow(); - }); - }); - }); + expect(() => tokenizeStyles(` key$: value; `)).toThrow(); + expect(() => tokenizeStyles(` key: value$; `)).toThrow(); + expect(() => tokenizeStyles(` key: value + 10; `)).toThrow(); + expect(() => tokenizeStyles(` key: &value; `)).toThrow(); + }); + }); +}); })(); diff --git a/packages/compiler/test/css_parser/css_parser_spec.ts b/packages/compiler/test/css_parser/css_parser_spec.ts index 670a48264b3bc..ad8c211ece16b 100644 --- a/packages/compiler/test/css_parser/css_parser_spec.ts +++ b/packages/compiler/test/css_parser/css_parser_spec.ts @@ -111,26 +111,26 @@ export function assertTokens(tokens: CssToken[], valuesArr: string[]) { expect(ast.rules.length).toEqual(1); const rule = <CssKeyframeRuleAst>ast.rules[0]; - expect(rule.name !.strValue).toEqual('rotateMe'); + expect(rule.name!.strValue).toEqual('rotateMe'); const block = <CssBlockAst>rule.block; const fromRule = <CssKeyframeDefinitionAst>block.entries[0]; - expect(fromRule.name !.strValue).toEqual('from'); + expect(fromRule.name!.strValue).toEqual('from'); const fromStyle = <CssDefinitionAst>(<CssBlockAst>fromRule.block).entries[0]; expect(fromStyle.property.strValue).toEqual('transform'); assertTokens(fromStyle.value.tokens, ['rotate', '(', '-360', 'deg', ')']); const midRule = <CssKeyframeDefinitionAst>block.entries[1]; - expect(midRule.name !.strValue).toEqual('50%'); + expect(midRule.name!.strValue).toEqual('50%'); const midStyle = <CssDefinitionAst>(<CssBlockAst>midRule.block).entries[0]; expect(midStyle.property.strValue).toEqual('transform'); assertTokens(midStyle.value.tokens, ['rotate', '(', '0', 'deg', ')']); const toRule = <CssKeyframeDefinitionAst>block.entries[2]; - expect(toRule.name !.strValue).toEqual('to'); + expect(toRule.name!.strValue).toEqual('to'); const toStyle = <CssDefinitionAst>(<CssBlockAst>toRule.block).entries[0]; expect(toStyle.property.strValue).toEqual('transform'); assertTokens(toStyle.value.tokens, ['rotate', '(', '360', 'deg', ')']); @@ -695,7 +695,7 @@ export function assertTokens(tokens: CssToken[], valuesArr: string[]) { const ast = output.ast; assertMatchesOffsetAndChar(ast.location.start, 0, '#'); - assertMatchesOffsetAndChar(ast.location.end, 22, undefined !); + assertMatchesOffsetAndChar(ast.location.end, 22, undefined!); }); }); diff --git a/packages/compiler/test/css_parser/css_visitor_spec.ts b/packages/compiler/test/css_parser/css_visitor_spec.ts index 5a36459264e6e..f7d05a5e5b2a7 100644 --- a/packages/compiler/test/css_parser/css_visitor_spec.ts +++ b/packages/compiler/test/css_parser/css_visitor_spec.ts @@ -8,7 +8,7 @@ import {beforeEach, describe, expect, it} from '../../../core/testing/src/testing_internal'; -import {CssAst, CssAstVisitor, CssAtRulePredicateAst, CssBlockAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssPseudoSelectorAst, CssRuleAst, CssSelectorAst, CssSelectorRuleAst, CssSimpleSelectorAst, CssStyleSheetAst, CssStyleValueAst, CssStylesBlockAst, CssUnknownRuleAst, CssUnknownTokenListAst} from '../../src/css_parser/css_ast'; +import {CssAst, CssAstVisitor, CssAtRulePredicateAst, CssBlockAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssPseudoSelectorAst, CssRuleAst, CssSelectorAst, CssSelectorRuleAst, CssSimpleSelectorAst, CssStylesBlockAst, CssStyleSheetAst, CssStyleValueAst, CssUnknownRuleAst, CssUnknownTokenListAst} from '../../src/css_parser/css_ast'; import {BlockType, CssParseError, CssParser, CssToken} from '../../src/css_parser/css_parser'; function _assertTokens(tokens: CssToken[], valuesArr: string[]): void { @@ -29,7 +29,9 @@ class MyVisitor implements CssAstVisitor { this.captures[method].push([ast, context]); } - constructor(ast: CssStyleSheetAst, context: any) { ast.visit(this, context); } + constructor(ast: CssStyleSheetAst, context: any) { + ast.visit(this, context); + } visitCssValue(ast: CssStyleValueAst, context: any): void { this._capture('visitCssValue', ast, context); @@ -61,20 +63,24 @@ class MyVisitor implements CssAstVisitor { visitCssSelectorRule(ast: CssSelectorRuleAst, context: any): void { this._capture('visitCssSelectorRule', ast, context); - ast.selectors.forEach((selAst: CssSelectorAst) => { selAst.visit(this, context); }); + ast.selectors.forEach((selAst: CssSelectorAst) => { + selAst.visit(this, context); + }); ast.block.visit(this, context); } visitCssSelector(ast: CssSelectorAst, context: any): void { this._capture('visitCssSelector', ast, context); - ast.selectorParts.forEach( - (simpleAst: CssSimpleSelectorAst) => { simpleAst.visit(this, context); }); + ast.selectorParts.forEach((simpleAst: CssSimpleSelectorAst) => { + simpleAst.visit(this, context); + }); } visitCssSimpleSelector(ast: CssSimpleSelectorAst, context: any): void { this._capture('visitCssSimpleSelector', ast, context); - ast.pseudoSelectors.forEach( - (pseudoAst: CssPseudoSelectorAst) => { pseudoAst.visit(this, context); }); + ast.pseudoSelectors.forEach((pseudoAst: CssPseudoSelectorAst) => { + pseudoAst.visit(this, context); + }); } visitCssDefinition(ast: CssDefinitionAst, context: any): void { @@ -84,18 +90,23 @@ class MyVisitor implements CssAstVisitor { visitCssBlock(ast: CssBlockAst, context: any): void { this._capture('visitCssBlock', ast, context); - ast.entries.forEach((entryAst: CssAst) => { entryAst.visit(this, context); }); + ast.entries.forEach((entryAst: CssAst) => { + entryAst.visit(this, context); + }); } visitCssStylesBlock(ast: CssStylesBlockAst, context: any): void { this._capture('visitCssStylesBlock', ast, context); - ast.definitions.forEach( - (definitionAst: CssDefinitionAst) => { definitionAst.visit(this, context); }); + ast.definitions.forEach((definitionAst: CssDefinitionAst) => { + definitionAst.visit(this, context); + }); } visitCssStyleSheet(ast: CssStyleSheetAst, context: any): void { this._capture('visitCssStyleSheet', ast, context); - ast.rules.forEach((ruleAst: CssRuleAst) => { ruleAst.visit(this, context); }); + ast.rules.forEach((ruleAst: CssRuleAst) => { + ruleAst.visit(this, context); + }); } visitCssUnknownRule(ast: CssUnknownRuleAst, context: any): void { @@ -116,21 +127,21 @@ function _getCaptureAst(capture: any[], index = 0): CssAst { } (function() { - function parse(cssCode: string, ignoreErrors: boolean = false) { - const output = new CssParser().parse(cssCode, 'some-fake-css-file.css'); - const errors = output.errors; - if (errors.length > 0 && !ignoreErrors) { - throw new Error(errors.map((error: CssParseError) => error.msg).join(', ')); - } - return output.ast; +function parse(cssCode: string, ignoreErrors: boolean = false) { + const output = new CssParser().parse(cssCode, 'some-fake-css-file.css'); + const errors = output.errors; + if (errors.length > 0 && !ignoreErrors) { + throw new Error(errors.map((error: CssParseError) => error.msg).join(', ')); } + return output.ast; +} - describe('CSS parsing and visiting', () => { - let ast: CssStyleSheetAst; - const context = {}; +describe('CSS parsing and visiting', () => { + let ast: CssStyleSheetAst; + const context = {}; - beforeEach(() => { - const cssCode = ` + beforeEach(() => { + const cssCode = ` .rule1 { prop1: value1 } .rule2 { prop2: value2 } @@ -149,174 +160,174 @@ function _getCaptureAst(capture: any[], index = 0): CssAst { } } `; - ast = parse(cssCode); - }); + ast = parse(cssCode); + }); - it('should parse and visit a stylesheet', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssStyleSheet']; + it('should parse and visit a stylesheet', () => { + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssStyleSheet']; - expect(captures.length).toEqual(1); + expect(captures.length).toEqual(1); - const capture = captures[0]; - expect(capture[0]).toEqual(ast); - expect(capture[1]).toEqual(context); - }); + const capture = captures[0]; + expect(capture[0]).toEqual(ast); + expect(capture[1]).toEqual(context); + }); - it('should parse and visit each of the stylesheet selectors', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssSelectorRule']; + it('should parse and visit each of the stylesheet selectors', () => { + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssSelectorRule']; - expect(captures.length).toEqual(3); + expect(captures.length).toEqual(3); - const rule1 = <CssSelectorRuleAst>_getCaptureAst(captures, 0); - expect(rule1).toEqual(ast.rules[0] as CssSelectorRuleAst); + const rule1 = <CssSelectorRuleAst>_getCaptureAst(captures, 0); + expect(rule1).toEqual(ast.rules[0] as CssSelectorRuleAst); - const firstSelector = rule1.selectors[0]; - const firstSimpleSelector = firstSelector.selectorParts[0]; - _assertTokens(firstSimpleSelector.tokens, ['.', 'rule1']); + const firstSelector = rule1.selectors[0]; + const firstSimpleSelector = firstSelector.selectorParts[0]; + _assertTokens(firstSimpleSelector.tokens, ['.', 'rule1']); - const rule2 = <CssSelectorRuleAst>_getCaptureAst(captures, 1); - expect(rule2).toEqual(ast.rules[1] as CssSelectorRuleAst); + const rule2 = <CssSelectorRuleAst>_getCaptureAst(captures, 1); + expect(rule2).toEqual(ast.rules[1] as CssSelectorRuleAst); - const secondSelector = rule2.selectors[0]; - const secondSimpleSelector = secondSelector.selectorParts[0]; - _assertTokens(secondSimpleSelector.tokens, ['.', 'rule2']); + const secondSelector = rule2.selectors[0]; + const secondSimpleSelector = secondSelector.selectorParts[0]; + _assertTokens(secondSimpleSelector.tokens, ['.', 'rule2']); - const rule3 = <CssSelectorRuleAst>_getCaptureAst(captures, 2); - expect(rule3).toEqual( - (ast.rules[2] as CssSelectorRuleAst).block.entries[0] as CssSelectorRuleAst); + const rule3 = <CssSelectorRuleAst>_getCaptureAst(captures, 2); + expect(rule3).toEqual( + (ast.rules[2] as CssSelectorRuleAst).block.entries[0] as CssSelectorRuleAst); - const thirdSelector = rule3.selectors[0]; - const thirdSimpleSelector = thirdSelector.selectorParts[0]; - _assertTokens(thirdSimpleSelector.tokens, ['#', 'rule3']); - }); + const thirdSelector = rule3.selectors[0]; + const thirdSimpleSelector = thirdSelector.selectorParts[0]; + _assertTokens(thirdSimpleSelector.tokens, ['#', 'rule3']); + }); - it('should parse and visit each of the stylesheet style key/value definitions', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssDefinition']; + it('should parse and visit each of the stylesheet style key/value definitions', () => { + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssDefinition']; - expect(captures.length).toEqual(5); + expect(captures.length).toEqual(5); - const def1 = <CssDefinitionAst>_getCaptureAst(captures, 0); - expect(def1.property.strValue).toEqual('prop1'); - expect(def1.value.tokens[0].strValue).toEqual('value1'); + const def1 = <CssDefinitionAst>_getCaptureAst(captures, 0); + expect(def1.property.strValue).toEqual('prop1'); + expect(def1.value.tokens[0].strValue).toEqual('value1'); - const def2 = <CssDefinitionAst>_getCaptureAst(captures, 1); - expect(def2.property.strValue).toEqual('prop2'); - expect(def2.value.tokens[0].strValue).toEqual('value2'); + const def2 = <CssDefinitionAst>_getCaptureAst(captures, 1); + expect(def2.property.strValue).toEqual('prop2'); + expect(def2.value.tokens[0].strValue).toEqual('value2'); - const def3 = <CssDefinitionAst>_getCaptureAst(captures, 2); - expect(def3.property.strValue).toEqual('prop3'); - expect(def3.value.tokens[0].strValue).toEqual('value3'); + const def3 = <CssDefinitionAst>_getCaptureAst(captures, 2); + expect(def3.property.strValue).toEqual('prop3'); + expect(def3.value.tokens[0].strValue).toEqual('value3'); - const def4 = <CssDefinitionAst>_getCaptureAst(captures, 3); - expect(def4.property.strValue).toEqual('prop4'); - expect(def4.value.tokens[0].strValue).toEqual('value4'); + const def4 = <CssDefinitionAst>_getCaptureAst(captures, 3); + expect(def4.property.strValue).toEqual('prop4'); + expect(def4.value.tokens[0].strValue).toEqual('value4'); - const def5 = <CssDefinitionAst>_getCaptureAst(captures, 4); - expect(def5.property.strValue).toEqual('prop5'); - expect(def5.value.tokens[0].strValue).toEqual('value5'); - }); + const def5 = <CssDefinitionAst>_getCaptureAst(captures, 4); + expect(def5.property.strValue).toEqual('prop5'); + expect(def5.value.tokens[0].strValue).toEqual('value5'); + }); - it('should parse and visit the associated media query values', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssMediaQueryRule']; + it('should parse and visit the associated media query values', () => { + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssMediaQueryRule']; - expect(captures.length).toEqual(1); + expect(captures.length).toEqual(1); - const query1 = <CssMediaQueryRuleAst>_getCaptureAst(captures, 0); - _assertTokens(query1.query.tokens, ['all', 'and', '(', 'max-width', '100', 'px', ')']); - expect(query1.block.entries.length).toEqual(1); - }); + const query1 = <CssMediaQueryRuleAst>_getCaptureAst(captures, 0); + _assertTokens(query1.query.tokens, ['all', 'and', '(', 'max-width', '100', 'px', ')']); + expect(query1.block.entries.length).toEqual(1); + }); - it('should capture the media query predicate', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssAtRulePredicate']; + it('should capture the media query predicate', () => { + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssAtRulePredicate']; - expect(captures.length).toEqual(1); + expect(captures.length).toEqual(1); - const predicate = <CssAtRulePredicateAst>_getCaptureAst(captures, 0); - expect(predicate.strValue).toEqual('@media all (max-width: 100px)'); - }); + const predicate = <CssAtRulePredicateAst>_getCaptureAst(captures, 0); + expect(predicate.strValue).toEqual('@media all (max-width: 100px)'); + }); - it('should parse and visit the associated "@inline" rule values', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssInlineRule']; + it('should parse and visit the associated "@inline" rule values', () => { + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssInlineRule']; - expect(captures.length).toEqual(1); + expect(captures.length).toEqual(1); - const inline1 = <CssInlineRuleAst>_getCaptureAst(captures, 0); - expect(inline1.type).toEqual(BlockType.Import); - _assertTokens(inline1.value.tokens, ['url', '(', 'file.css', ')']); - }); + const inline1 = <CssInlineRuleAst>_getCaptureAst(captures, 0); + expect(inline1.type).toEqual(BlockType.Import); + _assertTokens(inline1.value.tokens, ['url', '(', 'file.css', ')']); + }); - it('should parse and visit the keyframe blocks', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssKeyframeRule']; + it('should parse and visit the keyframe blocks', () => { + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssKeyframeRule']; - expect(captures.length).toEqual(1); + expect(captures.length).toEqual(1); - const keyframe1 = <CssKeyframeRuleAst>_getCaptureAst(captures, 0); - expect(keyframe1.name !.strValue).toEqual('rotate'); - expect(keyframe1.block.entries.length).toEqual(2); - }); + const keyframe1 = <CssKeyframeRuleAst>_getCaptureAst(captures, 0); + expect(keyframe1.name!.strValue).toEqual('rotate'); + expect(keyframe1.block.entries.length).toEqual(2); + }); - it('should parse and visit the associated keyframe rules', () => { - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssKeyframeDefinition']; + it('should parse and visit the associated keyframe rules', () => { + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssKeyframeDefinition']; - expect(captures.length).toEqual(2); + expect(captures.length).toEqual(2); - const def1 = <CssKeyframeDefinitionAst>_getCaptureAst(captures, 0); - _assertTokens(def1.steps, ['from']); - expect(def1.block.entries.length).toEqual(1); + const def1 = <CssKeyframeDefinitionAst>_getCaptureAst(captures, 0); + _assertTokens(def1.steps, ['from']); + expect(def1.block.entries.length).toEqual(1); - const def2 = <CssKeyframeDefinitionAst>_getCaptureAst(captures, 1); - _assertTokens(def2.steps, ['50%', '100%']); - expect(def2.block.entries.length).toEqual(1); - }); + const def2 = <CssKeyframeDefinitionAst>_getCaptureAst(captures, 1); + _assertTokens(def2.steps, ['50%', '100%']); + expect(def2.block.entries.length).toEqual(1); + }); - it('should visit an unknown `@` rule', () => { - const cssCode = ` + it('should visit an unknown `@` rule', () => { + const cssCode = ` @someUnknownRule param { one two three } `; - ast = parse(cssCode, true); - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssUnknownRule']; + ast = parse(cssCode, true); + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssUnknownRule']; - expect(captures.length).toEqual(1); + expect(captures.length).toEqual(1); - const rule = <CssUnknownRuleAst>_getCaptureAst(captures, 0); - expect(rule.ruleName).toEqual('@someUnknownRule'); + const rule = <CssUnknownRuleAst>_getCaptureAst(captures, 0); + expect(rule.ruleName).toEqual('@someUnknownRule'); - _assertTokens(rule.tokens, ['param', '{', 'one', 'two', 'three', '}']); - }); + _assertTokens(rule.tokens, ['param', '{', 'one', 'two', 'three', '}']); + }); - it('should collect an invalid list of tokens before a valid selector', () => { - const cssCode = 'one two three four five; selector { }'; - ast = parse(cssCode, true); - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssUnknownTokenList']; + it('should collect an invalid list of tokens before a valid selector', () => { + const cssCode = 'one two three four five; selector { }'; + ast = parse(cssCode, true); + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssUnknownTokenList']; - expect(captures.length).toEqual(1); + expect(captures.length).toEqual(1); - const rule = <CssUnknownTokenListAst>_getCaptureAst(captures, 0); - _assertTokens(rule.tokens, ['one', 'two', 'three', 'four', 'five']); - }); + const rule = <CssUnknownTokenListAst>_getCaptureAst(captures, 0); + _assertTokens(rule.tokens, ['one', 'two', 'three', 'four', 'five']); + }); - it('should collect an invalid list of tokens after a valid selector', () => { - const cssCode = 'selector { } six seven eight'; - ast = parse(cssCode, true); - const visitor = new MyVisitor(ast, context); - const captures = visitor.captures['visitCssUnknownTokenList']; + it('should collect an invalid list of tokens after a valid selector', () => { + const cssCode = 'selector { } six seven eight'; + ast = parse(cssCode, true); + const visitor = new MyVisitor(ast, context); + const captures = visitor.captures['visitCssUnknownTokenList']; - expect(captures.length).toEqual(1); + expect(captures.length).toEqual(1); - const rule = <CssUnknownTokenListAst>_getCaptureAst(captures, 0); - _assertTokens(rule.tokens, ['six', 'seven', 'eight']); - }); + const rule = <CssUnknownTokenListAst>_getCaptureAst(captures, 0); + _assertTokens(rule.tokens, ['six', 'seven', 'eight']); }); +}); })(); diff --git a/packages/compiler/test/directive_lifecycle_spec.ts b/packages/compiler/test/directive_lifecycle_spec.ts index a4bd85928f4b1..4e77bec8065b4 100644 --- a/packages/compiler/test/directive_lifecycle_spec.ts +++ b/packages/compiler/test/directive_lifecycle_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {LifecycleHooks as Hooks, hasLifecycleHook as hasLifecycleHookImpl} from '@angular/compiler/src/lifecycle_reflector'; +import {hasLifecycleHook as hasLifecycleHookImpl, LifecycleHooks as Hooks} from '@angular/compiler/src/lifecycle_reflector'; import {SimpleChanges} from '@angular/core'; import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector'; @@ -17,14 +17,14 @@ function hasLifecycleHook(hook: Hooks, directive: any): boolean { { describe('Create Directive', () => { describe('lifecycle', () => { - describe('ngOnChanges', () => { it('should be true when the directive has the ngOnChanges method', () => { expect(hasLifecycleHook(Hooks.OnChanges, DirectiveWithOnChangesMethod)).toBe(true); }); - it('should be false otherwise', - () => { expect(hasLifecycleHook(Hooks.OnChanges, DirectiveNoHooks)).toBe(false); }); + it('should be false otherwise', () => { + expect(hasLifecycleHook(Hooks.OnChanges, DirectiveNoHooks)).toBe(false); + }); }); describe('ngOnDestroy', () => { @@ -32,16 +32,19 @@ function hasLifecycleHook(hook: Hooks, directive: any): boolean { expect(hasLifecycleHook(Hooks.OnDestroy, DirectiveWithOnDestroyMethod)).toBe(true); }); - it('should be false otherwise', - () => { expect(hasLifecycleHook(Hooks.OnDestroy, DirectiveNoHooks)).toBe(false); }); + it('should be false otherwise', () => { + expect(hasLifecycleHook(Hooks.OnDestroy, DirectiveNoHooks)).toBe(false); + }); }); describe('ngOnInit', () => { - it('should be true when the directive has the ngOnInit method', - () => { expect(hasLifecycleHook(Hooks.OnInit, DirectiveWithOnInitMethod)).toBe(true); }); + it('should be true when the directive has the ngOnInit method', () => { + expect(hasLifecycleHook(Hooks.OnInit, DirectiveWithOnInitMethod)).toBe(true); + }); - it('should be false otherwise', - () => { expect(hasLifecycleHook(Hooks.OnInit, DirectiveNoHooks)).toBe(false); }); + it('should be false otherwise', () => { + expect(hasLifecycleHook(Hooks.OnInit, DirectiveNoHooks)).toBe(false); + }); }); describe('ngDoCheck', () => { @@ -49,8 +52,9 @@ function hasLifecycleHook(hook: Hooks, directive: any): boolean { expect(hasLifecycleHook(Hooks.DoCheck, DirectiveWithOnCheckMethod)).toBe(true); }); - it('should be false otherwise', - () => { expect(hasLifecycleHook(Hooks.DoCheck, DirectiveNoHooks)).toBe(false); }); + it('should be false otherwise', () => { + expect(hasLifecycleHook(Hooks.DoCheck, DirectiveNoHooks)).toBe(false); + }); }); describe('ngAfterContentInit', () => { @@ -83,8 +87,9 @@ function hasLifecycleHook(hook: Hooks, directive: any): boolean { .toBe(true); }); - it('should be false otherwise', - () => { expect(hasLifecycleHook(Hooks.AfterViewInit, DirectiveNoHooks)).toBe(false); }); + it('should be false otherwise', () => { + expect(hasLifecycleHook(Hooks.AfterViewInit, DirectiveNoHooks)).toBe(false); + }); }); describe('ngAfterViewChecked', () => { diff --git a/packages/compiler/test/directive_normalizer_spec.ts b/packages/compiler/test/directive_normalizer_spec.ts index 34bd29d809fc6..4e2c64cc20465 100644 --- a/packages/compiler/test/directive_normalizer_spec.ts +++ b/packages/compiler/test/directive_normalizer_spec.ts @@ -10,7 +10,7 @@ import {CompilerConfig, preserveWhitespacesDefault} from '@angular/compiler/src/ import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer'; import {ResourceLoader} from '@angular/compiler/src/resource_loader'; import {ViewEncapsulation} from '@angular/core/src/metadata/view'; -import {TestBed, inject} from '@angular/core/testing'; +import {inject, TestBed} from '@angular/core/testing'; import {noUndefined} from '../src/util'; @@ -20,7 +20,10 @@ const SOME_MODULE_URL = 'package:some/module/a.js'; const SOME_HTTP_MODULE_URL = 'http://some/module/a.js'; function normalizeTemplate(normalizer: DirectiveNormalizer, o: { - moduleUrl?: string; template?: string | null; templateUrl?: string | null; styles?: string[]; + moduleUrl?: string; + template?: string | null; + templateUrl?: string | null; + styles?: string[]; styleUrls?: string[]; interpolation?: [string, string] | null; encapsulation?: ViewEncapsulation | null; @@ -51,8 +54,7 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: { jasmine.createSpy('get').and.callFake((url: string) => `resource(${url})`); const resourceLoader = {get: resourceLoaderSpy}; TestBed.configureCompiler({ - providers: - [...TEST_COMPILER_PROVIDERS, {provide: ResourceLoader, useValue: resourceLoader}] + providers: [...TEST_COMPILER_PROVIDERS, {provide: ResourceLoader, useValue: resourceLoader}] }); }); @@ -64,12 +66,12 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: { })); it('should throw if template is not a string', inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { - expect(() => normalizeTemplate(normalizer, {template: <any>{}})) + expect(() => normalizeTemplate(normalizer, {template: <any> {}})) .toThrowError('The template specified for component SomeComp is not a string'); })); it('should throw if templateUrl is not a string', inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { - expect(() => normalizeTemplate(normalizer, {templateUrl: <any>{}})) + expect(() => normalizeTemplate(normalizer, {templateUrl: <any> {}})) .toThrowError('The templateUrl specified for component SomeComp is not a string'); })); it('should throw if both template and templateUrl are defined', @@ -89,11 +91,9 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: { .toThrowError( 'The preserveWhitespaces option for component SomeComp must be a boolean'); })); - }); describe('inline template', () => { - it('should store the template', inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { const template = <CompileTemplateMetadata>normalizeTemplate(normalizer, { @@ -174,7 +174,6 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: { })); describe('externalStylesheets', () => { - it('should load an external stylesheet', inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => { const template = <CompileTemplateMetadata>normalizeTemplate( @@ -222,7 +221,6 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: { expect(resourceLoaderSpy).toHaveBeenCalledTimes(1); })); - }); describe('normalizeLoadedTemplate', () => { diff --git a/packages/compiler/test/directive_resolver_mock_spec.ts b/packages/compiler/test/directive_resolver_mock_spec.ts index dc1a82cff98cd..c81dff025a877 100644 --- a/packages/compiler/test/directive_resolver_mock_spec.ts +++ b/packages/compiler/test/directive_resolver_mock_spec.ts @@ -7,7 +7,7 @@ */ import {Component, Directive, Injector} from '@angular/core'; -import {TestBed, inject} from '@angular/core/testing'; +import {inject, TestBed} from '@angular/core/testing'; import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector'; import {MockDirectiveResolver} from '../testing'; diff --git a/packages/compiler/test/directive_resolver_spec.ts b/packages/compiler/test/directive_resolver_spec.ts index 4d211643f97dd..c0cf0fa394136 100644 --- a/packages/compiler/test/directive_resolver_spec.ts +++ b/packages/compiler/test/directive_resolver_spec.ts @@ -32,13 +32,16 @@ class SomeDirectiveWithOutputs { @Directive({selector: 'someDirective'}) class SomeDirectiveWithSetterProps { @Input('renamed') - set a(value: any) {} + set a(value: any) { + } } @Directive({selector: 'someDirective'}) class SomeDirectiveWithGetterOutputs { @Output('renamed') - get a(): any { return null; } + get a(): any { + return null; + } } @Directive({selector: 'someDirective', host: {'[c]': 'c'}}) @@ -51,9 +54,11 @@ class SomeDirectiveWithHostBindings { @Directive({selector: 'someDirective', host: {'(c)': 'onC()'}}) class SomeDirectiveWithHostListeners { @HostListener('a') - onA() {} + onA() { + } @HostListener('b', ['$event.value']) - onB(value: any) {} + onB(value: any) { + } } @Directive({selector: 'someDirective', queries: {'cs': new ContentChildren('c')}}) @@ -101,13 +106,15 @@ class SomeDirectiveWithSameHostBindingAndInput { @Directive({selector: 'someDirective'}) class SomeDirectiveWithMalformedHostBinding1 { @HostBinding('(a)') - onA() {} + onA() { + } } @Directive({selector: 'someDirective'}) class SomeDirectiveWithMalformedHostBinding2 { @HostBinding('[a]') - onA() {} + onA() { + } } class SomeDirectiveWithoutMetadata {} @@ -116,7 +123,9 @@ class SomeDirectiveWithoutMetadata {} describe('DirectiveResolver', () => { let resolver: DirectiveResolver; - beforeEach(() => { resolver = new DirectiveResolver(new JitReflector()); }); + beforeEach(() => { + resolver = new DirectiveResolver(new JitReflector()); + }); it('should read out the Directive metadata', () => { const directiveMetadata = resolver.resolve(SomeDirective); @@ -204,8 +213,7 @@ class SomeDirectiveWithoutMetadata {} it('should prefer @Input over @Directive.inputs', () => { @Directive({selector: 'someDirective', inputs: ['a']}) class SomeDirectiveWithDuplicateInputs { - @Input('a') - propA: any; + @Input('a') propA: any; } const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateInputs); expect(directiveMetadata.inputs).toEqual(['propA: a']); @@ -214,17 +222,13 @@ class SomeDirectiveWithoutMetadata {} it('should support inheriting inputs', () => { @Directive({selector: 'p'}) class Parent { - @Input() - p1: any; - @Input('p21') - p2: any; + @Input() p1: any; + @Input('p21') p2: any; } class Child extends Parent { - @Input('p22') - p2: any; - @Input() - p3: any; + @Input('p22') p2: any; + @Input() p3: any; } const directiveMetadata = resolver.resolve(Child); @@ -264,8 +268,7 @@ class SomeDirectiveWithoutMetadata {} it('should prefer @Output over @Directive.outputs', () => { @Directive({selector: 'someDirective', outputs: ['a']}) class SomeDirectiveWithDuplicateOutputs { - @Output('a') - propA: any; + @Output('a') propA: any; } const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateOutputs); expect(directiveMetadata.outputs).toEqual(['propA: a']); @@ -274,17 +277,13 @@ class SomeDirectiveWithoutMetadata {} it('should support inheriting outputs', () => { @Directive({selector: 'p'}) class Parent { - @Output() - p1: any; - @Output('p21') - p2: any; + @Output() p1: any; + @Output('p21') p2: any; } class Child extends Parent { - @Output('p22') - p2: any; - @Output() - p3: any; + @Output('p22') p2: any; + @Output() p3: any; } const directiveMetadata = resolver.resolve(Child); @@ -324,17 +323,13 @@ class SomeDirectiveWithoutMetadata {} it('should support inheriting host bindings', () => { @Directive({selector: 'p'}) class Parent { - @HostBinding() - p1: any; - @HostBinding('p21') - p2: any; + @HostBinding() p1: any; + @HostBinding('p21') p2: any; } class Child extends Parent { - @HostBinding('p22') - p2: any; - @HostBinding() - p3: any; + @HostBinding('p22') p2: any; + @HostBinding() p3: any; } const directiveMetadata = resolver.resolve(Child); @@ -346,16 +341,20 @@ class SomeDirectiveWithoutMetadata {} @Directive({selector: 'p'}) class Parent { @HostListener('p1') - p1() {} + p1() { + } @HostListener('p21') - p2() {} + p2() { + } } class Child extends Parent { @HostListener('p22') - p2() {} + p2() { + } @HostListener('p3') - p3() {} + p3() { + } } const directiveMetadata = resolver.resolve(Child); @@ -366,19 +365,20 @@ class SomeDirectiveWithoutMetadata {} it('should combine host bindings and listeners during inheritance', () => { @Directive({selector: 'p'}) class Parent { - @HostListener('p11') @HostListener('p12') - p1() {} + @HostListener('p11') + @HostListener('p12') + p1() { + } - @HostBinding('p21') @HostBinding('p22') - p2: any; + @HostBinding('p21') @HostBinding('p22') p2: any; } class Child extends Parent { @HostListener('c1') - p1() {} + p1() { + } - @HostBinding('c2') - p2: any; + @HostBinding('c2') p2: any; } const directiveMetadata = resolver.resolve(Child); @@ -421,17 +421,13 @@ class SomeDirectiveWithoutMetadata {} it('should support inheriting queries', () => { @Directive({selector: 'p'}) class Parent { - @ContentChild('p1') - p1: any; - @ContentChild('p21') - p2: any; + @ContentChild('p1') p1: any; + @ContentChild('p21') p2: any; } class Child extends Parent { - @ContentChild('p22') - p2: any; - @ContentChild('p3') - p3: any; + @ContentChild('p22') p2: any; + @ContentChild('p3') p3: any; } const directiveMetadata = resolver.resolve(Child); diff --git a/packages/compiler/test/expression_parser/lexer_spec.ts b/packages/compiler/test/expression_parser/lexer_spec.ts index 3fda05b0a4263..d09d261cf766c 100644 --- a/packages/compiler/test/expression_parser/lexer_spec.ts +++ b/packages/compiler/test/expression_parser/lexer_spec.ts @@ -101,14 +101,17 @@ function expectErrorToken(token: Token, index: any, end: number, message: string expectNumberToken(tokens[0], 0, 2, 88); }); - it('should tokenize numbers within index ops', - () => { expectNumberToken(lex('a[22]')[2], 2, 4, 22); }); + it('should tokenize numbers within index ops', () => { + expectNumberToken(lex('a[22]')[2], 2, 4, 22); + }); - it('should tokenize simple quoted strings', - () => { expectStringToken(lex('"a"')[0], 0, 3, 'a'); }); + it('should tokenize simple quoted strings', () => { + expectStringToken(lex('"a"')[0], 0, 3, 'a'); + }); - it('should tokenize quoted strings with escaped quotes', - () => { expectStringToken(lex('"a\\""')[0], 0, 5, 'a"'); }); + it('should tokenize quoted strings with escaped quotes', () => { + expectStringToken(lex('"a\\""')[0], 0, 5, 'a"'); + }); it('should tokenize a string', () => { const tokens: Token[] = lex('j-a.bc[22]+1.3|f:\'a\\\'c\':"d\\"e"'); @@ -213,7 +216,9 @@ function expectErrorToken(token: Token, index: any, end: number, message: string expectCharacterToken(tokens[13], 16, 17, ')'); }); - it('should tokenize number', () => { expectNumberToken(lex('0.5')[0], 0, 3, 0.5); }); + it('should tokenize number', () => { + expectNumberToken(lex('0.5')[0], 0, 3, 0.5); + }); it('should tokenize number with exponent', () => { let tokens: Token[] = lex('0.5E-10'); @@ -233,8 +238,9 @@ function expectErrorToken(token: Token, index: any, end: number, message: string 'Lexer Error: Invalid exponent at column 4 in expression [0.5E-A]'); }); - it('should tokenize number starting with a dot', - () => { expectNumberToken(lex('.5')[0], 0, 2, 0.5); }); + it('should tokenize number starting with a dot', () => { + expectNumberToken(lex('.5')[0], 0, 2, 0.5); + }); it('should throw error on invalid unicode', () => { expectErrorToken( @@ -242,11 +248,13 @@ function expectErrorToken(token: Token, index: any, end: number, message: string 'Lexer Error: Invalid unicode escape [\\u1\'\'b] at column 2 in expression [\'\\u1\'\'bla\']'); }); - it('should tokenize hash as operator', - () => { expectOperatorToken(lex('#')[0], 0, 1, '#'); }); + it('should tokenize hash as operator', () => { + expectOperatorToken(lex('#')[0], 0, 1, '#'); + }); - it('should tokenize ?. as operator', - () => { expectOperatorToken(lex('?.')[0], 0, 2, '?.'); }); + it('should tokenize ?. as operator', () => { + expectOperatorToken(lex('?.')[0], 0, 2, '?.'); + }); }); }); } diff --git a/packages/compiler/test/expression_parser/parser_spec.ts b/packages/compiler/test/expression_parser/parser_spec.ts index 69fb2a358c19c..f25aab0e40588 100644 --- a/packages/compiler/test/expression_parser/parser_spec.ts +++ b/packages/compiler/test/expression_parser/parser_spec.ts @@ -17,16 +17,22 @@ import {validate} from './utils/validator'; describe('parser', () => { describe('parseAction', () => { - it('should parse numbers', () => { checkAction('1'); }); + it('should parse numbers', () => { + checkAction('1'); + }); it('should parse strings', () => { checkAction('\'1\'', '"1"'); checkAction('"1"'); }); - it('should parse null', () => { checkAction('null'); }); + it('should parse null', () => { + checkAction('null'); + }); - it('should parse undefined', () => { checkAction('undefined'); }); + it('should parse undefined', () => { + checkAction('undefined'); + }); it('should parse unary - expressions', () => { checkAction('-1', '0 - 1'); @@ -47,10 +53,13 @@ describe('parser', () => { checkAction('a!!!!.b'); }); - it('should parse multiplicative expressions', - () => { checkAction('3*4/2%5', '3 * 4 / 2 % 5'); }); + it('should parse multiplicative expressions', () => { + checkAction('3*4/2%5', '3 * 4 / 2 % 5'); + }); - it('should parse additive expressions', () => { checkAction('3 + 6 - 2'); }); + it('should parse additive expressions', () => { + checkAction('3 + 6 - 2'); + }); it('should parse relational expressions', () => { checkAction('2 < 3'); @@ -74,14 +83,21 @@ describe('parser', () => { checkAction('true || false'); }); - it('should parse grouped expressions', () => { checkAction('(1 + 2) * 3', '1 + 2 * 3'); }); + it('should parse grouped expressions', () => { + checkAction('(1 + 2) * 3', '1 + 2 * 3'); + }); - it('should ignore comments in expressions', () => { checkAction('a //comment', 'a'); }); + it('should ignore comments in expressions', () => { + checkAction('a //comment', 'a'); + }); - it('should retain // in string literals', - () => { checkAction(`"http://www.google.com"`, `"http://www.google.com"`); }); + it('should retain // in string literals', () => { + checkAction(`"http://www.google.com"`, `"http://www.google.com"`); + }); - it('should parse an empty string', () => { checkAction(''); }); + it('should parse an empty string', () => { + checkAction(''); + }); describe('literals', () => { it('should parse array', () => { @@ -133,7 +149,9 @@ describe('parser', () => { }); describe('functional calls', () => { - it('should parse function calls', () => { checkAction('fn()(1, 2)'); }); + it('should parse function calls', () => { + checkAction('fn()(1, 2)'); + }); }); describe('conditional', () => { @@ -154,20 +172,26 @@ describe('parser', () => { checkAction('a = 123; b = 234;'); }); - it('should report on safe field assignments', - () => { expectActionError('a?.a = 123', 'cannot be used in the assignment'); }); + it('should report on safe field assignments', () => { + expectActionError('a?.a = 123', 'cannot be used in the assignment'); + }); - it('should support array updates', () => { checkAction('a[0] = 200'); }); + it('should support array updates', () => { + checkAction('a[0] = 200'); + }); }); - it('should error when using pipes', - () => { expectActionError('x|blah', 'Cannot have a pipe'); }); + it('should error when using pipes', () => { + expectActionError('x|blah', 'Cannot have a pipe'); + }); - it('should store the source in the result', - () => { expect(parseAction('someExpr', 'someExpr')); }); + it('should store the source in the result', () => { + expect(parseAction('someExpr', 'someExpr')); + }); - it('should store the passed-in location', - () => { expect(parseAction('someExpr', 'location').location).toBe('location'); }); + it('should store the passed-in location', () => { + expect(parseAction('someExpr', 'location').location).toBe('location'); + }); it('should report when encountering interpolation', () => { expectActionError('{{a()}}', 'Got interpolation ({{}}) where expression was expected'); @@ -175,11 +199,13 @@ describe('parser', () => { }); describe('general error handling', () => { - it('should report an unexpected token', - () => { expectActionError('[1,2] trac', 'Unexpected token \'trac\''); }); + it('should report an unexpected token', () => { + expectActionError('[1,2] trac', 'Unexpected token \'trac\''); + }); - it('should report reasonable error for unconsumed tokens', - () => { expectActionError(')', 'Unexpected token ) at column 1 in [)]'); }); + it('should report reasonable error for unconsumed tokens', () => { + expectActionError(')', 'Unexpected token ) at column 1 in [)]'); + }); it('should report a missing expected token', () => { expectActionError('a(b', 'Missing expected ) at the end of the expression [a(b]'); @@ -206,12 +232,17 @@ describe('parser', () => { expectBindingError('"Foo"|"uppercase"', 'identifier or keyword'); }); - it('should parse quoted expressions', () => { checkBinding('a:b', 'a:b'); }); + it('should parse quoted expressions', () => { + checkBinding('a:b', 'a:b'); + }); - it('should not crash when prefix part is not tokenizable', - () => { checkBinding('"a:b"', '"a:b"'); }); + it('should not crash when prefix part is not tokenizable', () => { + checkBinding('"a:b"', '"a:b"'); + }); - it('should ignore whitespace around quote prefix', () => { checkBinding(' a :b', 'a:b'); }); + it('should ignore whitespace around quote prefix', () => { + checkBinding(' a :b', 'a:b'); + }); it('should refuse prefixes that are not single identifiers', () => { expectBindingError('a + b:c', ''); @@ -219,31 +250,41 @@ describe('parser', () => { }); }); - it('should store the source in the result', - () => { expect(parseBinding('someExpr').source).toBe('someExpr'); }); + it('should store the source in the result', () => { + expect(parseBinding('someExpr').source).toBe('someExpr'); + }); - it('should store the passed-in location', - () => { expect(parseBinding('someExpr', 'location').location).toBe('location'); }); + it('should store the passed-in location', () => { + expect(parseBinding('someExpr', 'location').location).toBe('location'); + }); - it('should report chain expressions', - () => { expectError(parseBinding('1;2'), 'contain chained expression'); }); + it('should report chain expressions', () => { + expectError(parseBinding('1;2'), 'contain chained expression'); + }); - it('should report assignment', - () => { expectError(parseBinding('a=2'), 'contain assignments'); }); + it('should report assignment', () => { + expectError(parseBinding('a=2'), 'contain assignments'); + }); it('should report when encountering interpolation', () => { expectBindingError('{{a.b}}', 'Got interpolation ({{}}) where expression was expected'); }); - it('should parse conditional expression', () => { checkBinding('a < b ? a : b'); }); - - it('should ignore comments in bindings', () => { checkBinding('a //comment', 'a'); }); + it('should parse conditional expression', () => { + checkBinding('a < b ? a : b'); + }); - it('should retain // in string literals', - () => { checkBinding(`"http://www.google.com"`, `"http://www.google.com"`); }); + it('should ignore comments in bindings', () => { + checkBinding('a //comment', 'a'); + }); - it('should retain // in : microsyntax', () => { checkBinding('one:a//b', 'one:a//b'); }); + it('should retain // in string literals', () => { + checkBinding(`"http://www.google.com"`, `"http://www.google.com"`); + }); + it('should retain // in : microsyntax', () => { + checkBinding('one:a//b', 'one:a//b'); + }); }); describe('parseTemplateBindings', () => { @@ -555,11 +596,12 @@ describe('parser', () => { }); describe('parseInterpolation', () => { - it('should return null if no interpolation', - () => { expect(parseInterpolation('nothing')).toBe(null); }); + it('should return null if no interpolation', () => { + expect(parseInterpolation('nothing')).toBe(null); + }); it('should parse no prefix/suffix interpolation', () => { - const ast = parseInterpolation('{{a}}') !.ast as Interpolation; + const ast = parseInterpolation('{{a}}')!.ast as Interpolation; expect(ast.strings).toEqual(['', '']); expect(ast.expressions.length).toEqual(1); expect(ast.expressions[0].name).toEqual('a'); @@ -567,22 +609,23 @@ describe('parser', () => { it('should parse prefix/suffix with multiple interpolation', () => { const originalExp = 'before {{ a }} middle {{ b }} after'; - const ast = parseInterpolation(originalExp) !.ast; + const ast = parseInterpolation(originalExp)!.ast; expect(unparse(ast)).toEqual(originalExp); validate(ast); }); it('should report empty interpolation expressions', () => { expectError( - parseInterpolation('{{}}') !, - 'Blank expressions are not allowed in interpolated strings'); + parseInterpolation('{{}}')!, 'Blank expressions are not allowed in interpolated strings'); expectError( - parseInterpolation('foo {{ }}') !, + parseInterpolation('foo {{ }}')!, 'Parser Error: Blank expressions are not allowed in interpolated strings'); }); - it('should parse conditional expression', () => { checkInterpolation('{{ a < b ? a : b }}'); }); + it('should parse conditional expression', () => { + checkInterpolation('{{ a < b ? a : b }}'); + }); it('should parse expression with newline characters', () => { checkInterpolation(`{{ 'foo' +\n 'bar' +\r 'baz' }}`, `{{ "foo" + "bar" + "baz" }}`); @@ -591,15 +634,16 @@ describe('parser', () => { it('should support custom interpolation', () => { const parser = new Parser(new Lexer()); const ast = - parser.parseInterpolation('{% a %}', null, 0, {start: '{%', end: '%}'}) !.ast as any; + parser.parseInterpolation('{% a %}', null, 0, {start: '{%', end: '%}'})!.ast as any; expect(ast.strings).toEqual(['', '']); expect(ast.expressions.length).toEqual(1); expect(ast.expressions[0].name).toEqual('a'); }); describe('comments', () => { - it('should ignore comments in interpolation expressions', - () => { checkInterpolation('{{a //comment}}', '{{ a }}'); }); + it('should ignore comments in interpolation expressions', () => { + checkInterpolation('{{a //comment}}', '{{ a }}'); + }); it('should retain // in single quote strings', () => { checkInterpolation(`{{ 'http://www.google.com' }}`, `{{ "http://www.google.com" }}`); @@ -609,18 +653,19 @@ describe('parser', () => { checkInterpolation(`{{ "http://www.google.com" }}`, `{{ "http://www.google.com" }}`); }); - it('should ignore comments after string literals', - () => { checkInterpolation(`{{ "a//b" //comment }}`, `{{ "a//b" }}`); }); + it('should ignore comments after string literals', () => { + checkInterpolation(`{{ "a//b" //comment }}`, `{{ "a//b" }}`); + }); it('should retain // in complex strings', () => { checkInterpolation( `{{"//a\'//b\`//c\`//d\'//e" //comment}}`, `{{ "//a\'//b\`//c\`//d\'//e" }}`); }); - it('should retain // in nested, unterminated strings', - () => { checkInterpolation(`{{ "a\'b\`" //comment}}`, `{{ "a\'b\`" }}`); }); + it('should retain // in nested, unterminated strings', () => { + checkInterpolation(`{{ "a\'b\`" //comment}}`, `{{ "a\'b\`" }}`); + }); }); - }); describe('parseSimpleBinding', () => { @@ -670,12 +715,12 @@ describe('parser', () => { describe('offsets', () => { it('should retain the offsets of an interpolation', () => { - const interpolations = splitInterpolation('{{a}} {{b}} {{c}}') !; + const interpolations = splitInterpolation('{{a}} {{b}} {{c}}')!; expect(interpolations.offsets).toEqual([2, 9, 16]); }); it('should retain the offsets into the expression AST of interpolations', () => { - const source = parseInterpolation('{{a}} {{b}} {{c}}') !; + const source = parseInterpolation('{{a}} {{b}} {{c}}')!; const interpolation = source.ast as Interpolation; expect(interpolation.expressions.map(e => e.span.start)).toEqual([2, 9, 16]); }); @@ -722,7 +767,7 @@ function parseSimpleBinding(text: string, location: any = null, offset: number = } function checkInterpolation(exp: string, expected?: string) { - const ast = parseInterpolation(exp) !; + const ast = parseInterpolation(exp)!; if (expected == null) expected = exp; expect(unparse(ast)).toEqual(expected); validate(ast); diff --git a/packages/compiler/test/expression_parser/utils/unparser.ts b/packages/compiler/test/expression_parser/utils/unparser.ts index e5ea2f9f89ba1..fd2574e388848 100644 --- a/packages/compiler/test/expression_parser/utils/unparser.ts +++ b/packages/compiler/test/expression_parser/utils/unparser.ts @@ -12,9 +12,9 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../src/ml class Unparser implements AstVisitor { private static _quoteRegExp = /"/g; // TODO(issue/24571): remove '!'. - private _expression !: string; + private _expression!: string; // TODO(issue/24571): remove '!'. - private _interpolationConfig !: InterpolationConfig; + private _interpolationConfig!: InterpolationConfig; unparse(ast: AST, interpolationConfig: InterpolationConfig) { this._expression = ''; @@ -69,7 +69,7 @@ class Unparser implements AstVisitor { } visitFunctionCall(ast: FunctionCall, context: any) { - this._visit(ast.target !); + this._visit(ast.target!); this._expression += '('; let isFirst = true; ast.args.forEach(arg => { @@ -137,7 +137,7 @@ class Unparser implements AstVisitor { visitLiteralPrimitive(ast: LiteralPrimitive, context: any) { if (typeof ast.value === 'string') { - this._expression += `"${ast.value.replace( Unparser._quoteRegExp, '\"')}"`; + this._expression += `"${ast.value.replace(Unparser._quoteRegExp, '\"')}"`; } else { this._expression += `${ast.value}`; } @@ -186,7 +186,9 @@ class Unparser implements AstVisitor { this._expression += `${ast.prefix}:${ast.uninterpretedExpression}`; } - private _visit(ast: AST) { ast.visit(this); } + private _visit(ast: AST) { + ast.visit(this); + } } const sharedUnparser = new Unparser(); diff --git a/packages/compiler/test/expression_parser/utils/validator.ts b/packages/compiler/test/expression_parser/utils/validator.ts index cfdc851da5721..2ddbbcd9c080d 100644 --- a/packages/compiler/test/expression_parser/utils/validator.ts +++ b/packages/compiler/test/expression_parser/utils/validator.ts @@ -22,8 +22,8 @@ class ASTValidator extends RecursiveAstVisitor { if (!inSpan(ast.span, this.parentSpan)) { if (this.parentSpan) { const parentSpan = this.parentSpan as ParseSpan; - throw Error( - `Invalid AST span [expected (${ast.span.start}, ${ast.span.end}) to be in (${parentSpan.start}, ${parentSpan.end}) for ${unparse(ast)}`); + throw Error(`Invalid AST span [expected (${ast.span.start}, ${ast.span.end}) to be in (${ + parentSpan.start}, ${parentSpan.end}) for ${unparse(ast)}`); } else { throw Error(`Invalid root AST span for ${unparse(ast)}`); } @@ -111,7 +111,7 @@ class ASTValidator extends RecursiveAstVisitor { } } -function inSpan(span: ParseSpan, parentSpan: ParseSpan | undefined): parentSpan is ParseSpan { +function inSpan(span: ParseSpan, parentSpan: ParseSpan|undefined): parentSpan is ParseSpan { return !parentSpan || (span.start >= parentSpan.start && span.end <= parentSpan.end); } diff --git a/packages/compiler/test/i18n/digest_spec.ts b/packages/compiler/test/i18n/digest_spec.ts index e77e49735f3a5..b37988d8ba024 100644 --- a/packages/compiler/test/i18n/digest_spec.ts +++ b/packages/compiler/test/i18n/digest_spec.ts @@ -27,14 +27,17 @@ import {computeMsgId, digest, sha1} from '../../src/i18n/digest'; }); describe('sha1', () => { - it('should work on empty strings', - () => { expect(sha1('')).toEqual('da39a3ee5e6b4b0d3255bfef95601890afd80709'); }); + it('should work on empty strings', () => { + expect(sha1('')).toEqual('da39a3ee5e6b4b0d3255bfef95601890afd80709'); + }); - it('should returns the sha1 of "hello world"', - () => { expect(sha1('abc')).toEqual('a9993e364706816aba3e25717850c26c9cd0d89d'); }); + it('should returns the sha1 of "hello world"', () => { + expect(sha1('abc')).toEqual('a9993e364706816aba3e25717850c26c9cd0d89d'); + }); - it('should returns the sha1 of unicode strings', - () => { expect(sha1('你好,世界')).toEqual('3becb03b015ed48050611c8d7afe4b88f70d5a20'); }); + it('should returns the sha1 of unicode strings', () => { + expect(sha1('你好,世界')).toEqual('3becb03b015ed48050611c8d7afe4b88f70d5a20'); + }); it('should support arbitrary string size', () => { // node.js reference code: @@ -89,8 +92,9 @@ import {computeMsgId, digest, sha1} from '../../src/i18n/digest'; '': '4416290763660062288', }; - Object.keys(fixtures).forEach( - msg => { expect(computeMsgId(msg, '')).toEqual(fixtures[msg]); }); + Object.keys(fixtures).forEach(msg => { + expect(computeMsgId(msg, '')).toEqual(fixtures[msg]); + }); }); it('should work on well known inputs with meaning', () => { @@ -100,8 +104,9 @@ import {computeMsgId, digest, sha1} from '../../src/i18n/digest'; '3993998469942805487': ['View', 'Gmail UI'], }; - Object.keys(fixtures).forEach( - id => { expect(computeMsgId(fixtures[id][0], fixtures[id][1])).toEqual(id); }); + Object.keys(fixtures).forEach(id => { + expect(computeMsgId(fixtures[id][0], fixtures[id][1])).toEqual(id); + }); }); it('should support arbitrary string size', () => { @@ -116,7 +121,6 @@ import {computeMsgId, digest, sha1} from '../../src/i18n/digest'; } expect(computeMsgId(result, '')).toEqual('2122606631351252558'); }); - }); }); } diff --git a/packages/compiler/test/i18n/extractor_merger_spec.ts b/packages/compiler/test/i18n/extractor_merger_spec.ts index 52eec7c548a25..7ad468fb1ac25 100644 --- a/packages/compiler/test/i18n/extractor_merger_spec.ts +++ b/packages/compiler/test/i18n/extractor_merger_spec.ts @@ -94,8 +94,9 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; ]); }); - it('should not create a message for empty elements', - () => { expect(extract('<div i18n="m|d"></div>')).toEqual([]); }); + it('should not create a message for empty elements', () => { + expect(extract('<div i18n="m|d"></div>')).toEqual([]); + }); it('should ignore implicit elements in translatable elements', () => { expect(extract('<div i18n="m|d"><p></p></div>', ['p'])).toEqual([ @@ -138,7 +139,8 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; ], [ [ - 'text', '<ph tag name="START_PARAGRAPH">html, <ph tag' + + 'text', + '<ph tag name="START_PARAGRAPH">html, <ph tag' + ' name="START_BOLD_TEXT">nested</ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">', '<ph icu name="ICU">{count, plural, =0 {[<ph tag' + ' name="START_TAG_SPAN">html</ph name="CLOSE_TAG_SPAN">]}}</ph>', @@ -156,8 +158,9 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; ]); }); - it('should not create a message for empty blocks', - () => { expect(extract(`<!-- i18n: meaning1|desc1 --><!-- /i18n -->`)).toEqual([]); }); + it('should not create a message for empty blocks', () => { + expect(extract(`<!-- i18n: meaning1|desc1 --><!-- /i18n -->`)).toEqual([]); + }); }); describe('ICU messages', () => { @@ -199,8 +202,9 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; ]); }); - it('should not extract ICU messages outside of i18n sections', - () => { expect(extract('{count, plural, =0 {text}}')).toEqual([]); }); + it('should not extract ICU messages outside of i18n sections', () => { + expect(extract('{count, plural, =0 {text}}')).toEqual([]); + }); it('should ignore nested ICU messages', () => { expect(extract('<div i18n="m|d">{count, plural, =0 { {sex, select, male {m}} }}</div>')) @@ -280,8 +284,9 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; ]); }); - it('should not create a message for empty attributes', - () => { expect(extract('<div i18n-title="m|d" title></div>')).toEqual([]); }); + it('should not create a message for empty attributes', () => { + expect(extract('<div i18n-title="m|d" title></div>')).toEqual([]); + }); }); describe('implicit elements', () => { @@ -292,7 +297,7 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; }); it('should allow nested implicit elements', () => { - let result: any[] = undefined !; + let result: any[] = undefined!; expect(() => { result = extract('<div>outer<div>inner</div></div>', ['div']); @@ -302,7 +307,6 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; [['outer', '<ph tag name="START_TAG_DIV">inner</ph name="CLOSE_TAG_DIV">'], '', '', ''], ]); }); - }); describe('implicit attributes', () => { @@ -341,7 +345,6 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util'; ['Could not start a block inside a translatable section', '<!--'], ['Trying to close an unopened block', '<!--'], ]); - }); it('should report unclosed blocks', () => { @@ -527,7 +530,7 @@ function fakeTranslate( messages.forEach(message => { const id = digest(message); const text = serializeI18nNodes(message.nodes).join('').replace(/</g, '['); - i18nMsgMap[id] = [new i18n.Text(`**${text}**`, null !)]; + i18nMsgMap[id] = [new i18n.Text(`**${text}**`, null!)]; }); const translationBundle = new TranslationBundle(i18nMsgMap, null, digest); diff --git a/packages/compiler/test/i18n/i18n_html_parser_spec.ts b/packages/compiler/test/i18n/i18n_html_parser_spec.ts index e4edccc85bcb9..a5e0b74223caa 100644 --- a/packages/compiler/test/i18n/i18n_html_parser_spec.ts +++ b/packages/compiler/test/i18n/i18n_html_parser_spec.ts @@ -26,6 +26,5 @@ import {ParseTreeResult} from '@angular/compiler/src/ml_parser/parser'; i18nHtmlParser.parse('source', 'url'); expect(TranslationBundle.load).toHaveBeenCalledTimes(1); }); - }); } diff --git a/packages/compiler/test/i18n/i18n_parser_spec.ts b/packages/compiler/test/i18n/i18n_parser_spec.ts index cc8c9dd3afb3c..7c4c75daf9da9 100644 --- a/packages/compiler/test/i18n/i18n_parser_spec.ts +++ b/packages/compiler/test/i18n/i18n_parser_spec.ts @@ -14,7 +14,6 @@ import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/inte { describe('I18nParser', () => { - describe('elements', () => { it('should extract from elements', () => { expect(_humanizeMessages('<div i18n="m|d">text</div>')).toEqual([ @@ -34,11 +33,13 @@ import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/inte ]); }); - it('should not create a message for empty elements', - () => { expect(_humanizeMessages('<div i18n="m|d"></div>')).toEqual([]); }); + it('should not create a message for empty elements', () => { + expect(_humanizeMessages('<div i18n="m|d"></div>')).toEqual([]); + }); - it('should not create a message for plain elements', - () => { expect(_humanizeMessages('<div></div>')).toEqual([]); }); + it('should not create a message for plain elements', () => { + expect(_humanizeMessages('<div></div>')).toEqual([]); + }); it('should support void elements', () => { expect(_humanizeMessages('<div i18n="m|d"><p><br></p></div>')).toEqual([ @@ -115,8 +116,9 @@ import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/inte ]); }); - it('should not create a message for empty attributes', - () => { expect(_humanizeMessages('<div i18n-title="m|d" title></div>')).toEqual([]); }); + it('should not create a message for empty attributes', () => { + expect(_humanizeMessages('<div i18n-title="m|d" title></div>')).toEqual([]); + }); }); describe('interpolation', () => { @@ -252,7 +254,6 @@ import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/inte expect(_humanizePlaceholders(html)).toEqual([ 'START_PARAGRAPH=<p>, CLOSE_PARAGRAPH=</p>, START_PARAGRAPH_1=<p other>', ]); - }); it('should reuse the same placeholder name for interpolations', () => { @@ -302,7 +303,6 @@ import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/inte '', '', ]); - }); }); }); diff --git a/packages/compiler/test/i18n/integration_common.ts b/packages/compiler/test/i18n/integration_common.ts index 2b59094aae5f8..65d6e6fb0834b 100644 --- a/packages/compiler/test/i18n/integration_common.ts +++ b/packages/compiler/test/i18n/integration_common.ts @@ -20,11 +20,11 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }) export class I18nComponent { // TODO(issue/24571): remove '!'. - count !: number; + count!: number; // TODO(issue/24571): remove '!'. - sex !: string; + sex!: string; // TODO(issue/24571): remove '!'. - sexB !: string; + sexB!: string; response: any = {getItemsList: (): any[] => []}; } diff --git a/packages/compiler/test/i18n/integration_xliff2_spec.ts b/packages/compiler/test/i18n/integration_xliff2_spec.ts index e7f82d09ccd6d..5feeca415cd56 100644 --- a/packages/compiler/test/i18n/integration_xliff2_spec.ts +++ b/packages/compiler/test/i18n/integration_xliff2_spec.ts @@ -13,7 +13,7 @@ import {Xliff2} from '@angular/compiler/src/i18n/serializers/xliff2'; import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser'; import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/interpolation_config'; import {DebugElement, TRANSLATIONS, TRANSLATIONS_FORMAT} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {SpyResourceLoader} from '../spies'; @@ -22,7 +22,6 @@ import {FrLocalization, HTML, I18nComponent, validateHtml} from './integration_c { describe('i18n XLIFF 2.0 integration spec', () => { - beforeEach(async(() => { TestBed.configureCompiler({ providers: [ diff --git a/packages/compiler/test/i18n/integration_xliff_spec.ts b/packages/compiler/test/i18n/integration_xliff_spec.ts index 5c8daf498440d..bbbcbd8bf6503 100644 --- a/packages/compiler/test/i18n/integration_xliff_spec.ts +++ b/packages/compiler/test/i18n/integration_xliff_spec.ts @@ -13,7 +13,7 @@ import {Xliff} from '@angular/compiler/src/i18n/serializers/xliff'; import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser'; import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/interpolation_config'; import {DebugElement, TRANSLATIONS, TRANSLATIONS_FORMAT} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {SpyResourceLoader} from '../spies'; @@ -22,7 +22,6 @@ import {FrLocalization, HTML, I18nComponent, validateHtml} from './integration_c { describe('i18n XLIFF integration spec', () => { - beforeEach(async(() => { TestBed.configureCompiler({ providers: [ diff --git a/packages/compiler/test/i18n/integration_xmb_xtb_spec.ts b/packages/compiler/test/i18n/integration_xmb_xtb_spec.ts index 59853bb233487..7ed6fd92fd1fb 100644 --- a/packages/compiler/test/i18n/integration_xmb_xtb_spec.ts +++ b/packages/compiler/test/i18n/integration_xmb_xtb_spec.ts @@ -13,7 +13,7 @@ import {Xmb} from '@angular/compiler/src/i18n/serializers/xmb'; import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser'; import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/interpolation_config'; import {DebugElement, TRANSLATIONS, TRANSLATIONS_FORMAT} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {SpyResourceLoader} from '../spies'; @@ -22,7 +22,6 @@ import {FrLocalization, HTML, I18nComponent, validateHtml} from './integration_c { describe('i18n XMB/XTB integration spec', () => { - beforeEach(async(() => { TestBed.configureCompiler({ providers: [ diff --git a/packages/compiler/test/i18n/message_bundle_spec.ts b/packages/compiler/test/i18n/message_bundle_spec.ts index ef9cbde561b70..c01a49fb28c42 100644 --- a/packages/compiler/test/i18n/message_bundle_spec.ts +++ b/packages/compiler/test/i18n/message_bundle_spec.ts @@ -18,7 +18,9 @@ import {DEFAULT_INTERPOLATION_CONFIG} from '../../src/ml_parser/interpolation_co describe('Messages', () => { let messages: MessageBundle; - beforeEach(() => { messages = new MessageBundle(new HtmlParser, [], {}); }); + beforeEach(() => { + messages = new MessageBundle(new HtmlParser, [], {}); + }); it('should extract the message to the catalog', () => { messages.updateFromTemplate( @@ -48,11 +50,13 @@ class _TestSerializer extends Serializer { } load(content: string, url: string): - {locale: string | null, i18nNodesByMsgId: {[id: string]: i18n.Node[]}} { + {locale: string|null, i18nNodesByMsgId: {[id: string]: i18n.Node[]}} { return {locale: null, i18nNodesByMsgId: {}}; } - digest(msg: i18n.Message): string { return msg.id || `default`; } + digest(msg: i18n.Message): string { + return msg.id || `default`; + } } function humanizeMessages(catalog: MessageBundle): string[] { diff --git a/packages/compiler/test/i18n/serializers/i18n_ast_spec.ts b/packages/compiler/test/i18n/serializers/i18n_ast_spec.ts index 3edf6951a5000..298ca1d80b179 100644 --- a/packages/compiler/test/i18n/serializers/i18n_ast_spec.ts +++ b/packages/compiler/test/i18n/serializers/i18n_ast_spec.ts @@ -36,13 +36,13 @@ import {_extractMessages} from '../i18n_parser_spec'; const visitor = new RecurseVisitor(); const container = new i18n.Container( [ - new i18n.Text('', null !), - new i18n.Placeholder('', '', null !), - new i18n.IcuPlaceholder(null !, '', null !), + new i18n.Text('', null!), + new i18n.Placeholder('', '', null!), + new i18n.IcuPlaceholder(null!, '', null!), ], - null !); - const tag = new i18n.TagPlaceholder('', {}, '', '', [container], false, null !); - const icu = new i18n.Icu('', '', {tag}, null !); + null!); + const tag = new i18n.TagPlaceholder('', {}, '', '', [container], false, null!); + const icu = new i18n.Icu('', '', {tag}, null!); icu.visit(visitor); expect(visitor.textCount).toEqual(1); @@ -58,9 +58,15 @@ class RecurseVisitor extends i18n.RecurseVisitor { phCount = 0; icuPhCount = 0; - visitText(text: i18n.Text, context?: any): any { this.textCount++; } + visitText(text: i18n.Text, context?: any): any { + this.textCount++; + } - visitPlaceholder(ph: i18n.Placeholder, context?: any): any { this.phCount++; } + visitPlaceholder(ph: i18n.Placeholder, context?: any): any { + this.phCount++; + } - visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { this.icuPhCount++; } + visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { + this.icuPhCount++; + } } diff --git a/packages/compiler/test/i18n/serializers/placeholder_spec.ts b/packages/compiler/test/i18n/serializers/placeholder_spec.ts index e3aa205d176ca..5f5c823aff981 100644 --- a/packages/compiler/test/i18n/serializers/placeholder_spec.ts +++ b/packages/compiler/test/i18n/serializers/placeholder_spec.ts @@ -12,7 +12,9 @@ import {PlaceholderRegistry} from '../../../src/i18n/serializers/placeholder'; describe('PlaceholderRegistry', () => { let reg: PlaceholderRegistry; - beforeEach(() => { reg = new PlaceholderRegistry(); }); + beforeEach(() => { + reg = new PlaceholderRegistry(); + }); describe('tag placeholder', () => { it('should generate names for well known tags', () => { @@ -84,7 +86,6 @@ import {PlaceholderRegistry} from '../../../src/i18n/serializers/placeholder'; expect(reg.getPlaceholderName('name1', 'content')).toEqual('NAME1'); expect(reg.getPlaceholderName('name2', 'content')).toEqual('NAME2'); }); - }); }); } \ No newline at end of file diff --git a/packages/compiler/test/i18n/serializers/xliff2_spec.ts b/packages/compiler/test/i18n/serializers/xliff2_spec.ts index 027bdc5bdce2b..c63146d3bc1ee 100644 --- a/packages/compiler/test/i18n/serializers/xliff2_spec.ts +++ b/packages/compiler/test/i18n/serializers/xliff2_spec.ts @@ -261,65 +261,67 @@ lignes</target> `; (function() { - const serializer = new Xliff2(); +const serializer = new Xliff2(); - function toXliff(html: string, locale: string | null = null): string { - const catalog = new MessageBundle(new HtmlParser, [], {}, locale); - catalog.updateFromTemplate(html, 'file.ts', DEFAULT_INTERPOLATION_CONFIG); - return catalog.write(serializer); - } +function toXliff(html: string, locale: string|null = null): string { + const catalog = new MessageBundle(new HtmlParser, [], {}, locale); + catalog.updateFromTemplate(html, 'file.ts', DEFAULT_INTERPOLATION_CONFIG); + return catalog.write(serializer); +} - function loadAsMap(xliff: string): {[id: string]: string} { - const {i18nNodesByMsgId} = serializer.load(xliff, 'url'); +function loadAsMap(xliff: string): {[id: string]: string} { + const {i18nNodesByMsgId} = serializer.load(xliff, 'url'); - const msgMap: {[id: string]: string} = {}; - Object.keys(i18nNodesByMsgId) - .forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('')); + const msgMap: {[id: string]: string} = {}; + Object.keys(i18nNodesByMsgId) + .forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('')); - return msgMap; - } + return msgMap; +} - describe('XLIFF 2.0 serializer', () => { - describe('write', () => { - it('should write a valid xliff 2.0 file', - () => { expect(toXliff(HTML)).toEqual(WRITE_XLIFF); }); - it('should write a valid xliff 2.0 file with a source language', - () => { expect(toXliff(HTML, 'fr')).toContain('srcLang="fr"'); }); +describe('XLIFF 2.0 serializer', () => { + describe('write', () => { + it('should write a valid xliff 2.0 file', () => { + expect(toXliff(HTML)).toEqual(WRITE_XLIFF); }); + it('should write a valid xliff 2.0 file with a source language', () => { + expect(toXliff(HTML, 'fr')).toContain('srcLang="fr"'); + }); + }); - describe('load', () => { - it('should load XLIFF files', () => { - expect(loadAsMap(LOAD_XLIFF)).toEqual({ - '1933478729560469763': 'etubirtta elbatalsnart', - '7056919470098446707': - '<ph name="INTERPOLATION"/> <ph name="START_BOLD_TEXT"/>sredlohecalp htiw<ph name="CLOSE_BOLD_TEXT"/> tnemele elbatalsnart', - '2981514368455622387': - '{VAR_PLURAL, plural, =0 {[<ph name="START_PARAGRAPH"/>, TEST, <ph name="CLOSE_PARAGRAPH"/>]}}', - 'i': 'oof', - '6440235004920703622': - '<ph name="START_BOLD_TEXT"/><ph name="START_UNDERLINED_TEXT"/>txeT <ph name="INTERPOLATION"/><ph name="CLOSE_UNDERLINED_TEXT"/><ph name="CLOSE_BOLD_TEXT"/>', - '8779402634269838862': - '<ph name="TAG_IMG_1"/><ph name="TAG_IMG"/><ph name="LINE_BREAK"/>', - '6536355551500405293': '<ph name="START_TAG_SPAN"/><ph name="CLOSE_TAG_SPAN"/> olleh', - 'baz': - '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}', - '6997386649824869937': 'Test: <ph name="ICU"/>', - '5229984852258993423': '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph' + - ' name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}, =other {[beaucoup]}}', - '2340165783990709777': `multi + describe('load', () => { + it('should load XLIFF files', () => { + expect(loadAsMap(LOAD_XLIFF)).toEqual({ + '1933478729560469763': 'etubirtta elbatalsnart', + '7056919470098446707': + '<ph name="INTERPOLATION"/> <ph name="START_BOLD_TEXT"/>sredlohecalp htiw<ph name="CLOSE_BOLD_TEXT"/> tnemele elbatalsnart', + '2981514368455622387': + '{VAR_PLURAL, plural, =0 {[<ph name="START_PARAGRAPH"/>, TEST, <ph name="CLOSE_PARAGRAPH"/>]}}', + 'i': 'oof', + '6440235004920703622': + '<ph name="START_BOLD_TEXT"/><ph name="START_UNDERLINED_TEXT"/>txeT <ph name="INTERPOLATION"/><ph name="CLOSE_UNDERLINED_TEXT"/><ph name="CLOSE_BOLD_TEXT"/>', + '8779402634269838862': '<ph name="TAG_IMG_1"/><ph name="TAG_IMG"/><ph name="LINE_BREAK"/>', + '6536355551500405293': '<ph name="START_TAG_SPAN"/><ph name="CLOSE_TAG_SPAN"/> olleh', + 'baz': + '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}', + '6997386649824869937': 'Test: <ph name="ICU"/>', + '5229984852258993423': '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph' + + ' name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}, =other {[beaucoup]}}', + '2340165783990709777': `multi lignes`, - 'mrk-test': 'Vous pouvez utiliser votre propre namespace.', - 'mrk-test2': 'Vous pouvez utiliser votre propre namespace.' - }); + 'mrk-test': 'Vous pouvez utiliser votre propre namespace.', + 'mrk-test2': 'Vous pouvez utiliser votre propre namespace.' }); + }); - it('should return the target locale', - () => { expect(serializer.load(LOAD_XLIFF, 'url').locale).toEqual('fr'); }); + it('should return the target locale', () => { + expect(serializer.load(LOAD_XLIFF, 'url').locale).toEqual('fr'); }); + }); - describe('structure errors', () => { - it('should throw when a wrong xliff version is used', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + describe('structure errors', () => { + it('should throw when a wrong xliff version is used', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="ng2.template"> <body> @@ -331,13 +333,13 @@ lignes`, </file> </xliff>`; - expect(() => { - loadAsMap(XLIFF); - }).toThrowError(/The XLIFF file version 1.2 is not compatible with XLIFF 2.0 serializer/); - }); + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(/The XLIFF file version 1.2 is not compatible with XLIFF 2.0 serializer/); + }); - it('should throw when an unit has no translation', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + it('should throw when an unit has no translation', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en"> <file original="ng.template" id="ngi18n"> <unit id="missingtarget"> @@ -348,14 +350,14 @@ lignes`, </file> </xliff>`; - expect(() => { - loadAsMap(XLIFF); - }).toThrowError(/Message missingtarget misses a translation/); - }); + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(/Message missingtarget misses a translation/); + }); - it('should throw when an unit has no id attribute', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + it('should throw when an unit has no id attribute', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en"> <file original="ng.template" id="ngi18n"> <unit> @@ -367,11 +369,13 @@ lignes`, </file> </xliff>`; - expect(() => { loadAsMap(XLIFF); }).toThrowError(/<unit> misses the "id" attribute/); - }); + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(/<unit> misses the "id" attribute/); + }); - it('should throw on duplicate unit id', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + it('should throw on duplicate unit id', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en"> <file original="ng.template" id="ngi18n"> <unit id="deadbeef"> @@ -389,15 +393,15 @@ lignes`, </file> </xliff>`; - expect(() => { - loadAsMap(XLIFF); - }).toThrowError(/Duplicated translations for msg deadbeef/); - }); + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(/Duplicated translations for msg deadbeef/); }); + }); - describe('message errors', () => { - it('should throw on unknown message tags', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + describe('message errors', () => { + it('should throw on unknown message tags', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en"> <file original="ng.template" id="ngi18n"> <unit id="deadbeef"> @@ -409,13 +413,15 @@ lignes`, </file> </xliff>`; - expect(() => { loadAsMap(XLIFF); }) - .toThrowError(new RegExp( - escapeRegExp(`[ERROR ->]<b>msg should contain only ph and pc tags</b>`))); - }); + expect(() => { + loadAsMap(XLIFF); + }) + .toThrowError( + new RegExp(escapeRegExp(`[ERROR ->]<b>msg should contain only ph and pc tags</b>`))); + }); - it('should throw when a placeholder misses an id attribute', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + it('should throw when a placeholder misses an id attribute', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en"> <file original="ng.template" id="ngi18n"> <unit id="deadbeef"> @@ -427,10 +433,10 @@ lignes`, </file> </xliff>`; - expect(() => { - loadAsMap(XLIFF); - }).toThrowError(new RegExp(escapeRegExp(`<ph> misses the "equiv" attribute`))); - }); + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(new RegExp(escapeRegExp(`<ph> misses the "equiv" attribute`))); }); }); +}); })(); diff --git a/packages/compiler/test/i18n/serializers/xliff_spec.ts b/packages/compiler/test/i18n/serializers/xliff_spec.ts index 99ee251a57713..7a4935a7f0fcf 100644 --- a/packages/compiler/test/i18n/serializers/xliff_spec.ts +++ b/packages/compiler/test/i18n/serializers/xliff_spec.ts @@ -241,63 +241,67 @@ lignes</target> `; (function() { - const serializer = new Xliff(); +const serializer = new Xliff(); - function toXliff(html: string, locale: string | null = null): string { - const catalog = new MessageBundle(new HtmlParser, [], {}, locale); - catalog.updateFromTemplate(html, 'file.ts', DEFAULT_INTERPOLATION_CONFIG); - return catalog.write(serializer); - } +function toXliff(html: string, locale: string|null = null): string { + const catalog = new MessageBundle(new HtmlParser, [], {}, locale); + catalog.updateFromTemplate(html, 'file.ts', DEFAULT_INTERPOLATION_CONFIG); + return catalog.write(serializer); +} - function loadAsMap(xliff: string): {[id: string]: string} { - const {i18nNodesByMsgId} = serializer.load(xliff, 'url'); +function loadAsMap(xliff: string): {[id: string]: string} { + const {i18nNodesByMsgId} = serializer.load(xliff, 'url'); - const msgMap: {[id: string]: string} = {}; - Object.keys(i18nNodesByMsgId) - .forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('')); + const msgMap: {[id: string]: string} = {}; + Object.keys(i18nNodesByMsgId) + .forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('')); - return msgMap; - } + return msgMap; +} - describe('XLIFF serializer', () => { - describe('write', () => { - it('should write a valid xliff file', () => { expect(toXliff(HTML)).toEqual(WRITE_XLIFF); }); - it('should write a valid xliff file with a source language', - () => { expect(toXliff(HTML, 'fr')).toContain('file source-language="fr"'); }); +describe('XLIFF serializer', () => { + describe('write', () => { + it('should write a valid xliff file', () => { + expect(toXliff(HTML)).toEqual(WRITE_XLIFF); }); + it('should write a valid xliff file with a source language', () => { + expect(toXliff(HTML, 'fr')).toContain('file source-language="fr"'); + }); + }); - describe('load', () => { - it('should load XLIFF files', () => { - expect(loadAsMap(LOAD_XLIFF)).toEqual({ - '983775b9a51ce14b036be72d4cfd65d68d64e231': 'etubirtta elbatalsnart', - 'ec1d033f2436133c14ab038286c4f5df4697484a': - '<ph name="INTERPOLATION"/> footnemele elbatalsnart <ph name="START_BOLD_TEXT"/>sredlohecalp htiw<ph name="CLOSE_BOLD_TEXT"/>', - 'e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2': - '{VAR_PLURAL, plural, =0 {[<ph name="START_PARAGRAPH"/>, TEST, <ph name="CLOSE_PARAGRAPH"/>]}}', - 'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': 'oof', - 'i': 'toto', - 'bar': 'tata', - 'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d': - '<ph name="START_TAG_DIV"/><ph name="CLOSE_TAG_DIV"/><ph name="TAG_IMG"/><ph name="LINE_BREAK"/>', - 'empty target': '', - 'baz': - '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}', - '52ffa620dcd76247a56d5331f34e73f340a43cdb': 'Test: <ph name="ICU"/>', - '1503afd0ccc20ff01d5e2266a9157b7b342ba494': - '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph' + - ' name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}, =other {[beaucoup]}}', - 'fcfa109b0e152d4c217dbc02530be0bcb8123ad1': `multi + describe('load', () => { + it('should load XLIFF files', () => { + expect(loadAsMap(LOAD_XLIFF)).toEqual({ + '983775b9a51ce14b036be72d4cfd65d68d64e231': 'etubirtta elbatalsnart', + 'ec1d033f2436133c14ab038286c4f5df4697484a': + '<ph name="INTERPOLATION"/> footnemele elbatalsnart <ph name="START_BOLD_TEXT"/>sredlohecalp htiw<ph name="CLOSE_BOLD_TEXT"/>', + 'e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2': + '{VAR_PLURAL, plural, =0 {[<ph name="START_PARAGRAPH"/>, TEST, <ph name="CLOSE_PARAGRAPH"/>]}}', + 'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': 'oof', + 'i': 'toto', + 'bar': 'tata', + 'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d': + '<ph name="START_TAG_DIV"/><ph name="CLOSE_TAG_DIV"/><ph name="TAG_IMG"/><ph name="LINE_BREAK"/>', + 'empty target': '', + 'baz': + '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}', + '52ffa620dcd76247a56d5331f34e73f340a43cdb': 'Test: <ph name="ICU"/>', + '1503afd0ccc20ff01d5e2266a9157b7b342ba494': + '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph' + + ' name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}, =other {[beaucoup]}}', + 'fcfa109b0e152d4c217dbc02530be0bcb8123ad1': `multi lignes`, - 'mrk-test': 'Translated first sentence.', - 'mrk-test2': 'Translated first sentence.' - }); + 'mrk-test': 'Translated first sentence.', + 'mrk-test2': 'Translated first sentence.' }); + }); - it('should return the target locale', - () => { expect(serializer.load(LOAD_XLIFF, 'url').locale).toEqual('fr'); }); + it('should return the target locale', () => { + expect(serializer.load(LOAD_XLIFF, 'url').locale).toEqual('fr'); + }); - it('should ignore alt-trans targets', () => { - const XLIFF = ` + it('should ignore alt-trans targets', () => { + const XLIFF = ` <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" target-language="fr" datatype="plaintext" original="ng2.template"> <body> @@ -318,12 +322,12 @@ lignes`, </file> </xliff>`; - expect(loadAsMap(XLIFF)).toEqual({'registration.submit': 'Weiter'}); - }); + expect(loadAsMap(XLIFF)).toEqual({'registration.submit': 'Weiter'}); + }); - describe('structure errors', () => { - it('should throw when a trans-unit has no translation', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + describe('structure errors', () => { + it('should throw when a trans-unit has no translation', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="ng2.template"> <body> @@ -334,14 +338,14 @@ lignes`, </file> </xliff>`; - expect(() => { - loadAsMap(XLIFF); - }).toThrowError(/Message missingtarget misses a translation/); - }); + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(/Message missingtarget misses a translation/); + }); - it('should throw when a trans-unit has no id attribute', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + it('should throw when a trans-unit has no id attribute', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="ng2.template"> <body> @@ -353,13 +357,13 @@ lignes`, </file> </xliff>`; - expect(() => { - loadAsMap(XLIFF); - }).toThrowError(/<trans-unit> misses the "id" attribute/); - }); + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(/<trans-unit> misses the "id" attribute/); + }); - it('should throw on duplicate trans-unit id', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + it('should throw on duplicate trans-unit id', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="ng2.template"> <body> @@ -375,15 +379,15 @@ lignes`, </file> </xliff>`; - expect(() => { - loadAsMap(XLIFF); - }).toThrowError(/Duplicated translations for msg deadbeef/); - }); + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(/Duplicated translations for msg deadbeef/); }); + }); - describe('message errors', () => { - it('should throw on unknown message tags', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + describe('message errors', () => { + it('should throw on unknown message tags', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="ng2.template"> <body> @@ -395,13 +399,15 @@ lignes`, </file> </xliff>`; - expect(() => { loadAsMap(XLIFF); }) - .toThrowError( - new RegExp(escapeRegExp(`[ERROR ->]<b>msg should contain only ph tags</b>`))); - }); + expect(() => { + loadAsMap(XLIFF); + }) + .toThrowError( + new RegExp(escapeRegExp(`[ERROR ->]<b>msg should contain only ph tags</b>`))); + }); - it('should throw when a placeholder misses an id attribute', () => { - const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> + it('should throw when a placeholder misses an id attribute', () => { + const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="ng2.template"> <body> @@ -413,12 +419,11 @@ lignes`, </file> </xliff>`; - expect(() => { - loadAsMap(XLIFF); - }).toThrowError(new RegExp(escapeRegExp(`<x> misses the "id" attribute`))); - }); - + expect(() => { + loadAsMap(XLIFF); + }).toThrowError(new RegExp(escapeRegExp(`<x> misses the "id" attribute`))); }); }); }); +}); })(); diff --git a/packages/compiler/test/i18n/serializers/xmb_spec.ts b/packages/compiler/test/i18n/serializers/xmb_spec.ts index 68a8f47db1335..38605c01129e5 100644 --- a/packages/compiler/test/i18n/serializers/xmb_spec.ts +++ b/packages/compiler/test/i18n/serializers/xmb_spec.ts @@ -76,7 +76,7 @@ lines</msg> }); } -function toXmb(html: string, url: string, locale: string | null = null): string { +function toXmb(html: string, url: string, locale: string|null = null): string { const catalog = new MessageBundle(new HtmlParser, [], {}, locale); const serializer = new Xmb(); diff --git a/packages/compiler/test/i18n/serializers/xml_helper_spec.ts b/packages/compiler/test/i18n/serializers/xml_helper_spec.ts index d8e4016048f5a..6421c089d8082 100644 --- a/packages/compiler/test/i18n/serializers/xml_helper_spec.ts +++ b/packages/compiler/test/i18n/serializers/xml_helper_spec.ts @@ -15,11 +15,13 @@ import * as xml from '../../../src/i18n/serializers/xml_helper'; .toEqual('<?xml version="1.0" ?>'); }); - it('should serialize text node', - () => { expect(xml.serialize([new xml.Text('foo bar')])).toEqual('foo bar'); }); + it('should serialize text node', () => { + expect(xml.serialize([new xml.Text('foo bar')])).toEqual('foo bar'); + }); - it('should escape text nodes', - () => { expect(xml.serialize([new xml.Text('<>')])).toEqual('<>'); }); + it('should escape text nodes', () => { + expect(xml.serialize([new xml.Text('<>')])).toEqual('<>'); + }); it('should serialize xml nodes without children', () => { expect(xml.serialize([new xml.Tag('el', {foo: 'bar'}, [])])).toEqual('<el foo="bar"/>'); @@ -42,6 +44,5 @@ import * as xml from '../../../src/i18n/serializers/xml_helper'; expect(xml.serialize([new xml.Tag('el', {foo: '<">'}, [])])) .toEqual('<el foo="<">"/>'); }); - }); } \ No newline at end of file diff --git a/packages/compiler/test/i18n/serializers/xtb_spec.ts b/packages/compiler/test/i18n/serializers/xtb_spec.ts index d162fc9739b4f..247e4ef7e7f93 100644 --- a/packages/compiler/test/i18n/serializers/xtb_spec.ts +++ b/packages/compiler/test/i18n/serializers/xtb_spec.ts @@ -117,7 +117,7 @@ import {Xtb} from '../../../src/i18n/serializers/xtb'; </translationbundle>`; // Invalid messages should not cause the parser to throw - let i18nNodesByMsgId: {[id: string]: i18n.Node[]} = undefined !; + let i18nNodesByMsgId: {[id: string]: i18n.Node[]} = undefined!; expect(() => { i18nNodesByMsgId = serializer.load(XTB, 'url').i18nNodesByMsgId; }).not.toThrow(); @@ -144,7 +144,9 @@ import {Xtb} from '../../../src/i18n/serializers/xtb'; <translation></translation> </translationbundle>`; - expect(() => { loadAsMap(XTB); }).toThrowError(/<translation> misses the "id" attribute/); + expect(() => { + loadAsMap(XTB); + }).toThrowError(/<translation> misses the "id" attribute/); }); it('should throw when a placeholder has no name attribute', () => { @@ -152,7 +154,9 @@ import {Xtb} from '../../../src/i18n/serializers/xtb'; <translation id="1186013544048295927"><ph /></translation> </translationbundle>`; - expect(() => { loadAsMap(XTB); }).toThrowError(/<ph> misses the "name" attribute/); + expect(() => { + loadAsMap(XTB); + }).toThrowError(/<ph> misses the "name" attribute/); }); it('should throw on unknown xtb tags', () => { @@ -168,7 +172,9 @@ import {Xtb} from '../../../src/i18n/serializers/xtb'; <translation id="1186013544048295927"><b>msg should contain only ph tags</b></translation> </translationbundle>`; - expect(() => { loadAsMap(XTB); }) + expect(() => { + loadAsMap(XTB); + }) .toThrowError( new RegExp(escapeRegExp(`[ERROR ->]<b>msg should contain only ph tags</b>`))); }); @@ -184,9 +190,11 @@ import {Xtb} from '../../../src/i18n/serializers/xtb'; }).toThrowError(/Duplicated translations for msg 1186013544048295927/); }); - it('should throw when trying to save an xtb file', - () => { expect(() => { serializer.write([], null); }).toThrowError(/Unsupported/); }); - + it('should throw when trying to save an xtb file', () => { + expect(() => { + serializer.write([], null); + }).toThrowError(/Unsupported/); + }); }); }); } diff --git a/packages/compiler/test/i18n/translation_bundle_spec.ts b/packages/compiler/test/i18n/translation_bundle_spec.ts index d164d8874fffb..f46a2546ba184 100644 --- a/packages/compiler/test/i18n/translation_bundle_spec.ts +++ b/packages/compiler/test/i18n/translation_bundle_spec.ts @@ -25,14 +25,14 @@ import {_extractMessages} from './i18n_parser_spec'; const srcNode = new i18n.Text('src', span); it('should translate a plain text', () => { - const msgMap = {foo: [new i18n.Text('bar', null !)]}; + const msgMap = {foo: [new i18n.Text('bar', null!)]}; const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); expect(serializeNodes(tb.get(msg))).toEqual(['bar']); }); it('should translate html-like plain text', () => { - const msgMap = {foo: [new i18n.Text('<p>bar</p>', null !)]}; + const msgMap = {foo: [new i18n.Text('<p>bar</p>', null!)]}; const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); const nodes = tb.get(msg); @@ -45,8 +45,8 @@ import {_extractMessages} from './i18n_parser_spec'; it('should translate a message with placeholder', () => { const msgMap = { foo: [ - new i18n.Text('bar', null !), - new i18n.Placeholder('', 'ph1', null !), + new i18n.Text('bar', null!), + new i18n.Placeholder('', 'ph1', null!), ] }; const phMap = { @@ -60,12 +60,12 @@ import {_extractMessages} from './i18n_parser_spec'; it('should translate a message with placeholder referencing messages', () => { const msgMap = { foo: [ - new i18n.Text('--', null !), - new i18n.Placeholder('', 'ph1', null !), - new i18n.Text('++', null !), + new i18n.Text('--', null!), + new i18n.Placeholder('', 'ph1', null!), + new i18n.Text('++', null!), ], ref: [ - new i18n.Text('*refMsg*', null !), + new i18n.Text('*refMsg*', null!), ], }; const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); @@ -84,13 +84,13 @@ import {_extractMessages} from './i18n_parser_spec'; const digest = (_: any) => `no matching id`; // Empty message map -> use source messages in Ignore mode - let tb = new TranslationBundle({}, null, digest, null !, MissingTranslationStrategy.Ignore); + let tb = new TranslationBundle({}, null, digest, null!, MissingTranslationStrategy.Ignore); expect(serializeNodes(tb.get(messages[0])).join('')).toEqual(src); // Empty message map -> use source messages in Warning mode - tb = new TranslationBundle({}, null, digest, null !, MissingTranslationStrategy.Warning); + tb = new TranslationBundle({}, null, digest, null!, MissingTranslationStrategy.Warning); expect(serializeNodes(tb.get(messages[0])).join('')).toEqual(src); // Empty message map -> throw in Error mode - tb = new TranslationBundle({}, null, digest, null !, MissingTranslationStrategy.Error); + tb = new TranslationBundle({}, null, digest, null!, MissingTranslationStrategy.Error); expect(() => serializeNodes(tb.get(messages[0])).join('')).toThrow(); }); @@ -98,7 +98,7 @@ import {_extractMessages} from './i18n_parser_spec'; it('should report unknown placeholders', () => { const msgMap = { foo: [ - new i18n.Text('bar', null !), + new i18n.Text('bar', null!), new i18n.Placeholder('', 'ph1', span), ] }; @@ -109,7 +109,7 @@ import {_extractMessages} from './i18n_parser_spec'; it('should report missing translation', () => { const tb = - new TranslationBundle({}, null, (_) => 'foo', null !, MissingTranslationStrategy.Error); + new TranslationBundle({}, null, (_) => 'foo', null!, MissingTranslationStrategy.Error); const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); expect(() => tb.get(msg)).toThrowError(/Missing translation for message "foo"/); }); @@ -117,12 +117,14 @@ import {_extractMessages} from './i18n_parser_spec'; it('should report missing translation with MissingTranslationStrategy.Warning', () => { const log: string[] = []; const console = { - log: (msg: string) => { throw `unexpected`; }, + log: (msg: string) => { + throw `unexpected`; + }, warn: (msg: string) => log.push(msg), }; const tb = new TranslationBundle( - {}, 'en', (_) => 'foo', null !, MissingTranslationStrategy.Warning, console); + {}, 'en', (_) => 'foo', null!, MissingTranslationStrategy.Warning, console); const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); expect(() => tb.get(msg)).not.toThrowError(); @@ -131,8 +133,8 @@ import {_extractMessages} from './i18n_parser_spec'; }); it('should not report missing translation with MissingTranslationStrategy.Ignore', () => { - const tb = new TranslationBundle( - {}, null, (_) => 'foo', null !, MissingTranslationStrategy.Ignore); + const tb = + new TranslationBundle({}, null, (_) => 'foo', null!, MissingTranslationStrategy.Ignore); const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); expect(() => tb.get(msg)).not.toThrowError(); }); @@ -146,15 +148,15 @@ import {_extractMessages} from './i18n_parser_spec'; let count = 0; const digest = (_: any) => count++ ? 'ref' : 'foo'; const tb = - new TranslationBundle(msgMap, null, digest, null !, MissingTranslationStrategy.Error); + new TranslationBundle(msgMap, null, digest, null!, MissingTranslationStrategy.Error); expect(() => tb.get(msg)).toThrowError(/Missing translation for message "ref"/); }); it('should report invalid translated html', () => { const msgMap = { foo: [ - new i18n.Text('text', null !), - new i18n.Placeholder('', 'ph1', null !), + new i18n.Text('text', null!), + new i18n.Placeholder('', 'ph1', null!), ] }; const phMap = { diff --git a/packages/compiler/test/integration_spec.ts b/packages/compiler/test/integration_spec.ts index da7f870600dfb..e43675a57fc5f 100644 --- a/packages/compiler/test/integration_spec.ts +++ b/packages/compiler/test/integration_spec.ts @@ -7,7 +7,7 @@ */ import {Component, Directive, Input} from '@angular/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -21,7 +21,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; @Directive({selector: '[dot.name]'}) class MyDir { // TODO(issue/24571): remove '!'. - @Input('dot.name') value !: string; + @Input('dot.name') value!: string; } TestBed.configureTestingModule({ diff --git a/packages/compiler/test/metadata_resolver_spec.ts b/packages/compiler/test/metadata_resolver_spec.ts index 406b8755b0339..2f853c5bc4238 100644 --- a/packages/compiler/test/metadata_resolver_spec.ts +++ b/packages/compiler/test/metadata_resolver_spec.ts @@ -8,7 +8,7 @@ import {LIFECYCLE_HOOKS_VALUES, LifecycleHooks} from '@angular/compiler/src/lifecycle_reflector'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, Directive, DoCheck, Injectable, NgModule, OnChanges, OnDestroy, OnInit, Pipe, SimpleChanges, ViewEncapsulation, ɵstringify as stringify} from '@angular/core'; -import {TestBed, async, inject} from '@angular/core/testing'; +import {async, inject, TestBed} from '@angular/core/testing'; import {CompileDiDependencyMetadata, identifierName} from '../src/compile_metadata'; import {CompileMetadataResolver} from '../src/metadata_resolver'; @@ -20,7 +20,9 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; { describe('CompileMetadataResolver', () => { - beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); + beforeEach(() => { + TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); + }); it('should throw on the getDirectiveMetadata/getPipeMetadata methods if the module has not been loaded yet', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { @@ -70,8 +72,8 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; } expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) - .toThrowError( - `Can't compile synchronously as ${stringify(ComponentWithExternalResources)} is still being loaded!`); + .toThrowError(`Can't compile synchronously as ${ + stringify(ComponentWithExternalResources)} is still being loaded!`); })); it('should read external metadata when sync=false', @@ -106,7 +108,7 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => { const value: string = - resolver.getDirectiveMetadata(ComponentWithoutModuleId).template !.templateUrl !; + resolver.getDirectiveMetadata(ComponentWithoutModuleId).template !.templateUrl!; const expectedEndValue = './someUrl'; expect(value.endsWith(expectedEndValue)).toBe(true); }); @@ -217,7 +219,7 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it('should throw with descriptive error message when null is passed to declarations', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @NgModule({declarations: [null !]}) + @NgModule({declarations: [null!]}) class ModuleWithNullDeclared { } expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullDeclared, true)) @@ -227,7 +229,7 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it('should throw with descriptive error message when null is passed to imports', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @NgModule({imports: [null !]}) + @NgModule({imports: [null!]}) class ModuleWithNullImported { } expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullImported, true)) @@ -248,7 +250,7 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it('should throw with descriptive error message when encounter invalid provider', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @NgModule({providers: [{provide: SimpleService, useClass: undefined !}]}) + @NgModule({providers: [{provide: SimpleService, useClass: undefined!}]}) class SomeModule { } @@ -258,7 +260,7 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it('should throw with descriptive error message when provider is undefined', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @NgModule({providers: [undefined !]}) + @NgModule({providers: [undefined!]}) class SomeModule { } @@ -290,10 +292,10 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it('should throw with descriptive error message when null or undefined is passed to module bootstrap', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @NgModule({bootstrap: [null !]}) + @NgModule({bootstrap: [null!]}) class ModuleWithNullBootstrap { } - @NgModule({bootstrap: [undefined !]}) + @NgModule({bootstrap: [undefined!]}) class ModuleWithUndefinedBootstrap { } @@ -347,7 +349,6 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it(`should throw an error when a Pipe is added to module's bootstrap list`, inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @Pipe({name: 'pipe'}) class MyPipe { } @@ -362,7 +363,6 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it(`should throw an error when a Service is added to module's bootstrap list`, inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @NgModule({declarations: [], bootstrap: [SimpleService]}) class ModuleWithServiceInBootstrap { } @@ -373,7 +373,6 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it('should generate an error when a dependency could not be resolved', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - // Override the errorCollector so that error gets collected instead of // being thrown. (resolver as any)._errorCollector = (error: Error, type?: any) => { @@ -390,12 +389,11 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; class AppModule { } - const moduleMetadata = resolver.getNgModuleMetadata(AppModule) !; + const moduleMetadata = resolver.getNgModuleMetadata(AppModule)!; expect(moduleMetadata).toBeTruthy(); expect(moduleMetadata.declaredDirectives.length).toBe(1); const directive = moduleMetadata.declaredDirectives[0]; - const directiveMetadata = - resolver.getNonNormalizedDirectiveMetadata(directive.reference) !; + const directiveMetadata = resolver.getNonNormalizedDirectiveMetadata(directive.reference)!; expect(directiveMetadata).toBeTruthy(); const {metadata} = directiveMetadata; const diDeps: CompileDiDependencyMetadata[] = metadata.type.diDeps; @@ -405,7 +403,6 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it(`should throw an error when a Directive is added to module's bootstrap list`, inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @Directive({selector: 'directive'}) class MyDirective { } @@ -420,7 +417,6 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it(`should not throw an error when a Component is added to module's bootstrap list`, inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @Component({template: ''}) class MyComp { } @@ -439,7 +435,9 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; class InvalidModule { } - expect(() => { resolver.getNgModuleMetadata(InvalidModule); }) + expect(() => { + resolver.getNgModuleMetadata(InvalidModule); + }) .toThrowError( `Unexpected value '[object Object]' imported by the module 'InvalidModule'. Please add a @NgModule annotation.`); })); @@ -447,7 +445,6 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; it('should dedupe declarations in NgModule', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { - @Component({template: ''}) class MyComp { } @@ -456,7 +453,7 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; class MyModule { } - const modMeta = resolver.getNgModuleMetadata(MyModule) !; + const modMeta = resolver.getNgModuleMetadata(MyModule)!; expect(modMeta.declaredDirectives.length).toBe(1); expect(modMeta.declaredDirectives[0].reference).toBe(MyComp); })); @@ -495,9 +492,9 @@ class ComponentWithExternalResources { styles: ['someStyle'], interpolation: ['{{', '}}'] }) -class ComponentWithEverythingInline implements OnChanges, - OnInit, DoCheck, OnDestroy, AfterContentInit, AfterContentChecked, AfterViewInit, - AfterViewChecked { +class ComponentWithEverythingInline implements OnChanges, OnInit, DoCheck, OnDestroy, + AfterContentInit, AfterContentChecked, AfterViewInit, + AfterViewChecked { ngOnChanges(changes: SimpleChanges): void {} ngOnInit(): void {} ngDoCheck(): void {} @@ -526,12 +523,12 @@ class MyBrokenComp2 { class SimpleService { } -@Component({selector: 'my-broken-comp', template: '', providers: [SimpleService, null !, [null]]}) +@Component({selector: 'my-broken-comp', template: '', providers: [SimpleService, null!, [null]]}) class MyBrokenComp3 { } @Component( - {selector: 'my-broken-comp', template: '', viewProviders: [null !, SimpleService, [null]]}) + {selector: 'my-broken-comp', template: '', viewProviders: [null!, SimpleService, [null]]}) class MyBrokenComp4 { } diff --git a/packages/compiler/test/ml_parser/ast_serializer_spec.ts b/packages/compiler/test/ml_parser/ast_serializer_spec.ts index 055b0a7a1cb04..e1545821d176b 100644 --- a/packages/compiler/test/ml_parser/ast_serializer_spec.ts +++ b/packages/compiler/test/ml_parser/ast_serializer_spec.ts @@ -13,7 +13,9 @@ import {serializeNodes} from './util/util'; describe('Node serializer', () => { let parser: HtmlParser; - beforeEach(() => { parser = new HtmlParser(); }); + beforeEach(() => { + parser = new HtmlParser(); + }); it('should support element', () => { const html = '<p></p>'; diff --git a/packages/compiler/test/ml_parser/ast_spec_utils.ts b/packages/compiler/test/ml_parser/ast_spec_utils.ts index 4432c2eb98ecf..268fe947007d1 100644 --- a/packages/compiler/test/ml_parser/ast_spec_utils.ts +++ b/packages/compiler/test/ml_parser/ast_spec_utils.ts @@ -78,7 +78,7 @@ class _Humanizer implements html.Visitor { private _appendContext(ast: html.Node, input: any[]): any[] { if (!this.includeSourceSpan) return input; - input.push(ast.sourceSpan !.toString()); + input.push(ast.sourceSpan!.toString()); return input; } } diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index 3cc1b893532cd..a488d827ac06e 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -17,7 +17,9 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe describe('HtmlParser', () => { let parser: HtmlParser; - beforeEach(() => { parser = new HtmlParser(); }); + beforeEach(() => { + parser = new HtmlParser(); + }); describe('parse', () => { describe('text nodes', () => { @@ -77,11 +79,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe // <meta> - it can be present in head only // <command> - obsolete // <keygen> - obsolete - ['<map><area></map>', '<div><br></div>', '<colgroup><col></colgroup>', - '<div><embed></div>', '<div><hr></div>', '<div><img></div>', '<div><input></div>', - '<object><param>/<object>', '<audio><source></audio>', '<audio><track></audio>', + ['<map><area></map>', + '<div><br></div>', + '<colgroup><col></colgroup>', + '<div><embed></div>', + '<div><hr></div>', + '<div><img></div>', + '<div><input></div>', + '<object><param>/<object>', + '<audio><source></audio>', + '<audio><track></audio>', '<p><wbr></p>', - ].forEach((html) => { expect(parser.parse(html, 'TestComp').errors).toEqual([]); }); + ].forEach((html) => { + expect(parser.parse(html, 'TestComp').errors).toEqual([]); + }); }); it('should close void elements on text nodes', () => { @@ -189,7 +200,6 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe [html.Text, '\n', 1], ]); }); - }); describe('attributes', () => { @@ -263,8 +273,9 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe [html.Text, ' messages', 0], ]); - expect(humanizeDom(new ParseTreeResult(cases[1].expression, [ - ]))).toEqual([[html.Text, 'One {{message}}', 0]]); + expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ + [html.Text, 'One {{message}}', 0] + ]); }); it('should parse out expansion forms', () => { @@ -313,6 +324,26 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe expect(p.errors.length).toEqual(0); }); + it(`should support ICU expressions with cases that contain any character except '}'`, + () => { + const p = parser.parse( + `{a, select, b {foo} % bar {% bar}}`, 'TestComp', {tokenizeExpansionForms: true}); + expect(p.errors.length).toEqual(0); + }); + + it('should error when expansion case is not properly closed', () => { + const p = parser.parse( + `{a, select, b {foo} % { bar {% bar}}`, 'TestComp', {tokenizeExpansionForms: true}); + expect(humanizeErrors(p.errors)).toEqual([ + [ + 6, + 'Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ \'{\' }}") to escape it.)', + '0:36' + ], + [null, 'Invalid ICU message. Missing \'}\'.', '0:22'] + ]); + }); + it('should error when expansion case is not closed', () => { const p = parser.parse( `{messages.length, plural, =0 {one`, 'TestComp', {tokenizeExpansionForms: true}); @@ -347,11 +378,11 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe it('should set the start and end source spans', () => { const node = <html.Element>parser.parse('<div>a</div>', 'TestComp').rootNodes[0]; - expect(node.startSourceSpan !.start.offset).toEqual(0); - expect(node.startSourceSpan !.end.offset).toEqual(5); + expect(node.startSourceSpan!.start.offset).toEqual(0); + expect(node.startSourceSpan!.end.offset).toEqual(5); - expect(node.endSourceSpan !.start.offset).toEqual(6); - expect(node.endSourceSpan !.end.offset).toEqual(12); + expect(node.endSourceSpan!.start.offset).toEqual(6); + expect(node.endSourceSpan!.end.offset).toEqual(12); }); it('should support expansion form', () => { @@ -373,15 +404,15 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe it('should report a value span for an attribute with a value', () => { const ast = parser.parse('<div bar="12"></div>', 'TestComp'); const attr = (ast.rootNodes[0] as html.Element).attrs[0]; - expect(attr.valueSpan !.start.offset).toEqual(10); - expect(attr.valueSpan !.end.offset).toEqual(12); + expect(attr.valueSpan!.start.offset).toEqual(10); + expect(attr.valueSpan!.end.offset).toEqual(12); }); it('should report a value span for an unquoted attribute value', () => { const ast = parser.parse('<div bar=12></div>', 'TestComp'); const attr = (ast.rootNodes[0] as html.Element).attrs[0]; - expect(attr.valueSpan !.start.offset).toEqual(9); - expect(attr.valueSpan !.end.offset).toEqual(11); + expect(attr.valueSpan!.start.offset).toEqual(9); + expect(attr.valueSpan!.end.offset).toEqual(11); }); }); @@ -406,7 +437,9 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe parser.parse('<div id="foo"><span id="bar">a</span><span>b</span></div>', 'TestComp'); const accumulator: html.Node[] = []; const visitor = new class { - visit(node: html.Node, context: any) { accumulator.push(node); } + visit(node: html.Node, context: any) { + accumulator.push(node); + } visitElement(element: html.Element, context: any): any { html.visitAll(this, element.attrs); html.visitAll(this, element.children); @@ -429,13 +462,21 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe it('should skip typed visit if visit() returns a truthy value', () => { const visitor = new class { - visit(node: html.Node, context: any) { return true; } - visitElement(element: html.Element, context: any): any { throw Error('Unexpected'); } + visit(node: html.Node, context: any) { + return true; + } + visitElement(element: html.Element, context: any): any { + throw Error('Unexpected'); + } visitAttribute(attribute: html.Attribute, context: any): any { throw Error('Unexpected'); } - visitText(text: html.Text, context: any): any { throw Error('Unexpected'); } - visitComment(comment: html.Comment, context: any): any { throw Error('Unexpected'); } + visitText(text: html.Text, context: any): any { + throw Error('Unexpected'); + } + visitComment(comment: html.Comment, context: any): any { + throw Error('Unexpected'); + } visitExpansion(expansion: html.Expansion, context: any): any { throw Error('Unexpected'); } diff --git a/packages/compiler/test/ml_parser/html_whitespaces_spec.ts b/packages/compiler/test/ml_parser/html_whitespaces_spec.ts index 3b5c1ddc4eac4..4c22b47801b4c 100644 --- a/packages/compiler/test/ml_parser/html_whitespaces_spec.ts +++ b/packages/compiler/test/ml_parser/html_whitespaces_spec.ts @@ -15,7 +15,6 @@ import {humanizeDom} from './ast_spec_utils'; { describe('removeWhitespaces', () => { - function parseAndRemoveWS(template: string, options?: TokenizeOptions): any[] { return humanizeDom(removeWhitespaces(new HtmlParser().parse(template, 'TestComp', options))); } diff --git a/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts b/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts index 0682030852902..0aab1414d8872 100644 --- a/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts +++ b/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts @@ -8,7 +8,7 @@ import * as html from '../../src/ml_parser/ast'; import {HtmlParser} from '../../src/ml_parser/html_parser'; -import {ExpansionResult, expandNodes} from '../../src/ml_parser/icu_ast_expander'; +import {expandNodes, ExpansionResult} from '../../src/ml_parser/icu_ast_expander'; import {ParseError} from '../../src/parse_util'; import {humanizeNodes} from './ast_spec_utils'; @@ -56,28 +56,28 @@ import {humanizeNodes} from './ast_spec_utils'; const nodes = expand(`{messages.length, plural,=0 {<b>bold</b>}}`).nodes; const container: html.Element = <html.Element>nodes[0]; - expect(container.sourceSpan !.start.col).toEqual(0); - expect(container.sourceSpan !.end.col).toEqual(42); - expect(container.startSourceSpan !.start.col).toEqual(0); - expect(container.startSourceSpan !.end.col).toEqual(42); - expect(container.endSourceSpan !.start.col).toEqual(0); - expect(container.endSourceSpan !.end.col).toEqual(42); + expect(container.sourceSpan!.start.col).toEqual(0); + expect(container.sourceSpan!.end.col).toEqual(42); + expect(container.startSourceSpan!.start.col).toEqual(0); + expect(container.startSourceSpan!.end.col).toEqual(42); + expect(container.endSourceSpan!.start.col).toEqual(0); + expect(container.endSourceSpan!.end.col).toEqual(42); const switchExp = container.attrs[0]; expect(switchExp.sourceSpan.start.col).toEqual(1); expect(switchExp.sourceSpan.end.col).toEqual(16); const template: html.Element = <html.Element>container.children[0]; - expect(template.sourceSpan !.start.col).toEqual(25); - expect(template.sourceSpan !.end.col).toEqual(41); + expect(template.sourceSpan!.start.col).toEqual(25); + expect(template.sourceSpan!.end.col).toEqual(41); const switchCheck = template.attrs[0]; expect(switchCheck.sourceSpan.start.col).toEqual(25); expect(switchCheck.sourceSpan.end.col).toEqual(28); const b: html.Element = <html.Element>template.children[0]; - expect(b.sourceSpan !.start.col).toEqual(29); - expect(b.endSourceSpan !.end.col).toEqual(40); + expect(b.sourceSpan!.start.col).toEqual(29); + expect(b.endSourceSpan!.end.col).toEqual(40); }); it('should handle other special forms', () => { diff --git a/packages/compiler/test/ml_parser/lexer_spec.ts b/packages/compiler/test/ml_parser/lexer_spec.ts index b89734e4f8d57..8874ad83d445b 100644 --- a/packages/compiler/test/ml_parser/lexer_spec.ts +++ b/packages/compiler/test/ml_parser/lexer_spec.ts @@ -232,7 +232,6 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u [lex.TokenType.EOF, ''], ]); }); - }); describe('attributes', () => { @@ -400,7 +399,6 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u [lex.TokenType.ATTR_VALUE, 'Unexpected character "EOF"', '0:8'], ]); }); - }); describe('closing tags', () => { @@ -729,7 +727,6 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u [lex.TokenType.EOF, ''], ]); }); - }); describe('expansion forms', () => { @@ -898,7 +895,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u const file = new ParseSourceFile(src, 'file://'); const location = new ParseLocation(file, 12, 123, 456); const span = new ParseSourceSpan(location, location); - const error = new lex.TokenError('**ERROR**', null !, span); + const error = new lex.TokenError('**ERROR**', null!, span); expect(error.toString()) .toEqual(`**ERROR** ("\n222\n333\n[ERROR ->]E\n444\n555\n"): file://@123:456`); }); diff --git a/packages/compiler/test/ml_parser/util/util.ts b/packages/compiler/test/ml_parser/util/util.ts index b61939b501390..d640b6448dca8 100644 --- a/packages/compiler/test/ml_parser/util/util.ts +++ b/packages/compiler/test/ml_parser/util/util.ts @@ -15,16 +15,21 @@ class _SerializerVisitor implements html.Visitor { return `<${element.name}${this._visitAll(element.attrs, ' ')}/>`; } - return `<${element.name}${this._visitAll(element.attrs, ' ')}>${this._visitAll(element.children)}</${element.name}>`; + return `<${element.name}${this._visitAll(element.attrs, ' ')}>${ + this._visitAll(element.children)}</${element.name}>`; } visitAttribute(attribute: html.Attribute, context: any): any { return `${attribute.name}="${attribute.value}"`; } - visitText(text: html.Text, context: any): any { return text.value; } + visitText(text: html.Text, context: any): any { + return text.value; + } - visitComment(comment: html.Comment, context: any): any { return `<!--${comment.value}-->`; } + visitComment(comment: html.Comment, context: any): any { + return `<!--${comment.value}-->`; + } visitExpansion(expansion: html.Expansion, context: any): any { return `{${expansion.switchValue}, ${expansion.type},${this._visitAll(expansion.cases)}}`; diff --git a/packages/compiler/test/ng_module_resolver_spec.ts b/packages/compiler/test/ng_module_resolver_spec.ts index d9b019c520bed..787f5ca93ee25 100644 --- a/packages/compiler/test/ng_module_resolver_spec.ts +++ b/packages/compiler/test/ng_module_resolver_spec.ts @@ -33,7 +33,9 @@ class SimpleClass {} describe('NgModuleResolver', () => { let resolver: NgModuleResolver; - beforeEach(() => { resolver = new NgModuleResolver(new JitReflector()); }); + beforeEach(() => { + resolver = new NgModuleResolver(new JitReflector()); + }); it('should read out the metadata from the class', () => { const moduleMetadata = resolver.resolve(SomeModule); @@ -66,6 +68,5 @@ class SimpleClass {} expect(resolver.resolve(ChildWithDecorator)).toEqual(new NgModule({id: 'c'})); }); - }); } diff --git a/packages/compiler/test/output/abstract_emitter_node_only_spec.ts b/packages/compiler/test/output/abstract_emitter_node_only_spec.ts index d70410d491b22..a95031527d328 100644 --- a/packages/compiler/test/output/abstract_emitter_node_only_spec.ts +++ b/packages/compiler/test/output/abstract_emitter_node_only_spec.ts @@ -18,14 +18,16 @@ import {extractSourceMap, originalPositionFor} from '@angular/compiler/testing/s const fileB = new ParseSourceFile('b0b1b2b3b4b5b6b7b8b9', 'b.js'); let ctx: EmitterVisitorContext; - beforeEach(() => { ctx = EmitterVisitorContext.createRoot(); }); + beforeEach(() => { + ctx = EmitterVisitorContext.createRoot(); + }); it('should add source files to the source map', () => { ctx.print(createSourceSpan(fileA, 0), 'o0'); ctx.print(createSourceSpan(fileA, 1), 'o1'); ctx.print(createSourceSpan(fileB, 0), 'o2'); ctx.print(createSourceSpan(fileB, 1), 'o3'); - const sm = ctx.toSourceMapGenerator('o.ts').toJSON() !; + const sm = ctx.toSourceMapGenerator('o.ts').toJSON()!; expect(sm.sources).toEqual([fileA.url, fileB.url]); expect(sm.sourcesContent).toEqual([fileA.content, fileB.content]); }); @@ -43,7 +45,7 @@ import {extractSourceMap, originalPositionFor} from '@angular/compiler/testing/s it('should be able to shift the content', () => { ctx.print(createSourceSpan(fileA, 0), 'fileA-0'); - const sm = ctx.toSourceMapGenerator('o.ts', 10).toJSON() !; + const sm = ctx.toSourceMapGenerator('o.ts', 10).toJSON()!; expect(originalPositionFor(sm, {line: 11, column: 0})).toEqual({ line: 1, column: 0, @@ -111,9 +113,9 @@ import {extractSourceMap, originalPositionFor} from '@angular/compiler/testing/s // All lines / columns indexes are 0-based // Note: source-map line indexes are 1-based, column 0-based function expectMap( - ctx: EmitterVisitorContext, genLine: number, genCol: number, source: string | null = null, - srcLine: number | null = null, srcCol: number | null = null) { - const sm = ctx.toSourceMapGenerator('o.ts').toJSON() !; + ctx: EmitterVisitorContext, genLine: number, genCol: number, source: string|null = null, + srcLine: number|null = null, srcCol: number|null = null) { + const sm = ctx.toSourceMapGenerator('o.ts').toJSON()!; const genPosition = {line: genLine + 1, column: genCol}; const origPosition = originalPositionFor(sm, genPosition); // TODO: Review use of `any` here (#19904) @@ -124,7 +126,7 @@ function expectMap( // returns the number of segments per line function nbSegmentsPerLine(ctx: EmitterVisitorContext) { - const sm = ctx.toSourceMapGenerator('o.ts').toJSON() !; + const sm = ctx.toSourceMapGenerator('o.ts').toJSON()!; const lines = sm.mappings.split(';'); return lines.map(l => { const m = l.match(/,/g); diff --git a/packages/compiler/test/output/abstract_emitter_spec.ts b/packages/compiler/test/output/abstract_emitter_spec.ts index d28b439ff1b66..6f777c9b6b774 100644 --- a/packages/compiler/test/output/abstract_emitter_spec.ts +++ b/packages/compiler/test/output/abstract_emitter_spec.ts @@ -11,24 +11,34 @@ import {escapeIdentifier} from '@angular/compiler/src/output/abstract_emitter'; { describe('AbstractEmitter', () => { describe('escapeIdentifier', () => { - it('should escape single quotes', - () => { expect(escapeIdentifier(`'`, false)).toEqual(`'\\''`); }); + it('should escape single quotes', () => { + expect(escapeIdentifier(`'`, false)).toEqual(`'\\''`); + }); - it('should escape backslash', - () => { expect(escapeIdentifier('\\', false)).toEqual(`'\\\\'`); }); + it('should escape backslash', () => { + expect(escapeIdentifier('\\', false)).toEqual(`'\\\\'`); + }); - it('should escape newlines', - () => { expect(escapeIdentifier('\n', false)).toEqual(`'\\n'`); }); + it('should escape newlines', () => { + expect(escapeIdentifier('\n', false)).toEqual(`'\\n'`); + }); - it('should escape carriage returns', - () => { expect(escapeIdentifier('\r', false)).toEqual(`'\\r'`); }); + it('should escape carriage returns', () => { + expect(escapeIdentifier('\r', false)).toEqual(`'\\r'`); + }); - it('should escape $', () => { expect(escapeIdentifier('$', true)).toEqual(`'\\$'`); }); - it('should not escape $', () => { expect(escapeIdentifier('$', false)).toEqual(`'$'`); }); - it('should add quotes for non-identifiers', - () => { expect(escapeIdentifier('==', false, false)).toEqual(`'=='`); }); - it('does not escape class (but it probably should)', - () => { expect(escapeIdentifier('class', false, false)).toEqual('class'); }); + it('should escape $', () => { + expect(escapeIdentifier('$', true)).toEqual(`'\\$'`); + }); + it('should not escape $', () => { + expect(escapeIdentifier('$', false)).toEqual(`'$'`); + }); + it('should add quotes for non-identifiers', () => { + expect(escapeIdentifier('==', false, false)).toEqual(`'=='`); + }); + it('does not escape class (but it probably should)', () => { + expect(escapeIdentifier('class', false, false)).toEqual('class'); + }); }); }); } diff --git a/packages/compiler/test/output/js_emitter_node_only_spec.ts b/packages/compiler/test/output/js_emitter_node_only_spec.ts index 1ec2bb4e3ce40..c2b5aac10a60e 100644 --- a/packages/compiler/test/output/js_emitter_node_only_spec.ts +++ b/packages/compiler/test/output/js_emitter_node_only_spec.ts @@ -22,12 +22,14 @@ const someGenFilePath = 'somePackage/someGenFile'; let emitter: JavaScriptEmitter; let someVar: o.ReadVarExpr; - beforeEach(() => { emitter = new JavaScriptEmitter(); }); + beforeEach(() => { + emitter = new JavaScriptEmitter(); + }); - function emitSourceMap(stmt: o.Statement | o.Statement[], preamble?: string): SourceMap { + function emitSourceMap(stmt: o.Statement|o.Statement[], preamble?: string): SourceMap { const stmts = Array.isArray(stmt) ? stmt : [stmt]; const source = emitter.emitStatements(someGenFilePath, stmts, preamble); - return extractSourceMap(source) !; + return extractSourceMap(source)!; } describe('source maps', () => { diff --git a/packages/compiler/test/output/js_emitter_spec.ts b/packages/compiler/test/output/js_emitter_spec.ts index a60c15c706806..a2f7d11999fe0 100644 --- a/packages/compiler/test/output/js_emitter_spec.ts +++ b/packages/compiler/test/output/js_emitter_spec.ts @@ -171,8 +171,9 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some }); it('should support function statements', () => { - expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [ - ]))).toEqual(['function someFn() {', '}'].join('\n')); + expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], []))).toEqual([ + 'function someFn() {', '}' + ].join('\n')); expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [], null, [o.StmtModifier.Exported]))) .toEqual([ 'function someFn() {', '}', @@ -181,8 +182,9 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [ new o.ReturnStatement(o.literal(1)) ]))).toEqual(['function someFn() {', ' return 1;', '}'].join('\n')); - expect(emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1')], [ - ]))).toEqual(['function someFn(param1) {', '}'].join('\n')); + expect(emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1')], []))).toEqual([ + 'function someFn(param1) {', '}' + ].join('\n')); }); it('should support comments', () => { @@ -219,25 +221,29 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some ].join('\n')); }); - it('should support support throwing', - () => { expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); }); + it('should support support throwing', () => { + expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); + }); describe('classes', () => { let callSomeMethod: o.Statement; - beforeEach(() => { callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); }); + beforeEach(() => { + callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); + }); it('should support declaring classes', () => { - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ - ]))).toEqual(['function SomeClass() {', '}'].join('\n')); + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, []))).toEqual([ + 'function SomeClass() {', '}' + ].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], null !, [], [o.StmtModifier.Exported]))) + 'SomeClass', null!, [], [], null!, [], [o.StmtModifier.Exported]))) .toEqual([ 'function SomeClass() {', '}', `Object.defineProperty(exports, 'SomeClass', { get: function() { return SomeClass; }});` ].join('\n')); - expect(emitStmt( - new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null !, []))) + expect( + emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null!, []))) .toEqual([ 'function SomeClass() {', '}', 'SomeClass.prototype = Object.create(SomeSuperClass.prototype);' @@ -247,23 +253,22 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some it('should support declaring constructors', () => { const superCall = o.SUPER_EXPR.callFn([o.variable('someParam')]).toStmt(); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], new o.ClassMethod(null !, [], []), []))) + 'SomeClass', null!, [], [], new o.ClassMethod(null!, [], []), []))) .toEqual(['function SomeClass() {', '}'].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], - new o.ClassMethod(null !, [new o.FnParam('someParam')], []), []))) + 'SomeClass', null!, [], [], + new o.ClassMethod(null!, [new o.FnParam('someParam')], []), []))) .toEqual(['function SomeClass(someParam) {', '}'].join('\n')); expect(emitStmt(new o.ClassStmt( 'SomeClass', o.variable('SomeSuperClass'), [], [], - new o.ClassMethod(null !, [], [superCall]), []))) + new o.ClassMethod(null!, [], [superCall]), []))) .toEqual([ 'function SomeClass() {', ' var self = this;', ' SomeSuperClass.call(this, someParam);', '}', 'SomeClass.prototype = Object.create(SomeSuperClass.prototype);' ].join('\n')); - expect( - emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], new o.ClassMethod(null !, [], [callSomeMethod]), []))) + expect(emitStmt(new o.ClassStmt( + 'SomeClass', null!, [], [], new o.ClassMethod(null!, [], [callSomeMethod]), []))) .toEqual([ 'function SomeClass() {', ' var self = this;', ' self.someMethod();', '}' ].join('\n')); @@ -271,14 +276,14 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some it('should support declaring getters', () => { expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [new o.ClassGetter('someGetter', [])], null !, []))) + 'SomeClass', null!, [], [new o.ClassGetter('someGetter', [])], null!, []))) .toEqual([ 'function SomeClass() {', '}', `Object.defineProperty(SomeClass.prototype, 'someGetter', { get: function() {`, `}});` ].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [new o.ClassGetter('someGetter', [callSomeMethod])], - null !, []))) + 'SomeClass', null!, [], [new o.ClassGetter('someGetter', [callSomeMethod])], + null!, []))) .toEqual([ 'function SomeClass() {', '}', `Object.defineProperty(SomeClass.prototype, 'someGetter', { get: function() {`, @@ -288,19 +293,19 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some it('should support methods', () => { expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], null !, [new o.ClassMethod('someMethod', [], [])]))) + 'SomeClass', null!, [], [], null!, [new o.ClassMethod('someMethod', [], [])]))) .toEqual([ 'function SomeClass() {', '}', 'SomeClass.prototype.someMethod = function() {', '};' ].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], null !, + 'SomeClass', null!, [], [], null!, [new o.ClassMethod('someMethod', [new o.FnParam('someParam')], [])]))) .toEqual([ 'function SomeClass() {', '}', 'SomeClass.prototype.someMethod = function(someParam) {', '};' ].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], null !, + 'SomeClass', null!, [], [], null!, [new o.ClassMethod('someMethod', [], [callSomeMethod])]))) .toEqual([ 'function SomeClass() {', '}', 'SomeClass.prototype.someMethod = function() {', diff --git a/packages/compiler/test/output/output_ast_spec.ts b/packages/compiler/test/output/output_ast_spec.ts index 2af2adaa99e03..41c7fbaca158f 100644 --- a/packages/compiler/test/output/output_ast_spec.ts +++ b/packages/compiler/test/output/output_ast_spec.ts @@ -15,8 +15,8 @@ import * as o from '../../src/output/output_ast'; const ref1 = new o.ExternalReference('aModule', 'name1'); const ref2 = new o.ExternalReference('aModule', 'name2'); const stmt = - o.variable('test').set(o.NULL_EXPR).toDeclStmt(o.importType(ref1, [o.importType( - ref2) !])); + o.variable('test').set(o.NULL_EXPR).toDeclStmt(o.importType(ref1, [o.importType(ref2)! + ])); expect(o.collectExternalReferences([stmt])).toEqual([ref1, ref2]); }); diff --git a/packages/compiler/test/output/output_jit_spec.ts b/packages/compiler/test/output/output_jit_spec.ts index eadc9292c51fa..d978ecda2220e 100644 --- a/packages/compiler/test/output/output_jit_spec.ts +++ b/packages/compiler/test/output/output_jit_spec.ts @@ -57,7 +57,7 @@ const anotherModuleUrl = 'somePackage/someOtherPath'; o.literal('use strict').toStmt(), ], ctx); - const matches = ctx.toSource().match(/'use strict';/g) !; + const matches = ctx.toSource().match(/'use strict';/g)!; expect(matches.length).toBe(1); }); }); diff --git a/packages/compiler/test/output/source_map_spec.ts b/packages/compiler/test/output/source_map_spec.ts index ea76893e115b7..2d80d01e09e2c 100644 --- a/packages/compiler/test/output/source_map_spec.ts +++ b/packages/compiler/test/output/source_map_spec.ts @@ -52,7 +52,7 @@ import {SourceMapGenerator, toBase64String} from '@angular/compiler/src/output/s .addMapping(3, 'a.js', 5, 2); // Generated with https://sokra.github.io/source-map-visualization using a TS source map - expect(map.toJSON() !.mappings) + expect(map.toJSON()!.mappings) .toEqual( 'AAAA,IAAM,CAAC,GAAe,CAAC,CAAC;AACxB,IAAM,CAAC,GAAG,CAAC,CAAC;AAEZ,EAAE,CAAC,OAAO,CAAC,UAAA,CAAC;IACR,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC,CAAC,CAAA'); }); @@ -64,7 +64,7 @@ import {SourceMapGenerator, toBase64String} from '@angular/compiler/src/output/s .addSource('url.ts', null) .addLine() .addMapping(0, 'inline.ts', 0, 0) - .toJSON() !; + .toJSON()!; expect(map.file).toEqual('out.js'); expect(map.sources).toEqual(['inline.ts', 'url.ts']); @@ -81,7 +81,11 @@ import {SourceMapGenerator, toBase64String} from '@angular/compiler/src/output/s describe('encodeB64String', () => { it('should return the b64 encoded value', () => { - [['', ''], ['a', 'YQ=='], ['Foo', 'Rm9v'], ['Foo1', 'Rm9vMQ=='], ['Foo12', 'Rm9vMTI='], + [['', ''], + ['a', 'YQ=='], + ['Foo', 'Rm9v'], + ['Foo1', 'Rm9vMQ=='], + ['Foo12', 'Rm9vMTI='], ['Foo123', 'Rm9vMTIz'], ].forEach(([src, b64]) => expect(toBase64String(src)).toEqual(b64)); }); @@ -113,7 +117,7 @@ import {SourceMapGenerator, toBase64String} from '@angular/compiler/src/output/s it('should throw when adding segments without column', () => { expect(() => { - new SourceMapGenerator('out.js').addSource('in.js').addLine().addMapping(null !); + new SourceMapGenerator('out.js').addSource('in.js').addLine().addMapping(null!); }).toThrowError('The column in the generated code must be provided'); }); diff --git a/packages/compiler/test/output/ts_emitter_node_only_spec.ts b/packages/compiler/test/output/ts_emitter_node_only_spec.ts index b1ce420096ead..0e6c75e457349 100644 --- a/packages/compiler/test/output/ts_emitter_node_only_spec.ts +++ b/packages/compiler/test/output/ts_emitter_node_only_spec.ts @@ -31,10 +31,10 @@ const someGenFilePath = 'somePackage/someGenFile'; someVar = o.variable('someVar'); }); - function emitSourceMap(stmt: o.Statement | o.Statement[], preamble?: string): SourceMap { + function emitSourceMap(stmt: o.Statement|o.Statement[], preamble?: string): SourceMap { const stmts = Array.isArray(stmt) ? stmt : [stmt]; const source = emitter.emitStatements(someGenFilePath, stmts, preamble); - return extractSourceMap(source) !; + return extractSourceMap(source)!; } describe('source maps', () => { diff --git a/packages/compiler/test/output/ts_emitter_spec.ts b/packages/compiler/test/output/ts_emitter_spec.ts index cc287c22a80c5..e499de649bde8 100644 --- a/packages/compiler/test/output/ts_emitter_spec.ts +++ b/packages/compiler/test/output/ts_emitter_spec.ts @@ -35,7 +35,7 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some someVar = o.variable('someVar', null, null); }); - function emitStmt(stmt: o.Statement | o.Statement[], preamble?: string): string { + function emitStmt(stmt: o.Statement|o.Statement[], preamble?: string): string { const stmts = Array.isArray(stmt) ? stmt : [stmt]; const source = emitter.emitStatements(someGenFilePath, stmts, preamble); return stripSourceMapAndNewLine(source); @@ -223,15 +223,17 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some }); it('should support function statements', () => { - expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [ - ]))).toEqual(['function someFn():void {', '}'].join('\n')); + expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], []))).toEqual([ + 'function someFn():void {', '}' + ].join('\n')); expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [], null, [o.StmtModifier.Exported]))) .toEqual(['export function someFn():void {', '}'].join('\n')); expect(emitStmt(new o.DeclareFunctionStmt( 'someFn', [], [new o.ReturnStatement(o.literal(1))], o.INT_TYPE))) .toEqual(['function someFn():number {', ' return 1;', '}'].join('\n')); - expect(emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1', o.INT_TYPE)], [ - ]))).toEqual(['function someFn(param1:number):void {', '}'].join('\n')); + expect( + emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1', o.INT_TYPE)], []))) + .toEqual(['function someFn(param1:number):void {', '}'].join('\n')); }); it('should support comments', () => { @@ -266,43 +268,47 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some ].join('\n')); }); - it('should support support throwing', - () => { expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); }); + it('should support support throwing', () => { + expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); + }); describe('classes', () => { let callSomeMethod: o.Statement; - beforeEach(() => { callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); }); + beforeEach(() => { + callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); + }); it('should support declaring classes', () => { - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ - ]))).toEqual(['class SomeClass {', '}'].join('\n')); - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [], [ + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, []))).toEqual([ + 'class SomeClass {', '}' + ].join('\n')); + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, [], [ o.StmtModifier.Exported ]))).toEqual(['export class SomeClass {', '}'].join('\n')); - expect(emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null !, [ - ]))).toEqual(['class SomeClass extends SomeSuperClass {', '}'].join('\n')); + expect( + emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null!, []))) + .toEqual(['class SomeClass extends SomeSuperClass {', '}'].join('\n')); }); it('should support declaring constructors', () => { const superCall = o.SUPER_EXPR.callFn([o.variable('someParam')]).toStmt(); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], new o.ClassMethod(null !, [], []), []))) + 'SomeClass', null!, [], [], new o.ClassMethod(null!, [], []), []))) .toEqual(['class SomeClass {', ' constructor() {', ' }', '}'].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], - new o.ClassMethod(null !, [new o.FnParam('someParam', o.INT_TYPE)], []), []))) + 'SomeClass', null!, [], [], + new o.ClassMethod(null!, [new o.FnParam('someParam', o.INT_TYPE)], []), []))) .toEqual( ['class SomeClass {', ' constructor(someParam:number) {', ' }', '}'].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], new o.ClassMethod(null !, [], [superCall]), []))) + 'SomeClass', null!, [], [], new o.ClassMethod(null!, [], [superCall]), []))) .toEqual([ 'class SomeClass {', ' constructor() {', ' super(someParam);', ' }', '}' ].join('\n')); - expect( - emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], new o.ClassMethod(null !, [], [callSomeMethod]), []))) + expect(emitStmt(new o.ClassStmt( + 'SomeClass', null!, [], [], new o.ClassMethod(null!, [], [callSomeMethod]), []))) .toEqual([ 'class SomeClass {', ' constructor() {', ' this.someMethod();', ' }', '}' ].join('\n')); @@ -310,57 +316,56 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some it('should support declaring fields', () => { expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [new o.ClassField('someField')], [], null !, []))) + 'SomeClass', null!, [new o.ClassField('someField')], [], null!, []))) .toEqual(['class SomeClass {', ' someField:any;', '}'].join('\n')); - expect( - emitStmt(new o.ClassStmt( - 'SomeClass', null !, [new o.ClassField('someField', o.INT_TYPE)], [], null !, []))) + expect(emitStmt(new o.ClassStmt( + 'SomeClass', null!, [new o.ClassField('someField', o.INT_TYPE)], [], null!, []))) .toEqual(['class SomeClass {', ' someField:number;', '}'].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, - [new o.ClassField('someField', o.INT_TYPE, [o.StmtModifier.Private])], [], - null !, []))) + 'SomeClass', null!, + [new o.ClassField('someField', o.INT_TYPE, [o.StmtModifier.Private])], [], null!, + []))) .toEqual(['class SomeClass {', ' /*private*/ someField:number;', '}'].join('\n')); }); it('should support declaring getters', () => { expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [new o.ClassGetter('someGetter', [])], null !, []))) + 'SomeClass', null!, [], [new o.ClassGetter('someGetter', [])], null!, []))) .toEqual(['class SomeClass {', ' get someGetter():any {', ' }', '}'].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [new o.ClassGetter('someGetter', [], o.INT_TYPE)], - null !, []))) + 'SomeClass', null!, [], [new o.ClassGetter('someGetter', [], o.INT_TYPE)], null!, + []))) .toEqual(['class SomeClass {', ' get someGetter():number {', ' }', '}'].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [new o.ClassGetter('someGetter', [callSomeMethod])], - null !, []))) + 'SomeClass', null!, [], [new o.ClassGetter('someGetter', [callSomeMethod])], + null!, []))) .toEqual([ 'class SomeClass {', ' get someGetter():any {', ' this.someMethod();', ' }', '}' ].join('\n')); - expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], - [new o.ClassGetter('someGetter', [], null !, [o.StmtModifier.Private])], null !, - []))) + expect( + emitStmt(new o.ClassStmt( + 'SomeClass', null!, [], + [new o.ClassGetter('someGetter', [], null!, [o.StmtModifier.Private])], null!, []))) .toEqual( ['class SomeClass {', ' private get someGetter():any {', ' }', '}'].join('\n')); }); it('should support methods', () => { - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, [ new o.ClassMethod('someMethod', [], []) ]))).toEqual(['class SomeClass {', ' someMethod():void {', ' }', '}'].join('\n')); - expect(emitStmt(new o.ClassStmt('SomeClass', null !, [], [], null !, [ + expect(emitStmt(new o.ClassStmt('SomeClass', null!, [], [], null!, [ new o.ClassMethod('someMethod', [], [], o.INT_TYPE) ]))).toEqual(['class SomeClass {', ' someMethod():number {', ' }', '}'].join('\n')); expect( emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], null !, + 'SomeClass', null!, [], [], null!, [new o.ClassMethod('someMethod', [new o.FnParam('someParam', o.INT_TYPE)], [])]))) .toEqual([ 'class SomeClass {', ' someMethod(someParam:number):void {', ' }', '}' ].join('\n')); expect(emitStmt(new o.ClassStmt( - 'SomeClass', null !, [], [], null !, + 'SomeClass', null!, [], [], null!, [new o.ClassMethod('someMethod', [], [callSomeMethod])]))) .toEqual([ 'class SomeClass {', ' someMethod():void {', ' this.someMethod();', ' }', '}' @@ -412,7 +417,7 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some it('should support combined types', () => { const writeVarExpr = o.variable('a').set(o.NULL_EXPR); - expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(null !)))) + expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(null!)))) .toEqual('var a:any[] = (null as any);'); expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(o.INT_TYPE)))) .toEqual('var a:number[] = (null as any);'); diff --git a/packages/compiler/test/output/value_util_spec.ts b/packages/compiler/test/output/value_util_spec.ts new file mode 100644 index 0000000000000..0098608151542 --- /dev/null +++ b/packages/compiler/test/output/value_util_spec.ts @@ -0,0 +1,23 @@ +/** + * @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 * as o from '../../src/output/output_ast'; +import {convertValueToOutputAst} from '../../src/output/value_util'; + +describe('convertValueToOutputAst', () => { + it('should convert all array elements, including undefined', () => { + const ctx = null; + const value = new Array(3).concat('foo'); + const expr = convertValueToOutputAst(ctx!, value) as o.LiteralArrayExpr; + expect(expr instanceof o.LiteralArrayExpr).toBe(true); + expect(expr.entries.length).toBe(4); + for (let i = 0; i < 4; ++i) { + expect(expr.entries[i] instanceof o.Expression).toBe(true); + } + }); +}); diff --git a/packages/compiler/test/pipe_resolver_spec.ts b/packages/compiler/test/pipe_resolver_spec.ts index e338973107856..bff6ce12636f7 100644 --- a/packages/compiler/test/pipe_resolver_spec.ts +++ b/packages/compiler/test/pipe_resolver_spec.ts @@ -21,7 +21,9 @@ class SimpleClass {} describe('PipeResolver', () => { let resolver: PipeResolver; - beforeEach(() => { resolver = new PipeResolver(new JitReflector()); }); + beforeEach(() => { + resolver = new PipeResolver(new JitReflector()); + }); it('should read out the metadata from the class', () => { const moduleMetadata = resolver.resolve(SomePipe); @@ -48,6 +50,5 @@ class SimpleClass {} expect(resolver.resolve(ChildWithDecorator)).toEqual(new Pipe({name: 'c'})); }); - }); } diff --git a/packages/compiler/test/render3/r3_ast_spans_spec.ts b/packages/compiler/test/render3/r3_ast_spans_spec.ts index 2352176974c34..f0d65ce50d243 100644 --- a/packages/compiler/test/render3/r3_ast_spans_spec.ts +++ b/packages/compiler/test/render3/r3_ast_spans_spec.ts @@ -74,18 +74,24 @@ class R3AstSourceSpans implements t.Visitor<void> { ['BoundEvent', humanizeSpan(event.sourceSpan), humanizeSpan(event.handlerSpan)]); } - visitText(text: t.Text) { this.result.push(['Text', humanizeSpan(text.sourceSpan)]); } + visitText(text: t.Text) { + this.result.push(['Text', humanizeSpan(text.sourceSpan)]); + } visitBoundText(text: t.BoundText) { this.result.push(['BoundText', humanizeSpan(text.sourceSpan)]); } - visitIcu(icu: t.Icu) { return null; } + visitIcu(icu: t.Icu) { + return null; + } - private visitAll(nodes: t.Node[][]) { nodes.forEach(node => t.visitAll(this, node)); } + private visitAll(nodes: t.Node[][]) { + nodes.forEach(node => t.visitAll(this, node)); + } } -function humanizeSpan(span: ParseSourceSpan | null | undefined): string { +function humanizeSpan(span: ParseSourceSpan|null|undefined): string { if (span === null || span === undefined) { return `<empty>`; } @@ -232,8 +238,8 @@ describe('R3 AST source spans', () => { expectFromHtml('<div *ngFor="let item of items"></div>').toEqual([ ['Template', '0:32', '0:32', '32:38'], ['TextAttribute', '5:31', '<empty>'], - ['BoundAttribute', '5:31', '<empty>'], - ['Variable', '13:22', '<empty>'], // let item + ['BoundAttribute', '5:31', '25:30'], // *ngFor="let item of items" -> items + ['Variable', '13:22', '<empty>'], // let item ['Element', '0:38', '0:32', '32:38'], ]); @@ -245,8 +251,8 @@ describe('R3 AST source spans', () => { // </ng-template> expectFromHtml('<div *ngFor="item of items"></div>').toEqual([ ['Template', '0:28', '0:28', '28:34'], - ['BoundAttribute', '5:27', '<empty>'], - ['BoundAttribute', '5:27', '<empty>'], + ['BoundAttribute', '5:27', '13:17'], // ngFor="item of items" -> item + ['BoundAttribute', '5:27', '21:26'], // ngFor="item of items" -> items ['Element', '0:34', '0:28', '28:34'], ]); }); @@ -263,8 +269,8 @@ describe('R3 AST source spans', () => { it('is correct for variables via as ...', () => { expectFromHtml('<div *ngIf="expr as local"></div>').toEqual([ ['Template', '0:27', '0:27', '27:33'], - ['BoundAttribute', '5:26', '<empty>'], - ['Variable', '6:25', '6:10'], // ngIf="expr as local -> ngIf + ['BoundAttribute', '5:26', '12:16'], // ngIf="expr as local" -> expr + ['Variable', '6:25', '6:10'], // ngIf="expr as local -> ngIf ['Element', '0:33', '0:27', '27:33'], ]); }); diff --git a/packages/compiler/test/render3/r3_template_transform_spec.ts b/packages/compiler/test/render3/r3_template_transform_spec.ts index 13f5e1b41c4f8..f43177fdcb16b 100644 --- a/packages/compiler/test/render3/r3_template_transform_spec.ts +++ b/packages/compiler/test/render3/r3_template_transform_spec.ts @@ -75,13 +75,21 @@ class R3AstHumanizer implements t.Visitor<void> { ]); } - visitText(text: t.Text) { this.result.push(['Text', text.value]); } + visitText(text: t.Text) { + this.result.push(['Text', text.value]); + } - visitBoundText(text: t.BoundText) { this.result.push(['BoundText', unparse(text.value)]); } + visitBoundText(text: t.BoundText) { + this.result.push(['BoundText', unparse(text.value)]); + } - visitIcu(icu: t.Icu) { return null; } + visitIcu(icu: t.Icu) { + return null; + } - private visitAll(nodes: t.Node[][]) { nodes.forEach(node => t.visitAll(this, node)); } + private visitAll(nodes: t.Node[][]) { + nodes.forEach(node => t.visitAll(this, node)); + } } function expectFromHtml(html: string) { @@ -97,13 +105,14 @@ function expectFromR3Nodes(nodes: t.Node[]) { function expectSpanFromHtml(html: string) { const {nodes} = parse(html); - return expect(nodes[0] !.sourceSpan.toString()); + return expect(nodes[0]!.sourceSpan.toString()); } describe('R3 template transform', () => { describe('ParseSpan on nodes toString', () => { - it('should create valid text span on Element with adjacent start and end tags', - () => { expectSpanFromHtml('<div></div>').toBe('<div></div>'); }); + it('should create valid text span on Element with adjacent start and end tags', () => { + expectSpanFromHtml('<div></div>').toBe('<div></div>'); + }); }); describe('Nodes without binding', () => { diff --git a/packages/compiler/test/render3/style_parser_spec.ts b/packages/compiler/test/render3/style_parser_spec.ts index 4f44f6aebb276..e0d9533bec76f 100644 --- a/packages/compiler/test/render3/style_parser_spec.ts +++ b/packages/compiler/test/render3/style_parser_spec.ts @@ -90,7 +90,8 @@ describe('style parsing', () => { expect(hyphenate('-fooBar-man')).toEqual('-foo-bar-man'); }); - it('should make everything lowercase', - () => { expect(hyphenate('-WebkitAnimation')).toEqual('-webkit-animation'); }); + it('should make everything lowercase', () => { + expect(hyphenate('-WebkitAnimation')).toEqual('-webkit-animation'); + }); }); }); diff --git a/packages/compiler/test/render3/util/expression.ts b/packages/compiler/test/render3/util/expression.ts index 65de317e1f56a..30d7dafa054b8 100644 --- a/packages/compiler/test/render3/util/expression.ts +++ b/packages/compiler/test/render3/util/expression.ts @@ -15,7 +15,9 @@ type HumanizedExpressionSource = [string, AbsoluteSourceSpan]; class ExpressionSourceHumanizer extends e.RecursiveAstVisitor implements t.Visitor { result: HumanizedExpressionSource[] = []; - private recordAst(ast: e.AST) { this.result.push([unparse(ast), ast.sourceSpan]); } + private recordAst(ast: e.AST) { + this.result.push([unparse(ast), ast.sourceSpan]); + } // This method is defined to reconcile the type of ExpressionSourceHumanizer // since both RecursiveAstVisitor and Visitor define the visit() method in @@ -124,11 +126,19 @@ class ExpressionSourceHumanizer extends e.RecursiveAstVisitor implements t.Visit } visitReference(ast: t.Reference) {} visitVariable(ast: t.Variable) {} - visitEvent(ast: t.BoundEvent) { ast.handler.visit(this); } + visitEvent(ast: t.BoundEvent) { + ast.handler.visit(this); + } visitTextAttribute(ast: t.TextAttribute) {} - visitBoundAttribute(ast: t.BoundAttribute) { ast.value.visit(this); } - visitBoundEvent(ast: t.BoundEvent) { ast.handler.visit(this); } - visitBoundText(ast: t.BoundText) { ast.value.visit(this); } + visitBoundAttribute(ast: t.BoundAttribute) { + ast.value.visit(this); + } + visitBoundEvent(ast: t.BoundEvent) { + ast.handler.visit(this); + } + visitBoundText(ast: t.BoundText) { + ast.value.visit(this); + } visitContent(ast: t.Content) {} visitText(ast: t.Text) {} visitIcu(ast: t.Icu) {} diff --git a/packages/compiler/test/render3/view/binding_spec.ts b/packages/compiler/test/render3/view/binding_spec.ts index e47c57d18bb90..15cad85c71302 100644 --- a/packages/compiler/test/render3/view/binding_spec.ts +++ b/packages/compiler/test/render3/view/binding_spec.ts @@ -54,8 +54,9 @@ describe('t2 binding', () => { const binder = new R3TargetBinder(new SelectorMatcher<DirectiveMeta>()); const res = binder.bind({template: template.nodes}); - const itemBinding = (findExpression(template.nodes, '{{item.name}}') !as e.Interpolation) - .expressions[0] as e.PropertyRead; + const itemBinding = + (findExpression(template.nodes, '{{item.name}}')! as e.Interpolation).expressions[0] as + e.PropertyRead; const item = itemBinding.receiver; const itemTarget = res.getExpressionTarget(item); if (!(itemTarget instanceof a.Variable)) { @@ -64,7 +65,7 @@ describe('t2 binding', () => { expect(itemTarget.value).toBe('$implicit'); const itemTemplate = res.getTemplateOfSymbol(itemTarget); expect(itemTemplate).not.toBeNull(); - expect(res.getNestingLevel(itemTemplate !)).toBe(1); + expect(res.getNestingLevel(itemTemplate!)).toBe(1); }); it('should match directives when binding a simple template', () => { @@ -72,7 +73,7 @@ describe('t2 binding', () => { const binder = new R3TargetBinder(makeSelectorMatcher()); const res = binder.bind({template: template.nodes}); const tmpl = template.nodes[0] as a.Template; - const directives = res.getDirectivesOfNode(tmpl) !; + const directives = res.getDirectivesOfNode(tmpl)!; expect(directives).not.toBeNull(); expect(directives.length).toBe(1); expect(directives[0].name).toBe('NgFor'); @@ -92,7 +93,7 @@ describe('t2 binding', () => { const res = binder.bind({template: template.nodes}); const svgNode = template.nodes[0] as a.Element; const textNode = svgNode.children[0] as a.Element; - const directives = res.getDirectivesOfNode(textNode) !; + const directives = res.getDirectivesOfNode(textNode)!; expect(directives).not.toBeNull(); expect(directives.length).toBe(1); expect(directives[0].name).toBe('Dir'); @@ -103,11 +104,11 @@ describe('t2 binding', () => { const binder = new R3TargetBinder(makeSelectorMatcher()); const res = binder.bind({template: template.nodes}); const tmpl = template.nodes[0] as a.Template; - const tmplDirectives = res.getDirectivesOfNode(tmpl) !; + const tmplDirectives = res.getDirectivesOfNode(tmpl)!; expect(tmplDirectives).not.toBeNull(); expect(tmplDirectives.length).toBe(1); expect(tmplDirectives[0].name).toBe('NgFor'); - const elDirectives = res.getDirectivesOfNode(tmpl.children[0] as a.Element) !; + const elDirectives = res.getDirectivesOfNode(tmpl.children[0] as a.Element)!; expect(elDirectives).not.toBeNull(); expect(elDirectives.length).toBe(1); expect(elDirectives[0].name).toBe('Dir'); diff --git a/packages/compiler/test/render3/view/i18n_spec.ts b/packages/compiler/test/render3/view/i18n_spec.ts index afa377807527d..ffdb57fee19fe 100644 --- a/packages/compiler/test/render3/view/i18n_spec.ts +++ b/packages/compiler/test/render3/view/i18n_spec.ts @@ -21,7 +21,7 @@ import {formatI18nPlaceholderName} from '../../../src/render3/view/i18n/util'; import {parseR3 as parse} from './util'; const expressionParser = new Parser(new Lexer()); -const i18nOf = (element: t.Node & {i18n?: i18n.I18nMeta}) => element.i18n !; +const i18nOf = (element: t.Node&{i18n?: i18n.I18nMeta}) => element.i18n!; describe('I18nContext', () => { it('should support i18n content collection', () => { @@ -74,7 +74,7 @@ describe('I18nContext', () => { const [boundTextB, elementC, boundTextC] = (elementB as t.Element).children; // simulate I18nContext for a given template - const ctx = new I18nContext(1, o.variable('ctx'), 0, null, root.i18n !); + const ctx = new I18nContext(1, o.variable('ctx'), 0, null, root.i18n!); // set data for root ctx ctx.appendBoundText(i18nOf(boundTextA)); @@ -87,7 +87,7 @@ describe('I18nContext', () => { expect(ctx.isResolved).toBe(false); // create child context - const childCtx = ctx.forkChildContext(2, 1, (templateA as t.Template).i18n !); + const childCtx = ctx.forkChildContext(2, 1, (templateA as t.Template).i18n!); expect(childCtx.bindings.size).toBe(0); expect(childCtx.isRoot).toBe(false); @@ -116,11 +116,12 @@ describe('I18nContext', () => { const expected = new Map([ ['INTERPOLATION', '�0�'], ['START_TAG_DIV', '�#0�|�#1:1�'], ['START_BOLD_TEXT', '�*1:1��#0:1�'], ['CLOSE_BOLD_TEXT', '�/#0:1��/*1:1�'], - ['CLOSE_TAG_DIV', '�/#0�|�/#1:1�'], ['INTERPOLATION_1', '�0:1�'], - ['INTERPOLATION_2', '�1:1�'] + ['CLOSE_TAG_DIV', '�/#0�|�/#1:1�'], ['INTERPOLATION_1', '�0:1�'], ['INTERPOLATION_2', '�1:1�'] ]); const phs = ctx.getSerializedPlaceholders(); - expected.forEach((value, key) => { expect(phs.get(key) !.join('|')).toEqual(value); }); + expected.forEach((value, key) => { + expect(phs.get(key)!.join('|')).toEqual(value); + }); // placeholders are added into the root ctx expect(phs.size).toBe(expected.size); @@ -152,7 +153,7 @@ describe('I18nContext', () => { const [textC] = (templateB as t.Template).children; // simulate I18nContext for a given template - const ctxLevelA = new I18nContext(0, o.variable('ctx'), 0, null, root.i18n !); + const ctxLevelA = new I18nContext(0, o.variable('ctx'), 0, null, root.i18n!); // create Level A context ctxLevelA.appendTemplate(i18nOf(templateA), 1); @@ -160,12 +161,12 @@ describe('I18nContext', () => { expect(ctxLevelA.isResolved).toBe(false); // create Level B context - const ctxLevelB = ctxLevelA.forkChildContext(0, 1, (templateA as t.Template).i18n !); + const ctxLevelB = ctxLevelA.forkChildContext(0, 1, (templateA as t.Template).i18n!); ctxLevelB.appendTemplate(i18nOf(templateB), 1); expect(ctxLevelB.isRoot).toBe(false); // create Level 2 context - const ctxLevelC = ctxLevelB.forkChildContext(0, 1, (templateB as t.Template).i18n !); + const ctxLevelC = ctxLevelB.forkChildContext(0, 1, (templateB as t.Template).i18n!); expect(ctxLevelC.isRoot).toBe(false); // reconcile @@ -176,7 +177,9 @@ describe('I18nContext', () => { const expected = new Map( [['START_TAG_NG-TEMPLATE', '�*1:1�|�*1:2�'], ['CLOSE_TAG_NG-TEMPLATE', '�/*1:2�|�/*1:1�']]); const phs = ctxLevelA.getSerializedPlaceholders(); - expected.forEach((value, key) => { expect(phs.get(key) !.join('|')).toEqual(value); }); + expected.forEach((value, key) => { + expect(phs.get(key)!.join('|')).toEqual(value); + }); // placeholders are added into the root ctx expect(phs.size).toBe(expected.size); @@ -195,8 +198,9 @@ describe('Utils', () => { ['START_TAG_NG-CONTAINER_1', 'startTagNgContainer_1'], ['CLOSE_TAG_ITALIC', 'closeTagItalic'], ['CLOSE_TAG_BOLD_1', 'closeTagBold_1'] ]; - cases.forEach( - ([input, output]) => { expect(formatI18nPlaceholderName(input)).toEqual(output); }); + cases.forEach(([input, output]) => { + expect(formatI18nPlaceholderName(input)).toEqual(output); + }); }); describe('metadata serialization', () => { @@ -292,8 +296,9 @@ describe('serializeI18nMessageForGetMsg', () => { return serializeI18nMessageForGetMsg(root.i18n as i18n.Message); }; - it('should serialize plain text for `GetMsg()`', - () => { expect(serialize('Some text')).toEqual('Some text'); }); + it('should serialize plain text for `GetMsg()`', () => { + expect(serialize('Some text')).toEqual('Some text'); + }); it('should serialize text with interpolation for `GetMsg()`', () => { expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}')) diff --git a/packages/compiler/test/render3/view/util.ts b/packages/compiler/test/render3/view/util.ts index df1268fed95ba..0ecf3ab42d0f3 100644 --- a/packages/compiler/test/render3/view/util.ts +++ b/packages/compiler/test/render3/view/util.ts @@ -14,7 +14,7 @@ import {HtmlParser, ParseTreeResult} from '../../../src/ml_parser/html_parser'; import {WhitespaceVisitor} from '../../../src/ml_parser/html_whitespaces'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../src/ml_parser/interpolation_config'; import * as a from '../../../src/render3/r3_ast'; -import {Render3ParseResult, htmlAstToRender3Ast} from '../../../src/render3/r3_template_transform'; +import {htmlAstToRender3Ast, Render3ParseResult} from '../../../src/render3/r3_template_transform'; import {I18nMetaVisitor} from '../../../src/render3/view/i18n/meta'; import {BindingParser} from '../../../src/template_parser/binding_parser'; import {MockSchemaRegistry} from '../../../testing'; diff --git a/packages/compiler/test/resource_loader_mock_spec.ts b/packages/compiler/test/resource_loader_mock_spec.ts index 760ff5fce08d2..8df73f398fc0c 100644 --- a/packages/compiler/test/resource_loader_mock_spec.ts +++ b/packages/compiler/test/resource_loader_mock_spec.ts @@ -13,10 +13,12 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang describe('MockResourceLoader', () => { let resourceLoader: MockResourceLoader; - beforeEach(() => { resourceLoader = new MockResourceLoader(); }); + beforeEach(() => { + resourceLoader = new MockResourceLoader(); + }); function expectResponse( - request: Promise<string>, url: string, response: string, done: () => void = null !) { + request: Promise<string>, url: string, response: string, done: () => void = null!) { function onResponse(text: string): string { if (response === null) { throw `Unexpected response ${url} -> ${text}`; @@ -52,7 +54,7 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang it('should return an error from the definitions', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const url = '/foo'; - const response: string = null !; + const response: string = null!; resourceLoader.when(url, response); expectResponse(resourceLoader.get(url), url, response, () => async.done()); resourceLoader.flush(); @@ -70,7 +72,7 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang it('should return an error from the expectations', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const url = '/foo'; - const response: string = null !; + const response: string = null!; resourceLoader.expect(url, response); expectResponse(resourceLoader.get(url), url, response, () => async.done()); resourceLoader.flush(); @@ -82,7 +84,9 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang resourceLoader.expect(url, response); resourceLoader.get(url); resourceLoader.get(url); - expect(() => { resourceLoader.flush(); }).toThrowError('Unexpected request /foo'); + expect(() => { + resourceLoader.flush(); + }).toThrowError('Unexpected request /foo'); }); it('should return expectations before definitions', @@ -97,18 +101,24 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang it('should throw when there is no definitions or expectations', () => { resourceLoader.get('/foo'); - expect(() => { resourceLoader.flush(); }).toThrowError('Unexpected request /foo'); + expect(() => { + resourceLoader.flush(); + }).toThrowError('Unexpected request /foo'); }); it('should throw when flush is called without any pending requests', () => { - expect(() => { resourceLoader.flush(); }).toThrowError('No pending requests to flush'); + expect(() => { + resourceLoader.flush(); + }).toThrowError('No pending requests to flush'); }); it('should throw on unsatisfied expectations', () => { resourceLoader.expect('/foo', 'bar'); resourceLoader.when('/bar', 'foo'); resourceLoader.get('/bar'); - expect(() => { resourceLoader.flush(); }).toThrowError('Unsatisfied requests: /foo'); + expect(() => { + resourceLoader.flush(); + }).toThrowError('Unsatisfied requests: /foo'); }); }); } diff --git a/packages/compiler/test/runtime_compiler_spec.ts b/packages/compiler/test/runtime_compiler_spec.ts index 47423395bd6fc..a66dfb3ccda2c 100644 --- a/packages/compiler/test/runtime_compiler_spec.ts +++ b/packages/compiler/test/runtime_compiler_spec.ts @@ -8,9 +8,11 @@ import {DirectiveResolver, ResourceLoader} from '@angular/compiler'; import {Compiler, Component, Injector, NgModule, NgModuleFactory, ɵstringify as stringify} from '@angular/core'; -import {TestBed, async, fakeAsync, inject, tick} from '@angular/core/testing'; +import {async, fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; + import {MockDirectiveResolver} from '../testing'; + import {SpyResourceLoader} from './spies'; @Component({selector: 'child-cmp'}) @@ -27,11 +29,12 @@ class SomeCompWithUrlTemplate { { describe('RuntimeCompiler', () => { - describe('compilerComponentSync', () => { describe('never resolving loader', () => { class StubResourceLoader { - get(url: string) { return new Promise(() => {}); } + get(url: string) { + return new Promise(() => {}); + } } beforeEach(() => { @@ -43,8 +46,8 @@ class SomeCompWithUrlTemplate { TestBed.configureTestingModule({declarations: [SomeCompWithUrlTemplate]}); TestBed.compileComponents().then(() => { expect(() => TestBed.createComponent(SomeCompWithUrlTemplate)) - .toThrowError( - `Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`); + .toThrowError(`Can't compile synchronously as ${ + stringify(SomeCompWithUrlTemplate)} is still being loaded!`); }); })); @@ -55,15 +58,17 @@ class SomeCompWithUrlTemplate { TestBed.overrideComponent(SomeComp, {set: {template: '<child-cmp></child-cmp>'}}); TestBed.compileComponents().then(() => { expect(() => TestBed.createComponent(SomeComp)) - .toThrowError( - `Can't compile synchronously as ${stringify(ChildComp)} is still being loaded!`); + .toThrowError(`Can't compile synchronously as ${ + stringify(ChildComp)} is still being loaded!`); }); }); }); describe('resolving loader', () => { class StubResourceLoader { - get(url: string) { return Promise.resolve('hello'); } + get(url: string) { + return Promise.resolve('hello'); + } } beforeEach(() => { @@ -88,7 +93,9 @@ class SomeCompWithUrlTemplate { let dirResolver: MockDirectiveResolver; let injector: Injector; - beforeEach(() => { TestBed.configureCompiler({providers: [SpyResourceLoader.PROVIDE]}); }); + beforeEach(() => { + TestBed.configureCompiler({providers: [SpyResourceLoader.PROVIDE]}); + }); beforeEach(fakeAsync(inject( [Compiler, ResourceLoader, DirectiveResolver, Injector], @@ -110,7 +117,7 @@ class SomeCompWithUrlTemplate { } resourceLoader.spy('get').and.callFake(() => Promise.resolve('hello')); - let ngModuleFactory: NgModuleFactory<any> = undefined !; + let ngModuleFactory: NgModuleFactory<any> = undefined!; compiler.compileModuleAsync(SomeModule).then((f) => ngModuleFactory = f); tick(); expect(ngModuleFactory.moduleType).toBe(SomeModule); @@ -126,8 +133,8 @@ class SomeCompWithUrlTemplate { resourceLoader.spy('get').and.callFake(() => Promise.resolve('')); expect(() => compiler.compileModuleSync(SomeModule)) - .toThrowError( - `Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`); + .toThrowError(`Can't compile synchronously as ${ + stringify(SomeCompWithUrlTemplate)} is still being loaded!`); }); it('should throw when using a templateUrl in a nested component that has not been compiled before', diff --git a/packages/compiler/test/schema/dom_element_schema_registry_spec.ts b/packages/compiler/test/schema/dom_element_schema_registry_spec.ts index 7c6b98c67c4b7..ef2a312633d1f 100644 --- a/packages/compiler/test/schema/dom_element_schema_registry_spec.ts +++ b/packages/compiler/test/schema/dom_element_schema_registry_spec.ts @@ -19,7 +19,9 @@ import {extractSchema} from './schema_extractor'; { describe('DOMElementSchema', () => { let registry: DomElementSchemaRegistry; - beforeEach(() => { registry = new DomElementSchemaRegistry(); }); + beforeEach(() => { + registry = new DomElementSchemaRegistry(); + }); it('should detect elements', () => { expect(registry.hasElement('div', [])).toBeTruthy(); @@ -97,8 +99,9 @@ import {extractSchema} from './schema_extractor'; expect(registry.hasElement('unknown', [NO_ERRORS_SCHEMA])).toBeTruthy(); }); - it('should re-map property names that are specified in DOM facade', - () => { expect(registry.getMappedPropName('readonly')).toEqual('readOnly'); }); + it('should re-map property names that are specified in DOM facade', () => { + expect(registry.getMappedPropName('readonly')).toEqual('readOnly'); + }); it('should not re-map property names that are not specified in DOM facade', () => { expect(registry.getMappedPropName('title')).toEqual('title'); @@ -173,8 +176,9 @@ If 'onAnything' is a directive input, make sure the directive is imported by the }); describe('Angular custom elements', () => { - it('should support <ng-container>', - () => { expect(registry.hasProperty('ng-container', 'id', [])).toBeFalsy(); }); + it('should support <ng-container>', () => { + expect(registry.hasProperty('ng-container', 'id', [])).toBeFalsy(); + }); it('should support <ng-content>', () => { expect(registry.hasProperty('ng-content', 'id', [])).toBeFalsy(); @@ -185,8 +189,9 @@ If 'onAnything' is a directive input, make sure the directive is imported by the if (browserDetection.isChromeDesktop) { it('generate a new schema', () => { let schema = '\n'; - extractSchema() !.forEach( - (props, name) => { schema += `'${name}|${props.join(',')}',\n`; }); + extractSchema()!.forEach((props, name) => { + schema += `'${name}|${props.join(',')}',\n`; + }); // Uncomment this line to see: // the generated schema which can then be pasted to the DomElementSchemaRegistry // console.log(schema); diff --git a/packages/compiler/test/schema/schema_extractor.ts b/packages/compiler/test/schema/schema_extractor.ts index ec25bda11ba3a..27a9650d9161a 100644 --- a/packages/compiler/test/schema/schema_extractor.ts +++ b/packages/compiler/test/schema/schema_extractor.ts @@ -97,7 +97,9 @@ export function extractSchema(): Map<string, string[]>|null { types.sort(); - types.forEach(type => { extractRecursiveProperties(visited, descMap, (window as any)[type]); }); + types.forEach(type => { + extractRecursiveProperties(visited, descMap, (window as any)[type]); + }); // Add elements missed by Chrome auto-detection Object.keys(MISSING_FROM_CHROME).forEach(elHierarchy => { @@ -125,7 +127,7 @@ function assertNoMissingTags(descMap: Map<string, string[]>): void { function extractRecursiveProperties( visited: {[name: string]: boolean}, descMap: Map<string, string[]>, type: Function): string { - const name = extractName(type) !; + const name = extractName(type)!; if (visited[name]) { return name; @@ -181,7 +183,7 @@ function extractProperties( const fullName = name + (superName ? '^' + superName : ''); - const props: string[] = descMap.has(fullName) ? descMap.get(fullName) ! : []; + const props: string[] = descMap.has(fullName) ? descMap.get(fullName)! : []; const prototype = type.prototype; const keys = Object.getOwnPropertyNames(prototype); diff --git a/packages/compiler/test/selector/selector_spec.ts b/packages/compiler/test/selector/selector_spec.ts index b61bc7ad44dd1..33ecbe686a6fd 100644 --- a/packages/compiler/test/selector/selector_spec.ts +++ b/packages/compiler/test/selector/selector_spec.ts @@ -17,13 +17,16 @@ import {el} from '@angular/platform-browser/testing/src/browser_util'; let s1: any[], s2: any[], s3: any[], s4: any[]; let matched: any[]; - function reset() { matched = []; } + function reset() { + matched = []; + } beforeEach(() => { reset(); - s1 = s2 = s3 = s4 = null !; - selectableCollector = - (selector: CssSelector, context: any) => { matched.push(selector, context); }; + s1 = s2 = s3 = s4 = null!; + selectableCollector = (selector: CssSelector, context: any) => { + matched.push(selector, context); + }; matcher = new SelectorMatcher(); }); @@ -128,7 +131,7 @@ import {el} from '@angular/platform-browser/testing/src/browser_util'; const elementSelector = new CssSelector(); const element = el('<div attr></div>'); - const empty = element.getAttribute('attr') !; + const empty = element.getAttribute('attr')!; elementSelector.addAttribute('some-decor', empty); matcher.match(elementSelector, selectableCollector); expect(matched).toEqual([s1[0], 1]); @@ -458,9 +461,13 @@ function getSelectorFor( const selector = new CssSelector(); selector.setElement(tag); - attrs.forEach(nameValue => { selector.addAttribute(nameValue[0], nameValue[1]); }); + attrs.forEach(nameValue => { + selector.addAttribute(nameValue[0], nameValue[1]); + }); - classes.trim().split(/\s+/g).forEach(cName => { selector.addClassName(cName); }); + classes.trim().split(/\s+/g).forEach(cName => { + selector.addClassName(cName); + }); return selector; } diff --git a/packages/compiler/test/shadow_css_spec.ts b/packages/compiler/test/shadow_css_spec.ts index 8bf0cd85e266a..e23e7e28aa52f 100644 --- a/packages/compiler/test/shadow_css_spec.ts +++ b/packages/compiler/test/shadow_css_spec.ts @@ -6,12 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {CssRule, ShadowCss, processRules} from '@angular/compiler/src/shadow_css'; +import {CssRule, processRules, ShadowCss} from '@angular/compiler/src/shadow_css'; import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; { describe('ShadowCss', function() { - function s(css: string, contentAttr: string, hostAttr: string = '') { const shadowCss = new ShadowCss(); const shim = shadowCss.shimCssText(css, contentAttr, hostAttr); @@ -19,7 +18,9 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; return normalizeCSS(shim.replace(nlRegexp, '')); } - it('should handle empty string', () => { expect(s('', 'contenta')).toEqual(''); }); + it('should handle empty string', () => { + expect(s('', 'contenta')).toEqual(''); + }); it('should add an attribute to every rule', () => { const css = 'one {color: red;}two {color: red;}'; @@ -112,14 +113,17 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; }); describe((':host'), () => { - it('should handle no context', - () => { expect(s(':host {}', 'contenta', 'a-host')).toEqual('[a-host] {}'); }); + it('should handle no context', () => { + expect(s(':host {}', 'contenta', 'a-host')).toEqual('[a-host] {}'); + }); - it('should handle tag selector', - () => { expect(s(':host(ul) {}', 'contenta', 'a-host')).toEqual('ul[a-host] {}'); }); + it('should handle tag selector', () => { + expect(s(':host(ul) {}', 'contenta', 'a-host')).toEqual('ul[a-host] {}'); + }); - it('should handle class selector', - () => { expect(s(':host(.x) {}', 'contenta', 'a-host')).toEqual('.x[a-host] {}'); }); + it('should handle class selector', () => { + expect(s(':host(.x) {}', 'contenta', 'a-host')).toEqual('.x[a-host] {}'); + }); it('should handle attribute selector', () => { expect(s(':host([a="b"]) {}', 'contenta', 'a-host')).toEqual('[a="b"][a-host] {}'); @@ -285,14 +289,17 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; expect(css).toEqual('div[contenta] {height:calc(100% - 55px);}'); }); - it('should strip comments', - () => { expect(s('/* x */b {c}', 'contenta')).toEqual('b[contenta] {c}'); }); + it('should strip comments', () => { + expect(s('/* x */b {c}', 'contenta')).toEqual('b[contenta] {c}'); + }); - it('should ignore special characters in comments', - () => { expect(s('/* {;, */b {c}', 'contenta')).toEqual('b[contenta] {c}'); }); + it('should ignore special characters in comments', () => { + expect(s('/* {;, */b {c}', 'contenta')).toEqual('b[contenta] {c}'); + }); - it('should support multiline comments', - () => { expect(s('/* \n */b {c}', 'contenta')).toEqual('b[contenta] {c}'); }); + it('should support multiline comments', () => { + expect(s('/* \n */b {c}', 'contenta')).toEqual('b[contenta] {c}'); + }); it('should keep sourceMappingURL comments', () => { expect(s('b {c}/*# sourceMappingURL=data:x */', 'contenta')) @@ -318,13 +325,17 @@ import {normalizeCSS} from '@angular/platform-browser/testing/src/browser_util'; return result; } - it('should work with empty css', () => { expect(captureRules('')).toEqual([]); }); + it('should work with empty css', () => { + expect(captureRules('')).toEqual([]); + }); - it('should capture a rule without body', - () => { expect(captureRules('a;')).toEqual([new CssRule('a', '')]); }); + it('should capture a rule without body', () => { + expect(captureRules('a;')).toEqual([new CssRule('a', '')]); + }); - it('should capture css rules with body', - () => { expect(captureRules('a {b}')).toEqual([new CssRule('a', 'b')]); }); + it('should capture css rules with body', () => { + expect(captureRules('a {b}')).toEqual([new CssRule('a', 'b')]); + }); it('should capture css rules with nested rules', () => { expect(captureRules('a {b {c}} d {e}')).toEqual([ diff --git a/packages/compiler/test/spies.ts b/packages/compiler/test/spies.ts index 54c2263c4ad9f..a5575dffb2dd7 100644 --- a/packages/compiler/test/spies.ts +++ b/packages/compiler/test/spies.ts @@ -12,5 +12,7 @@ import {SpyObject} from '@angular/core/testing/src/testing_internal'; export class SpyResourceLoader extends SpyObject { public static PROVIDE = {provide: ResourceLoader, useClass: SpyResourceLoader, deps: []}; - constructor() { super(ResourceLoader); } + constructor() { + super(ResourceLoader); + } } diff --git a/packages/compiler/test/style_url_resolver_spec.ts b/packages/compiler/test/style_url_resolver_spec.ts index ef8aebdb5c6ff..c91f3cc6874cf 100644 --- a/packages/compiler/test/style_url_resolver_spec.ts +++ b/packages/compiler/test/style_url_resolver_spec.ts @@ -13,7 +13,9 @@ import {UrlResolver} from '@angular/compiler/src/url_resolver'; describe('extractStyleUrls', () => { let urlResolver: UrlResolver; - beforeEach(() => { urlResolver = new UrlResolver(); }); + beforeEach(() => { + urlResolver = new UrlResolver(); + }); it('should not resolve "url()" urls', () => { const css = ` @@ -104,23 +106,25 @@ import {UrlResolver} from '@angular/compiler/src/url_resolver'; expect(styleWithImports.style.trim()).toEqual(``); expect(styleWithImports.styleUrls).toEqual(['fake_resolved_url']); }); - }); describe('isStyleUrlResolvable', () => { - it('should resolve relative urls', - () => { expect(isStyleUrlResolvable('someUrl.css')).toBe(true); }); + it('should resolve relative urls', () => { + expect(isStyleUrlResolvable('someUrl.css')).toBe(true); + }); - it('should resolve package: urls', - () => { expect(isStyleUrlResolvable('package:someUrl.css')).toBe(true); }); + it('should resolve package: urls', () => { + expect(isStyleUrlResolvable('package:someUrl.css')).toBe(true); + }); it('should not resolve empty urls', () => { - expect(isStyleUrlResolvable(null !)).toBe(false); + expect(isStyleUrlResolvable(null!)).toBe(false); expect(isStyleUrlResolvable('')).toBe(false); }); - it('should not resolve urls with other schema', - () => { expect(isStyleUrlResolvable('http://otherurl')).toBe(false); }); + it('should not resolve urls with other schema', () => { + expect(isStyleUrlResolvable('http://otherurl')).toBe(false); + }); it('should not resolve urls with absolute paths', () => { expect(isStyleUrlResolvable('/otherurl')).toBe(false); @@ -130,7 +134,11 @@ import {UrlResolver} from '@angular/compiler/src/url_resolver'; } class FakeUrlResolver extends UrlResolver { - constructor() { super(); } + constructor() { + super(); + } - resolve(baseUrl: string, url: string): string { return 'fake_resolved_url'; } + resolve(baseUrl: string, url: string): string { + return 'fake_resolved_url'; + } } diff --git a/packages/compiler/test/template_parser/binding_parser_spec.ts b/packages/compiler/test/template_parser/binding_parser_spec.ts index 77656c6bc4884..0d4f737203d82 100644 --- a/packages/compiler/test/template_parser/binding_parser_spec.ts +++ b/packages/compiler/test/template_parser/binding_parser_spec.ts @@ -16,16 +16,18 @@ import {calcPossibleSecurityContexts} from '../../src/template_parser/binding_pa describe('BindingParser', () => { let registry: ElementSchemaRegistry; - beforeEach(inject( - [ElementSchemaRegistry], (_registry: ElementSchemaRegistry) => { registry = _registry; })); + beforeEach(inject([ElementSchemaRegistry], (_registry: ElementSchemaRegistry) => { + registry = _registry; + })); describe('possibleSecurityContexts', () => { function hrefSecurityContexts(selector: string) { return calcPossibleSecurityContexts(registry, selector, 'href', false); } - it('should return a single security context if the selector as an element name', - () => { expect(hrefSecurityContexts('a')).toEqual([SecurityContext.URL]); }); + it('should return a single security context if the selector as an element name', () => { + expect(hrefSecurityContexts('a')).toEqual([SecurityContext.URL]); + }); it('should return the possible security contexts if the selector has no element name', () => { expect(hrefSecurityContexts('[myDir]')).toEqual([ @@ -45,8 +47,9 @@ import {calcPossibleSecurityContexts} from '../../src/template_parser/binding_pa ]); }); - it('should return SecurityContext.NONE if there are no possible elements', - () => { expect(hrefSecurityContexts('img:not(img)')).toEqual([SecurityContext.NONE]); }); + it('should return SecurityContext.NONE if there are no possible elements', () => { + expect(hrefSecurityContexts('img:not(img)')).toEqual([SecurityContext.NONE]); + }); it('should return the union of the possible security contexts if multiple selectors are specified', () => { diff --git a/packages/compiler/test/template_parser/template_parser_absolute_span_spec.ts b/packages/compiler/test/template_parser/template_parser_absolute_span_spec.ts index ca2b1b3ad8a67..7547490e5b44f 100644 --- a/packages/compiler/test/template_parser/template_parser_absolute_span_spec.ts +++ b/packages/compiler/test/template_parser/template_parser_absolute_span_spec.ts @@ -36,7 +36,7 @@ describe('expression AST absolute source spans', () => { beforeEach(inject([TemplateParser], (parser: TemplateParser) => { parse = (template: string, directives: CompileDirectiveSummary[] = [], - pipes: CompilePipeSummary[] | null = null, schemas: SchemaMetadata[] = [], + pipes: CompilePipeSummary[]|null = null, schemas: SchemaMetadata[] = [], preserveWhitespaces = true): TemplateAst[] => { if (pipes === null) { pipes = []; diff --git a/packages/compiler/test/template_parser/template_parser_spec.ts b/packages/compiler/test/template_parser/template_parser_spec.ts index af03ee7ca72c8..2b5c8498e1283 100644 --- a/packages/compiler/test/template_parser/template_parser_spec.ts +++ b/packages/compiler/test/template_parser/template_parser_spec.ts @@ -9,19 +9,20 @@ import {preserveWhitespacesDefault} from '@angular/compiler'; import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileTemplateMetadata, CompileTokenMetadata, tokenReference} from '@angular/compiler/src/compile_metadata'; import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry'; import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry'; -import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAstType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '@angular/compiler/src/template_parser/template_ast'; -import {TemplateParser, splitClasses} from '@angular/compiler/src/template_parser/template_parser'; +import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAstType, ReferenceAst, TemplateAst, TemplateAstVisitor, templateVisitAll, TextAst, VariableAst} from '@angular/compiler/src/template_parser/template_ast'; +import {splitClasses, TemplateParser} from '@angular/compiler/src/template_parser/template_parser'; import {SchemaMetadata, SecurityContext} from '@angular/core'; import {Console} from '@angular/core/src/console'; -import {TestBed, inject} from '@angular/core/testing'; +import {inject, TestBed} from '@angular/core/testing'; import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector'; -import {Identifiers, createTokenForExternalReference, createTokenForReference} from '../../src/identifiers'; +import {createTokenForExternalReference, createTokenForReference, Identifiers} from '../../src/identifiers'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config'; import {newArray} from '../../src/util'; import {MockSchemaRegistry} from '../../testing'; import {unparse} from '../expression_parser/utils/unparser'; import {TEST_COMPILER_PROVIDERS} from '../test_bindings'; + import {compileDirectiveMetadataCreate, compileTemplateMetadata, createTypeMeta} from './util/metadata'; const someModuleUrl = 'package:someModule'; @@ -139,7 +140,7 @@ class TemplateHumanizer implements TemplateAstVisitor { private _appendSourceSpan(ast: TemplateAst, input: any[]): any[] { if (!this.includeSourceSpan) return input; - input.push(ast.sourceSpan !.toString()); + input.push(ast.sourceSpan!.toString()); return input; } } @@ -166,11 +167,21 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor { templateVisitAll(this, ast.children); return null; } - visitReference(ast: ReferenceAst, context: any): any { return null; } - visitVariable(ast: VariableAst, context: any): any { return null; } - visitEvent(ast: BoundEventAst, context: any): any { return null; } - visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; } - visitAttr(ast: AttrAst, context: any): any { return null; } + visitReference(ast: ReferenceAst, context: any): any { + return null; + } + visitVariable(ast: VariableAst, context: any): any { + return null; + } + visitEvent(ast: BoundEventAst, context: any): any { + return null; + } + visitElementProperty(ast: BoundElementPropertyAst, context: any): any { + return null; + } + visitAttr(ast: AttrAst, context: any): any { + return null; + } visitBoundText(ast: BoundTextAst, context: any): any { this.result.push([`#text(${unparse(ast.value)})`, ast.ngContentIndex]); return null; @@ -179,22 +190,48 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor { this.result.push([`#text(${ast.value})`, ast.ngContentIndex]); return null; } - visitDirective(ast: DirectiveAst, context: any): any { return null; } - visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; } + visitDirective(ast: DirectiveAst, context: any): any { + return null; + } + visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { + return null; + } } class ThrowingVisitor implements TemplateAstVisitor { - visitNgContent(ast: NgContentAst, context: any): any { throw 'not implemented'; } - visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { throw 'not implemented'; } - visitElement(ast: ElementAst, context: any): any { throw 'not implemented'; } - visitReference(ast: ReferenceAst, context: any): any { throw 'not implemented'; } - visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; } - visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; } - visitElementProperty(ast: BoundElementPropertyAst, context: any): any { throw 'not implemented'; } - visitAttr(ast: AttrAst, context: any): any { throw 'not implemented'; } - visitBoundText(ast: BoundTextAst, context: any): any { throw 'not implemented'; } - visitText(ast: TextAst, context: any): any { throw 'not implemented'; } - visitDirective(ast: DirectiveAst, context: any): any { throw 'not implemented'; } + visitNgContent(ast: NgContentAst, context: any): any { + throw 'not implemented'; + } + visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { + throw 'not implemented'; + } + visitElement(ast: ElementAst, context: any): any { + throw 'not implemented'; + } + visitReference(ast: ReferenceAst, context: any): any { + throw 'not implemented'; + } + visitVariable(ast: VariableAst, context: any): any { + throw 'not implemented'; + } + visitEvent(ast: BoundEventAst, context: any): any { + throw 'not implemented'; + } + visitElementProperty(ast: BoundElementPropertyAst, context: any): any { + throw 'not implemented'; + } + visitAttr(ast: AttrAst, context: any): any { + throw 'not implemented'; + } + visitBoundText(ast: BoundTextAst, context: any): any { + throw 'not implemented'; + } + visitText(ast: TextAst, context: any): any { + throw 'not implemented'; + } + visitDirective(ast: DirectiveAst, context: any): any { + throw 'not implemented'; + } visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { throw 'not implemented'; } @@ -236,2014 +273,2012 @@ class NullVisitor implements TemplateAstVisitor { class ArrayConsole implements Console { logs: string[] = []; warnings: string[] = []; - log(msg: string) { this.logs.push(msg); } - warn(msg: string) { this.warnings.push(msg); } + log(msg: string) { + this.logs.push(msg); + } + warn(msg: string) { + this.warnings.push(msg); + } } (function() { - let ngIf: CompileDirectiveSummary; - let parse: ( - template: string, directives: CompileDirectiveSummary[], pipes?: CompilePipeSummary[], - schemas?: SchemaMetadata[], preserveWhitespaces?: boolean) => TemplateAst[]; - let console: ArrayConsole; - - function configureCompiler() { - console = new ArrayConsole(); - beforeEach(() => { - TestBed.configureCompiler({ - providers: [ - {provide: Console, useValue: console}, - ], - }); +let ngIf: CompileDirectiveSummary; +let parse: ( + template: string, directives: CompileDirectiveSummary[], pipes?: CompilePipeSummary[], + schemas?: SchemaMetadata[], preserveWhitespaces?: boolean) => TemplateAst[]; +let console: ArrayConsole; + +function configureCompiler() { + console = new ArrayConsole(); + beforeEach(() => { + TestBed.configureCompiler({ + providers: [ + {provide: Console, useValue: console}, + ], }); - } + }); +} - function commonBeforeEach() { - beforeEach(inject([TemplateParser], (parser: TemplateParser) => { - const someAnimation = ['someAnimation']; - const someTemplate = compileTemplateMetadata({animations: [someAnimation]}); - const component = compileDirectiveMetadataCreate({ - isHost: false, - selector: 'root', - template: someTemplate, - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Root'}}), - isComponent: true - }); - ngIf = compileDirectiveMetadataCreate({ - selector: '[ngIf]', - template: someTemplate, - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'NgIf'}}), - inputs: ['ngIf'] - }).toSummary(); - - parse = - (template: string, directives: CompileDirectiveSummary[], - pipes: CompilePipeSummary[] | null = null, schemas: SchemaMetadata[] = [], - preserveWhitespaces = true): TemplateAst[] => { - if (pipes === null) { - pipes = []; - } - return parser - .parse( - component, template, directives, pipes, schemas, 'TestComp', - preserveWhitespaces) - .template; - }; - })); - } +function commonBeforeEach() { + beforeEach(inject([TemplateParser], (parser: TemplateParser) => { + const someAnimation = ['someAnimation']; + const someTemplate = compileTemplateMetadata({animations: [someAnimation]}); + const component = compileDirectiveMetadataCreate({ + isHost: false, + selector: 'root', + template: someTemplate, + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Root'}}), + isComponent: true + }); + ngIf = compileDirectiveMetadataCreate({ + selector: '[ngIf]', + template: someTemplate, + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'NgIf'}}), + inputs: ['ngIf'] + }).toSummary(); + + parse = + (template: string, directives: CompileDirectiveSummary[], + pipes: CompilePipeSummary[]|null = null, schemas: SchemaMetadata[] = [], + preserveWhitespaces = true): TemplateAst[] => { + if (pipes === null) { + pipes = []; + } + return parser + .parse( + component, template, directives, pipes, schemas, 'TestComp', preserveWhitespaces) + .template; + }; + })); +} - describe('TemplateAstVisitor', () => { - function expectVisitedNode(visitor: TemplateAstVisitor, node: TemplateAst) { - expect(node.visit(visitor, null)).toEqual(node); - } +describe('TemplateAstVisitor', () => { + function expectVisitedNode(visitor: TemplateAstVisitor, node: TemplateAst) { + expect(node.visit(visitor, null)).toEqual(node); + } - it('should visit NgContentAst', () => { - expectVisitedNode( - new class extends - NullVisitor{visitNgContent(ast: NgContentAst, context: any): any{return ast;}}, - new NgContentAst(0, 0, null !)); - }); + it('should visit NgContentAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitNgContent(ast: NgContentAst, context: any): any { + return ast; + } + }, new NgContentAst(0, 0, null!)); + }); - it('should visit EmbeddedTemplateAst', () => { - expectVisitedNode( - new class extends NullVisitor{ - visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any) { return ast; } - }, - new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null !)); - }); + it('should visit EmbeddedTemplateAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any) { + return ast; + } + }, new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null!)); + }); - it('should visit ElementAst', () => { - expectVisitedNode( - new class extends - NullVisitor{visitElement(ast: ElementAst, context: any) { return ast; }}, - new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null !, null !)); - }); + it('should visit ElementAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitElement(ast: ElementAst, context: any) { + return ast; + } + }, new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null!, null!)); + }); - it('should visit RefererenceAst', () => { - expectVisitedNode( - new class extends - NullVisitor{visitReference(ast: ReferenceAst, context: any): any{return ast;}}, - new ReferenceAst('foo', null !, null !, null !)); - }); + it('should visit RefererenceAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitReference(ast: ReferenceAst, context: any): any { + return ast; + } + }, new ReferenceAst('foo', null!, null!, null!)); + }); - it('should visit VariableAst', () => { - expectVisitedNode( - new class extends - NullVisitor{visitVariable(ast: VariableAst, context: any): any{return ast;}}, - new VariableAst('foo', 'bar', null !)); - }); + it('should visit VariableAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitVariable(ast: VariableAst, context: any): any { + return ast; + } + }, new VariableAst('foo', 'bar', null!)); + }); - it('should visit BoundEventAst', () => { - expectVisitedNode( - new class extends - NullVisitor{visitEvent(ast: BoundEventAst, context: any): any{return ast;}}, - new BoundEventAst('foo', 'bar', 'goo', null !, null !, null !)); - }); + it('should visit BoundEventAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitEvent(ast: BoundEventAst, context: any): any { + return ast; + } + }, new BoundEventAst('foo', 'bar', 'goo', null!, null!, null!)); + }); - it('should visit BoundElementPropertyAst', () => { - expectVisitedNode( - new class extends NullVisitor{ - visitElementProperty(ast: BoundElementPropertyAst, context: any): any{return ast;} - }, - new BoundElementPropertyAst('foo', null !, null !, null !, 'bar', null !)); - }); + it('should visit BoundElementPropertyAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitElementProperty(ast: BoundElementPropertyAst, context: any): any { + return ast; + } + }, new BoundElementPropertyAst('foo', null!, null!, null!, 'bar', null!)); + }); - it('should visit AttrAst', () => { - expectVisitedNode( - new class extends NullVisitor{visitAttr(ast: AttrAst, context: any): any{return ast;}}, - new AttrAst('foo', 'bar', null !)); - }); + it('should visit AttrAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitAttr(ast: AttrAst, context: any): any { + return ast; + } + }, new AttrAst('foo', 'bar', null!)); + }); - it('should visit BoundTextAst', () => { - expectVisitedNode( - new class extends - NullVisitor{visitBoundText(ast: BoundTextAst, context: any): any{return ast;}}, - new BoundTextAst(null !, 0, null !)); - }); + it('should visit BoundTextAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitBoundText(ast: BoundTextAst, context: any): any { + return ast; + } + }, new BoundTextAst(null!, 0, null!)); + }); - it('should visit TextAst', () => { - expectVisitedNode( - new class extends NullVisitor{visitText(ast: TextAst, context: any): any{return ast;}}, - new TextAst('foo', 0, null !)); - }); + it('should visit TextAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitText(ast: TextAst, context: any): any { + return ast; + } + }, new TextAst('foo', 0, null!)); + }); - it('should visit DirectiveAst', () => { - expectVisitedNode( - new class extends - NullVisitor{visitDirective(ast: DirectiveAst, context: any): any{return ast;}}, - new DirectiveAst(null !, [], [], [], 0, null !)); - }); + it('should visit DirectiveAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitDirective(ast: DirectiveAst, context: any): any { + return ast; + } + }, new DirectiveAst(null!, [], [], [], 0, null!)); + }); - it('should visit DirectiveAst', () => { - expectVisitedNode( - new class extends NullVisitor{ - visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any{return ast;} - }, - new BoundDirectivePropertyAst('foo', 'bar', null !, null !)); - }); + it('should visit DirectiveAst', () => { + expectVisitedNode(new class extends NullVisitor { + visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { + return ast; + } + }, new BoundDirectivePropertyAst('foo', 'bar', null!, null!)); + }); - it('should skip the typed call of a visitor if visit() returns a truthy value', () => { - const visitor = new class extends ThrowingVisitor { - visit(ast: TemplateAst, context: any): any { return true; } - }; - const nodes: TemplateAst[] = [ - new NgContentAst(0, 0, null !), - new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null !), - new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null !, null !), - new ReferenceAst('foo', null !, 'bar', null !), new VariableAst('foo', 'bar', null !), - new BoundEventAst('foo', 'bar', 'goo', null !, null !, null !), - new BoundElementPropertyAst('foo', null !, null !, null !, 'bar', null !), - new AttrAst('foo', 'bar', null !), new BoundTextAst(null !, 0, null !), - new TextAst('foo', 0, null !), new DirectiveAst(null !, [], [], [], 0, null !), - new BoundDirectivePropertyAst('foo', 'bar', null !, null !) - ]; - const result = templateVisitAll(visitor, nodes, null); - expect(result).toEqual(newArray(nodes.length).fill(true)); + it('should skip the typed call of a visitor if visit() returns a truthy value', () => { + const visitor = new class extends ThrowingVisitor { + visit(ast: TemplateAst, context: any): any { + return true; + } + }; + const nodes: TemplateAst[] = [ + new NgContentAst(0, 0, null!), + new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null!), + new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null!, null!), + new ReferenceAst('foo', null!, 'bar', null!), new VariableAst('foo', 'bar', null!), + new BoundEventAst('foo', 'bar', 'goo', null!, null!, null!), + new BoundElementPropertyAst('foo', null!, null!, null!, 'bar', null!), + new AttrAst('foo', 'bar', null!), new BoundTextAst(null!, 0, null!), + new TextAst('foo', 0, null!), new DirectiveAst(null!, [], [], [], 0, null!), + new BoundDirectivePropertyAst('foo', 'bar', null!, null!) + ]; + const result = templateVisitAll(visitor, nodes, null); + expect(result).toEqual(newArray(nodes.length).fill(true)); + }); +}); + +describe('TemplateParser Security', () => { + // Semi-integration test to make sure TemplateParser properly sets the security context. + // Uses the actual DomElementSchemaRegistry. + beforeEach(() => { + TestBed.configureCompiler({ + providers: [ + TEST_COMPILER_PROVIDERS, + {provide: ElementSchemaRegistry, useClass: DomElementSchemaRegistry, deps: []} + ] }); }); - describe('TemplateParser Security', () => { - // Semi-integration test to make sure TemplateParser properly sets the security context. - // Uses the actual DomElementSchemaRegistry. - beforeEach(() => { - TestBed.configureCompiler({ - providers: [ - TEST_COMPILER_PROVIDERS, - {provide: ElementSchemaRegistry, useClass: DomElementSchemaRegistry, deps: []} - ] - }); + configureCompiler(); + commonBeforeEach(); + + describe('security context', () => { + function secContext(tpl: string): SecurityContext { + const ast = parse(tpl, []); + const propBinding = (<ElementAst>ast[0]).inputs[0]; + return propBinding.securityContext; + } + + it('should set for properties', () => { + expect(secContext('<div [title]="v">')).toBe(SecurityContext.NONE); + expect(secContext('<div [innerHTML]="v">')).toBe(SecurityContext.HTML); + }); + it('should set for property value bindings', () => { + expect(secContext('<div innerHTML="{{v}}">')).toBe(SecurityContext.HTML); + }); + it('should set for attributes', () => { + expect(secContext('<a [attr.href]="v">')).toBe(SecurityContext.URL); + // NB: attributes below need to change case. + expect(secContext('<a [attr.innerHtml]="v">')).toBe(SecurityContext.HTML); + expect(secContext('<a [attr.formaction]="v">')).toBe(SecurityContext.URL); + }); + it('should set for style', () => { + expect(secContext('<a [style.backgroundColor]="v">')).toBe(SecurityContext.STYLE); }); + }); +}); - configureCompiler(); - commonBeforeEach(); +describe('TemplateParser', () => { + beforeEach(() => { + TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); + }); - describe('security context', () => { - function secContext(tpl: string): SecurityContext { - const ast = parse(tpl, []); - const propBinding = (<ElementAst>ast[0]).inputs[0]; - return propBinding.securityContext; - } + configureCompiler(); + commonBeforeEach(); - it('should set for properties', () => { - expect(secContext('<div [title]="v">')).toBe(SecurityContext.NONE); - expect(secContext('<div [innerHTML]="v">')).toBe(SecurityContext.HTML); - }); - it('should set for property value bindings', - () => { expect(secContext('<div innerHTML="{{v}}">')).toBe(SecurityContext.HTML); }); - it('should set for attributes', () => { - expect(secContext('<a [attr.href]="v">')).toBe(SecurityContext.URL); - // NB: attributes below need to change case. - expect(secContext('<a [attr.innerHtml]="v">')).toBe(SecurityContext.HTML); - expect(secContext('<a [attr.formaction]="v">')).toBe(SecurityContext.URL); + describe('parse', () => { + describe('nodes without bindings', () => { + it('should parse text nodes', () => { + expect(humanizeTplAst(parse('a', []))).toEqual([[TextAst, 'a']]); }); - it('should set for style', () => { - expect(secContext('<a [style.backgroundColor]="v">')).toBe(SecurityContext.STYLE); + + it('should parse elements with attributes', () => { + expect(humanizeTplAst(parse('<div a=b>', []))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'a', 'b'] + ]); }); }); - }); - describe('TemplateParser', () => { - beforeEach(() => { - TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); + it('should parse ngContent', () => { + const parsed = parse('<ng-content select="a"></ng-content>', []); + expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]); }); - configureCompiler(); - commonBeforeEach(); + it('should parse ngContent when it contains WS only', () => { + const parsed = parse('<ng-content select="a"> \n </ng-content>', []); + expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]); + }); - describe('parse', () => { - describe('nodes without bindings', () => { + it('should parse ngContent regardless the namespace', () => { + const parsed = parse('<svg><ng-content></ng-content></svg>', []); + expect(humanizeTplAst(parsed)).toEqual([ + [ElementAst, ':svg:svg'], + [NgContentAst], + ]); + }); - it('should parse text nodes', () => { - expect(humanizeTplAst(parse('a', []))).toEqual([[TextAst, 'a']]); - }); + it('should parse bound text nodes', () => { + expect(humanizeTplAst(parse('{{a}}', []))).toEqual([[BoundTextAst, '{{ a }}']]); + }); - it('should parse elements with attributes', () => { - expect(humanizeTplAst(parse('<div a=b>', [ - ]))).toEqual([[ElementAst, 'div'], [AttrAst, 'a', 'b']]); - }); - }); + it('should parse with custom interpolation config', + inject([TemplateParser], (parser: TemplateParser) => { + const component = CompileDirectiveMetadata.create({ + selector: 'test', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Test'}}), + isComponent: true, + template: new CompileTemplateMetadata({ + interpolation: ['{%', '%}'], + isInline: false, + animations: [], + template: null, + templateUrl: null, + htmlAst: null, + ngContentSelectors: [], + externalStylesheets: [], + styleUrls: [], + styles: [], + encapsulation: null, + preserveWhitespaces: preserveWhitespacesDefault(null), + }), + isHost: false, + exportAs: null, + changeDetection: null, + inputs: [], + outputs: [], + host: {}, + providers: [], + viewProviders: [], + queries: [], + guards: {}, + viewQueries: [], + entryComponents: [], + componentViewType: null, + rendererType: null, + componentFactory: null - it('should parse ngContent', () => { - const parsed = parse('<ng-content select="a"></ng-content>', []); - expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]); + }); + expect(humanizeTplAst( + parser.parse(component, '{%a%}', [], [], [], 'TestComp', true).template, + {start: '{%', end: '%}'})) + .toEqual([[BoundTextAst, '{% a %}']]); + })); + + describe('bound properties', () => { + it('should parse mixed case bound properties', () => { + expect(humanizeTplAst(parse('<div [someProp]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'someProp', 'v', null] + ]); }); - it('should parse ngContent when it contains WS only', () => { - const parsed = parse('<ng-content select="a"> \n </ng-content>', []); - expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]); + it('should parse dash case bound properties', () => { + expect(humanizeTplAst(parse('<div [some-prop]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'some-prop', 'v', null] + ]); }); - it('should parse ngContent regardless the namespace', () => { - const parsed = parse('<svg><ng-content></ng-content></svg>', []); - expect(humanizeTplAst(parsed)).toEqual([ - [ElementAst, ':svg:svg'], - [NgContentAst], + it('should parse dotted name bound properties', () => { + expect(humanizeTplAst(parse('<div [dot.name]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'dot.name', 'v', null] ]); }); - it('should parse bound text nodes', () => { - expect(humanizeTplAst(parse('{{a}}', []))).toEqual([[BoundTextAst, '{{ a }}']]); - }); - - it('should parse with custom interpolation config', - inject([TemplateParser], (parser: TemplateParser) => { - const component = CompileDirectiveMetadata.create({ - selector: 'test', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Test'}}), - isComponent: true, - template: new CompileTemplateMetadata({ - interpolation: ['{%', '%}'], - isInline: false, - animations: [], - template: null, - templateUrl: null, - htmlAst: null, - ngContentSelectors: [], - externalStylesheets: [], - styleUrls: [], - styles: [], - encapsulation: null, - preserveWhitespaces: preserveWhitespacesDefault(null), - }), - isHost: false, - exportAs: null, - changeDetection: null, - inputs: [], - outputs: [], - host: {}, - providers: [], - viewProviders: [], - queries: [], - guards: {}, - viewQueries: [], - entryComponents: [], - componentViewType: null, - rendererType: null, - componentFactory: null - - }); - expect(humanizeTplAst( - parser.parse(component, '{%a%}', [], [], [], 'TestComp', true).template, - {start: '{%', end: '%}'})) - .toEqual([[BoundTextAst, '{% a %}']]); - })); - - describe('bound properties', () => { - - it('should parse mixed case bound properties', () => { - expect(humanizeTplAst(parse('<div [someProp]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'someProp', 'v', null] - ]); - }); - - it('should parse dash case bound properties', () => { - expect(humanizeTplAst(parse('<div [some-prop]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'some-prop', 'v', null] - ]); - }); - - it('should parse dotted name bound properties', () => { - expect(humanizeTplAst(parse('<div [dot.name]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'dot.name', 'v', null] - ]); - }); - - it('should normalize property names via the element schema', () => { - expect(humanizeTplAst(parse('<div [mappedAttr]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'mappedProp', 'v', null] - ]); - }); + it('should normalize property names via the element schema', () => { + expect(humanizeTplAst(parse('<div [mappedAttr]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'mappedProp', 'v', null] + ]); + }); - it('should parse mixed case bound attributes', () => { - expect(humanizeTplAst(parse('<div [attr.someAttr]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Attribute, 'someAttr', 'v', null] - ]); - }); + it('should parse mixed case bound attributes', () => { + expect(humanizeTplAst(parse('<div [attr.someAttr]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Attribute, 'someAttr', 'v', null] + ]); + }); - it('should parse mixed case bound attributes with dot in the attribute name', () => { - expect(humanizeTplAst(parse('<div [attr.someAttr.someAttrSuffix]="v">', []))).toEqual([ - [ElementAst, 'div'], - [ - BoundElementPropertyAst, PropertyBindingType.Attribute, 'someAttr.someAttrSuffix', - 'v', null - ] - ]); - }); + it('should parse mixed case bound attributes with dot in the attribute name', () => { + expect(humanizeTplAst(parse('<div [attr.someAttr.someAttrSuffix]="v">', []))).toEqual([ + [ElementAst, 'div'], + [ + BoundElementPropertyAst, PropertyBindingType.Attribute, 'someAttr.someAttrSuffix', 'v', + null + ] + ]); + }); - it('should parse and dash case bound classes', () => { - expect(humanizeTplAst(parse('<div [class.some-class]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Class, 'some-class', 'v', null] - ]); - }); + it('should parse and dash case bound classes', () => { + expect(humanizeTplAst(parse('<div [class.some-class]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Class, 'some-class', 'v', null] + ]); + }); - it('should parse mixed case bound classes', () => { - expect(humanizeTplAst(parse('<div [class.someClass]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Class, 'someClass', 'v', null] - ]); - }); + it('should parse mixed case bound classes', () => { + expect(humanizeTplAst(parse('<div [class.someClass]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Class, 'someClass', 'v', null] + ]); + }); - it('should parse mixed case bound styles', () => { - expect(humanizeTplAst(parse('<div [style.someStyle]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Style, 'someStyle', 'v', null] - ]); - }); + it('should parse mixed case bound styles', () => { + expect(humanizeTplAst(parse('<div [style.someStyle]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Style, 'someStyle', 'v', null] + ]); + }); - describe('errors', () => { - it('should throw error when binding to an unknown property', () => { - expect(() => parse('<my-component [invalidProp]="bar"></my-component>', [])) - .toThrowError(`Template parse errors: + describe('errors', () => { + it('should throw error when binding to an unknown property', () => { + expect(() => parse('<my-component [invalidProp]="bar"></my-component>', [])) + .toThrowError(`Template parse errors: Can't bind to 'invalidProp' since it isn't a known property of 'my-component'. 1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module. 2. If 'my-component' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. 3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. ("<my-component [ERROR ->][invalidProp]="bar"></my-component>"): TestComp@0:14`); - }); + }); - it('should throw error when binding to an unknown property of ng-container', () => { - expect(() => parse('<ng-container [invalidProp]="bar"></ng-container>', [])) - .toThrowError( - `Template parse errors: + it('should throw error when binding to an unknown property of ng-container', () => { + expect(() => parse('<ng-container [invalidProp]="bar"></ng-container>', [])) + .toThrowError( + `Template parse errors: Can't bind to 'invalidProp' since it isn't a known property of 'ng-container'. 1. If 'invalidProp' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component. 2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.` + - ` ("<ng-container [ERROR ->][invalidProp]="bar"></ng-container>"): TestComp@0:14`); - }); + ` ("<ng-container [ERROR ->][invalidProp]="bar"></ng-container>"): TestComp@0:14`); + }); - it('should throw error when binding to an unknown element w/o bindings', () => { - expect(() => parse('<unknown></unknown>', [])).toThrowError(`Template parse errors: + it('should throw error when binding to an unknown element w/o bindings', () => { + expect(() => parse('<unknown></unknown>', [])).toThrowError(`Template parse errors: 'unknown' is not a known element: 1. If 'unknown' is an Angular component, then verify that it is part of this module. 2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. ("[ERROR ->]<unknown></unknown>"): TestComp@0:0`); - }); + }); - it('should throw error when binding to an unknown custom element w/o bindings', () => { - expect(() => parse('<un-known></un-known>', [])).toThrowError(`Template parse errors: + it('should throw error when binding to an unknown custom element w/o bindings', () => { + expect(() => parse('<un-known></un-known>', [])).toThrowError(`Template parse errors: 'un-known' is not a known element: 1. If 'un-known' is an Angular component, then verify that it is part of this module. 2. If 'un-known' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<un-known></un-known>"): TestComp@0:0`); - }); + }); - it('should throw error when binding to an invalid property', () => { - expect(() => parse('<my-component [onEvent]="bar"></my-component>', [])) - .toThrowError(`Template parse errors: + it('should throw error when binding to an invalid property', () => { + expect(() => parse('<my-component [onEvent]="bar"></my-component>', [])) + .toThrowError(`Template parse errors: Binding to property 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][onEvent]="bar"></my-component>"): TestComp@0:14`); - }); + }); - it('should throw error when binding to an invalid attribute', () => { - expect(() => parse('<my-component [attr.onEvent]="bar"></my-component>', [])) - .toThrowError(`Template parse errors: + it('should throw error when binding to an invalid attribute', () => { + expect(() => parse('<my-component [attr.onEvent]="bar"></my-component>', [])) + .toThrowError(`Template parse errors: Binding to attribute 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][attr.onEvent]="bar"></my-component>"): TestComp@0:14`); - }); }); + }); - it('should parse bound properties via [...] and not report them as attributes', () => { - expect(humanizeTplAst(parse('<div [prop]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null] - ]); - }); + it('should parse bound properties via [...] and not report them as attributes', () => { + expect(humanizeTplAst(parse('<div [prop]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null] + ]); + }); - it('should parse bound properties via bind- and not report them as attributes', () => { - expect(humanizeTplAst(parse('<div bind-prop="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null] - ]); - }); + it('should parse bound properties via bind- and not report them as attributes', () => { + expect(humanizeTplAst(parse('<div bind-prop="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null] + ]); + }); - it('should report missing property names in bind- syntax', () => { - expect(() => parse('<div bind-></div>', [])).toThrowError(`Template parse errors: + it('should report missing property names in bind- syntax', () => { + expect(() => parse('<div bind-></div>', [])).toThrowError(`Template parse errors: Property name is missing in binding ("<div [ERROR ->]bind-></div>"): TestComp@0:5`); - }); + }); - it('should parse bound properties via {{...}} and not report them as attributes', () => { - expect(humanizeTplAst(parse('<div prop="{{v}}">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', '{{ v }}', null] - ]); - }); + it('should parse bound properties via {{...}} and not report them as attributes', () => { + expect(humanizeTplAst(parse('<div prop="{{v}}">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', '{{ v }}', null] + ]); + }); - it('should parse bound properties via bind-animate- and not report them as attributes', - () => { - expect(humanizeTplAst(parse('<div bind-animate-someAnimation="value2">', [], [], []))) - .toEqual([ - [ElementAst, 'div'], - [ - BoundElementPropertyAst, PropertyBindingType.Animation, 'someAnimation', - 'value2', null - ] - ]); - }); - - it('should throw an error when parsing detects non-bound properties via @ that contain a value', - () => { - expect(() => { parse('<div @someAnimation="value2">', [], [], []); }) - .toThrowError( - /Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("<div \[ERROR ->\]@someAnimation="value2">"\): TestComp@0:5/); - }); - - it('should report missing animation trigger in @ syntax', () => { - expect(() => parse('<div @></div>', [])).toThrowError(`Template parse errors: -Animation trigger is missing ("<div [ERROR ->]@></div>"): TestComp@0:5`); - }); + it('should parse bound properties via bind-animate- and not report them as attributes', + () => { + expect(humanizeTplAst(parse('<div bind-animate-someAnimation="value2">', [], [], []))) + .toEqual([ + [ElementAst, 'div'], + [ + BoundElementPropertyAst, PropertyBindingType.Animation, 'someAnimation', + 'value2', null + ] + ]); + }); - it('should not issue a warning when host attributes contain a valid property-bound animation trigger', - () => { - const animationEntries = ['prop']; - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - template: compileTemplateMetadata({animations: animationEntries}), - type: createTypeMeta({ - reference: {filePath: someModuleUrl, name: 'DirA'}, - }), - host: {'[@prop]': 'expr'} - }).toSummary(); - - humanizeTplAst(parse('<div></div>', [dirA])); - expect(console.warnings.length).toEqual(0); - }); - - it('should throw descriptive error when a host binding is not a string expression', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'broken', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - host: {'[class.foo]': null !} - }).toSummary(); + it('should throw an error when parsing detects non-bound properties via @ that contain a value', + () => { + expect(() => { + parse('<div @someAnimation="value2">', [], [], []); + }) + .toThrowError( + /Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("<div \[ERROR ->\]@someAnimation="value2">"\): TestComp@0:5/); + }); - expect(() => { parse('<broken></broken>', [dirA]); }) - .toThrowError( - `Template parse errors:\nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`); - }); + it('should report missing animation trigger in @ syntax', () => { + expect(() => parse('<div @></div>', [])).toThrowError(`Template parse errors: +Animation trigger is missing ("<div [ERROR ->]@></div>"): TestComp@0:5`); + }); - it('should throw descriptive error when a host event is not a string expression', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'broken', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - host: {'(click)': null !} - }).toSummary(); + it('should not issue a warning when host attributes contain a valid property-bound animation trigger', + () => { + const animationEntries = ['prop']; + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + template: compileTemplateMetadata({animations: animationEntries}), + type: createTypeMeta({ + reference: {filePath: someModuleUrl, name: 'DirA'}, + }), + host: {'[@prop]': 'expr'} + }).toSummary(); + + humanizeTplAst(parse('<div></div>', [dirA])); + expect(console.warnings.length).toEqual(0); + }); - expect(() => { parse('<broken></broken>', [dirA]); }) - .toThrowError( - `Template parse errors:\nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`); - }); + it('should throw descriptive error when a host binding is not a string expression', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'broken', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + host: {'[class.foo]': null!} + }).toSummary(); - it('should not issue a warning when an animation property is bound without an expression', - () => { - humanizeTplAst(parse('<div @someAnimation>', [], [], [])); - expect(console.warnings.length).toEqual(0); - }); - - it('should parse bound properties via [@] and not report them as attributes', () => { - expect(humanizeTplAst(parse('<div [@someAnimation]="value2">', [], [], []))).toEqual([ - [ElementAst, 'div'], - [ - BoundElementPropertyAst, PropertyBindingType.Animation, 'someAnimation', 'value2', - null - ] - ]); - }); + expect(() => { + parse('<broken></broken>', [dirA]); + }) + .toThrowError( + `Template parse errors:\nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`); + }); - it('should support * directives', () => { - expect(humanizeTplAst(parse('<div *ngIf>', [ngIf]))).toEqual([ - [EmbeddedTemplateAst], - [DirectiveAst, ngIf], - [BoundDirectivePropertyAst, 'ngIf', 'null'], - [ElementAst, 'div'], - ]); - }); + it('should throw descriptive error when a host event is not a string expression', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'broken', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + host: {'(click)': null!} + }).toSummary(); - it('should support <ng-template>', () => { - expect(humanizeTplAst(parse('<ng-template>', []))).toEqual([ - [EmbeddedTemplateAst], - ]); - }); + expect(() => { + parse('<broken></broken>', [dirA]); + }) + .toThrowError( + `Template parse errors:\nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`); + }); - it('should treat <template> as a regular tag', () => { - expect(humanizeTplAst(parse('<template>', []))).toEqual([ - [ElementAst, 'template'], - ]); - }); + it('should not issue a warning when an animation property is bound without an expression', + () => { + humanizeTplAst(parse('<div @someAnimation>', [], [], [])); + expect(console.warnings.length).toEqual(0); + }); - it('should not special case the template attribute', () => { - expect(humanizeTplAst(parse('<p template="ngFor">', []))).toEqual([ - [ElementAst, 'p'], - [AttrAst, 'template', 'ngFor'], - ]); - }); + it('should parse bound properties via [@] and not report them as attributes', () => { + expect(humanizeTplAst(parse('<div [@someAnimation]="value2">', [], [], []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Animation, 'someAnimation', 'value2', null] + ]); + }); + it('should support * directives', () => { + expect(humanizeTplAst(parse('<div *ngIf>', [ngIf]))).toEqual([ + [EmbeddedTemplateAst], + [DirectiveAst, ngIf], + [BoundDirectivePropertyAst, 'ngIf', 'null'], + [ElementAst, 'div'], + ]); }); - describe('events', () => { + it('should support <ng-template>', () => { + expect(humanizeTplAst(parse('<ng-template>', []))).toEqual([ + [EmbeddedTemplateAst], + ]); + }); - it('should parse bound events with a target', () => { - expect(humanizeTplAst(parse('<div (window:event)="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundEventAst, 'event', 'window', 'v'], - ]); - }); + it('should treat <template> as a regular tag', () => { + expect(humanizeTplAst(parse('<template>', []))).toEqual([ + [ElementAst, 'template'], + ]); + }); - it('should report an error on empty expression', () => { - expect(() => parse('<div (event)="">', [])) - .toThrowError(/Empty expressions are not allowed/); + it('should not special case the template attribute', () => { + expect(humanizeTplAst(parse('<p template="ngFor">', []))).toEqual([ + [ElementAst, 'p'], + [AttrAst, 'template', 'ngFor'], + ]); + }); + }); - expect(() => parse('<div (event)=" ">', [])) - .toThrowError(/Empty expressions are not allowed/); - }); + describe('events', () => { + it('should parse bound events with a target', () => { + expect(humanizeTplAst(parse('<div (window:event)="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundEventAst, 'event', 'window', 'v'], + ]); + }); - it('should parse bound events via (...) and not report them as attributes', () => { - expect(humanizeTplAst(parse('<div (event)="v">', [ - ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'event', null, 'v']]); - }); + it('should report an error on empty expression', () => { + expect(() => parse('<div (event)="">', [])) + .toThrowError(/Empty expressions are not allowed/); - it('should parse event names case sensitive', () => { - expect(humanizeTplAst(parse('<div (some-event)="v">', [ - ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'some-event', null, 'v']]); - expect(humanizeTplAst(parse('<div (someEvent)="v">', [ - ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'someEvent', null, 'v']]); - }); + expect(() => parse('<div (event)=" ">', [])) + .toThrowError(/Empty expressions are not allowed/); + }); - it('should parse bound events via on- and not report them as attributes', () => { - expect(humanizeTplAst(parse('<div on-event="v">', [ - ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'event', null, 'v']]); - }); - - it('should report missing event names in on- syntax', () => { - expect(() => parse('<div on-></div>', [])) - .toThrowError(/Event name is missing in binding/); - }); + it('should parse bound events via (...) and not report them as attributes', () => { + expect(humanizeTplAst(parse('<div (event)="v">', []))).toEqual([ + [ElementAst, 'div'], [BoundEventAst, 'event', null, 'v'] + ]); + }); - it('should allow events on explicit embedded templates that are emitted by a directive', - () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: 'ng-template', - outputs: ['e'], - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) - }).toSummary(); - - expect(humanizeTplAst(parse('<ng-template (e)="f"></ng-template>', [dirA]))).toEqual([ - [EmbeddedTemplateAst], - [BoundEventAst, 'e', null, 'f'], - [DirectiveAst, dirA], - ]); - }); + it('should parse event names case sensitive', () => { + expect(humanizeTplAst(parse('<div (some-event)="v">', []))).toEqual([ + [ElementAst, 'div'], [BoundEventAst, 'some-event', null, 'v'] + ]); + expect(humanizeTplAst(parse('<div (someEvent)="v">', []))).toEqual([ + [ElementAst, 'div'], [BoundEventAst, 'someEvent', null, 'v'] + ]); }); - describe('bindon', () => { - it('should parse bound events and properties via [(...)] and not report them as attributes', - () => { - expect(humanizeTplAst(parse('<div [(prop)]="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null], - [BoundEventAst, 'propChange', null, 'v = $event'] - ]); - }); - - it('should parse bound events and properties via bindon- and not report them as attributes', - () => { - expect(humanizeTplAst(parse('<div bindon-prop="v">', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null], - [BoundEventAst, 'propChange', null, 'v = $event'] - ]); - }); + it('should parse bound events via on- and not report them as attributes', () => { + expect(humanizeTplAst(parse('<div on-event="v">', []))).toEqual([ + [ElementAst, 'div'], [BoundEventAst, 'event', null, 'v'] + ]); + }); - it('should report missing property names in bindon- syntax', () => { - expect(() => parse('<div bindon-></div>', [])) - .toThrowError(/Property name is missing in binding/); - }); + it('should report missing event names in on- syntax', () => { + expect(() => parse('<div on-></div>', [])).toThrowError(/Event name is missing in binding/); }); - describe('directives', () => { - it('should order directives by the directives array in the View and match them only once', - () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) - }).toSummary(); - const dirB = - compileDirectiveMetadataCreate({ - selector: '[b]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}) - }).toSummary(); - const dirC = - compileDirectiveMetadataCreate({ - selector: '[c]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirC'}}) - }).toSummary(); - expect(humanizeTplAst(parse('<div a c b a b>', [dirA, dirB, dirC]))).toEqual([ - [ElementAst, 'div'], [AttrAst, 'a', ''], [AttrAst, 'c', ''], [AttrAst, 'b', ''], - [AttrAst, 'a', ''], [AttrAst, 'b', ''], [DirectiveAst, dirA], [DirectiveAst, dirB], - [DirectiveAst, dirC] - ]); - }); + it('should allow events on explicit embedded templates that are emitted by a directive', + () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'ng-template', + outputs: ['e'], + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) + }).toSummary(); + + expect(humanizeTplAst(parse('<ng-template (e)="f"></ng-template>', [dirA]))).toEqual([ + [EmbeddedTemplateAst], + [BoundEventAst, 'e', null, 'f'], + [DirectiveAst, dirA], + ]); + }); + }); - it('should parse directive dotted properties', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: '[dot.name]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['localName: dot.name'], - }).toSummary(); + describe('bindon', () => { + it('should parse bound events and properties via [(...)] and not report them as attributes', + () => { + expect(humanizeTplAst(parse('<div [(prop)]="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null], + [BoundEventAst, 'propChange', null, 'v = $event'] + ]); + }); - expect(humanizeTplAst(parse('<div [dot.name]="expr"></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], - [DirectiveAst, dirA], - [BoundDirectivePropertyAst, 'localName', 'expr'], - ]); - }); + it('should parse bound events and properties via bindon- and not report them as attributes', + () => { + expect(humanizeTplAst(parse('<div bindon-prop="v">', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null], + [BoundEventAst, 'propChange', null, 'v = $event'] + ]); + }); - it('should locate directives in property bindings', () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a=b]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) - }).toSummary(); - const dirB = - compileDirectiveMetadataCreate({ - selector: '[b]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}) - }).toSummary(); - expect(humanizeTplAst(parse('<div [a]="b">', [dirA, dirB]))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'b', null], - [DirectiveAst, dirA] - ]); - }); + it('should report missing property names in bindon- syntax', () => { + expect(() => parse('<div bindon-></div>', [])) + .toThrowError(/Property name is missing in binding/); + }); + }); - it('should locate directives in inline templates', () => { - const dirTemplate = - compileDirectiveMetadataCreate({ - selector: 'ng-template', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'onTemplate'}}) - }).toSummary(); - expect(humanizeTplAst(parse('<div *ngIf="cond">', [ngIf, dirTemplate]))).toEqual([ - [EmbeddedTemplateAst], - [DirectiveAst, ngIf], - [BoundDirectivePropertyAst, 'ngIf', 'cond'], - [DirectiveAst, dirTemplate], - [ElementAst, 'div'], - ]); - }); + describe('directives', () => { + it('should order directives by the directives array in the View and match them only once', + () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) + }).toSummary(); + const dirB = compileDirectiveMetadataCreate({ + selector: '[b]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}) + }).toSummary(); + const dirC = compileDirectiveMetadataCreate({ + selector: '[c]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirC'}}) + }).toSummary(); + expect(humanizeTplAst(parse('<div a c b a b>', [dirA, dirB, dirC]))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'a', ''], [AttrAst, 'c', ''], [AttrAst, 'b', ''], + [AttrAst, 'a', ''], [AttrAst, 'b', ''], [DirectiveAst, dirA], [DirectiveAst, dirB], + [DirectiveAst, dirC] + ]); + }); - it('should locate directives in event bindings', () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}) - }).toSummary(); + it('should parse directive dotted properties', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[dot.name]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['localName: dot.name'], + }).toSummary(); - expect(humanizeTplAst(parse('<div (a)="b">', [dirA]))).toEqual([ - [ElementAst, 'div'], [BoundEventAst, 'a', null, 'b'], [DirectiveAst, dirA] - ]); - }); + expect(humanizeTplAst(parse('<div [dot.name]="expr"></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], + [DirectiveAst, dirA], + [BoundDirectivePropertyAst, 'localName', 'expr'], + ]); + }); - it('should parse directive host properties', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - host: {'[a]': 'expr'} - }).toSummary(); - expect(humanizeTplAst(parse('<div></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA], - [BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'expr', null] - ]); - }); + it('should locate directives in property bindings', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a=b]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) + }).toSummary(); + const dirB = compileDirectiveMetadataCreate({ + selector: '[b]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}) + }).toSummary(); + expect(humanizeTplAst(parse('<div [a]="b">', [dirA, dirB]))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'b', null], + [DirectiveAst, dirA] + ]); + }); - it('should parse directive host listeners', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - host: {'(a)': 'expr'} - }).toSummary(); - expect(humanizeTplAst(parse('<div></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA], [BoundEventAst, 'a', null, 'expr'] - ]); - }); + it('should locate directives in inline templates', () => { + const dirTemplate = + compileDirectiveMetadataCreate({ + selector: 'ng-template', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'onTemplate'}}) + }).toSummary(); + expect(humanizeTplAst(parse('<div *ngIf="cond">', [ngIf, dirTemplate]))).toEqual([ + [EmbeddedTemplateAst], + [DirectiveAst, ngIf], + [BoundDirectivePropertyAst, 'ngIf', 'cond'], + [DirectiveAst, dirTemplate], + [ElementAst, 'div'], + ]); + }); - it('should parse directive properties', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['aProp'] - }).toSummary(); - expect(humanizeTplAst(parse('<div [aProp]="expr"></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA], - [BoundDirectivePropertyAst, 'aProp', 'expr'] - ]); - }); + it('should locate directives in event bindings', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}) + }).toSummary(); - it('should parse renamed directive properties', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['b:a'] - }).toSummary(); - expect(humanizeTplAst(parse('<div [a]="expr"></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA], [BoundDirectivePropertyAst, 'b', 'expr'] - ]); - }); + expect(humanizeTplAst(parse('<div (a)="b">', [dirA]))).toEqual([ + [ElementAst, 'div'], [BoundEventAst, 'a', null, 'b'], [DirectiveAst, dirA] + ]); + }); - it('should parse literal directive properties', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['a'] - }).toSummary(); - expect(humanizeTplAst(parse('<div a="literal"></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], [AttrAst, 'a', 'literal'], [DirectiveAst, dirA], - [BoundDirectivePropertyAst, 'a', '"literal"'] - ]); - }); + it('should parse directive host properties', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + host: {'[a]': 'expr'} + }).toSummary(); + expect(humanizeTplAst(parse('<div></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA], + [BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'expr', null] + ]); + }); - it('should favor explicit bound properties over literal properties', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['a'] - }).toSummary(); - expect(humanizeTplAst(parse('<div a="literal" [a]="\'literal2\'"></div>', [dirA]))) - .toEqual([ - [ElementAst, 'div'], [AttrAst, 'a', 'literal'], [DirectiveAst, dirA], - [BoundDirectivePropertyAst, 'a', '"literal2"'] - ]); - }); + it('should parse directive host listeners', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + host: {'(a)': 'expr'} + }).toSummary(); + expect(humanizeTplAst(parse('<div></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA], [BoundEventAst, 'a', null, 'expr'] + ]); + }); - it('should support optional directive properties', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['a'] - }).toSummary(); - expect(humanizeTplAst(parse('<div></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA] - ]); - }); + it('should parse directive properties', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['aProp'] + }).toSummary(); + expect(humanizeTplAst(parse('<div [aProp]="expr"></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA], [BoundDirectivePropertyAst, 'aProp', 'expr'] + ]); + }); + it('should parse renamed directive properties', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['b:a'] + }).toSummary(); + expect(humanizeTplAst(parse('<div [a]="expr"></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA], [BoundDirectivePropertyAst, 'b', 'expr'] + ]); }); - describe('providers', () => { - let nextProviderId: number; + it('should parse literal directive properties', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['a'] + }).toSummary(); + expect(humanizeTplAst(parse('<div a="literal"></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'a', 'literal'], [DirectiveAst, dirA], + [BoundDirectivePropertyAst, 'a', '"literal"'] + ]); + }); - function createToken(value: string): CompileTokenMetadata { - let token: CompileTokenMetadata; - if (value.startsWith('type:')) { - const name = value.substring(5); - token = {identifier: createTypeMeta({reference: <any>name})}; - } else { - token = {value: value}; - } - return token; - } + it('should favor explicit bound properties over literal properties', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['a'] + }).toSummary(); + expect(humanizeTplAst(parse('<div a="literal" [a]="\'literal2\'"></div>', [dirA]))) + .toEqual([ + [ElementAst, 'div'], [AttrAst, 'a', 'literal'], [DirectiveAst, dirA], + [BoundDirectivePropertyAst, 'a', '"literal2"'] + ]); + }); - function createDep(value: string): CompileDiDependencyMetadata { - let isOptional = false; - if (value.startsWith('optional:')) { - isOptional = true; - value = value.substring(9); - } - let isSelf = false; - if (value.startsWith('self:')) { - isSelf = true; - value = value.substring(5); - } - let isHost = false; - if (value.startsWith('host:')) { - isHost = true; - value = value.substring(5); - } - return { - token: createToken(value), - isOptional: isOptional, - isSelf: isSelf, - isHost: isHost - }; - } + it('should support optional directive properties', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['a'] + }).toSummary(); + expect(humanizeTplAst(parse('<div></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA] + ]); + }); + }); - function createProvider( - token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}): - CompileProviderMetadata { - const compileToken = createToken(token); - return { - token: compileToken, - multi: multi, - useClass: createTypeMeta({reference: tokenReference(compileToken)}), - deps: deps.map(createDep), - useExisting: undefined, - useFactory: undefined, - useValue: undefined - }; - } + describe('providers', () => { + let nextProviderId: number; - function createDir( - selector: string, - {providers = undefined, viewProviders = undefined, deps = [], queries = []}: { - providers?: CompileProviderMetadata[] | undefined, - viewProviders?: CompileProviderMetadata[] | undefined, - deps?: string[], - queries?: string[] - } = {}): CompileDirectiveSummary { - const isComponent = !selector.startsWith('['); - return compileDirectiveMetadataCreate({ - selector: selector, - type: createTypeMeta({ - reference: <any>selector, - diDeps: deps.map(createDep), - }), - isComponent: isComponent, - template: compileTemplateMetadata({ngContentSelectors: []}), - providers: providers, - viewProviders: viewProviders, - queries: queries.map((value) => { - return { - selectors: [createToken(value)], - descendants: false, - first: false, - propertyName: 'test', - read: undefined ! - }; - }) - }) - .toSummary(); + function createToken(value: string): CompileTokenMetadata { + let token: CompileTokenMetadata; + if (value.startsWith('type:')) { + const name = value.substring(5); + token = {identifier: createTypeMeta({reference: <any>name})}; + } else { + token = {value: value}; } + return token; + } - beforeEach(() => { nextProviderId = 0; }); - - it('should provide a component', () => { - const comp = createDir('my-comp'); - const elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0]; - expect(elAst.providers.length).toBe(1); - expect(elAst.providers[0].providerType).toBe(ProviderAstType.Component); - expect(elAst.providers[0].providers[0].useClass).toBe(comp.type); - }); - - it('should provide a directive', () => { - const dirA = createDir('[dirA]'); - const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0]; - expect(elAst.providers.length).toBe(1); - expect(elAst.providers[0].providerType).toBe(ProviderAstType.Directive); - expect(elAst.providers[0].providers[0].useClass).toBe(dirA.type); - }); - - it('should use the public providers of a directive', () => { - const provider = createProvider('service'); - const dirA = createDir('[dirA]', {providers: [provider]}); - const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0]; - expect(elAst.providers.length).toBe(2); - expect(elAst.providers[0].providerType).toBe(ProviderAstType.PublicService); - expect(elAst.providers[0].providers).toEqual([provider]); - }); - - it('should use the private providers of a component', () => { - const provider = createProvider('service'); - const comp = createDir('my-comp', {viewProviders: [provider]}); - const elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0]; - expect(elAst.providers.length).toBe(2); - expect(elAst.providers[0].providerType).toBe(ProviderAstType.PrivateService); - expect(elAst.providers[0].providers).toEqual([provider]); - }); - - it('should support multi providers', () => { - const provider0 = createProvider('service0', {multi: true}); - const provider1 = createProvider('service1', {multi: true}); - const provider2 = createProvider('service0', {multi: true}); - const dirA = createDir('[dirA]', {providers: [provider0, provider1]}); - const dirB = createDir('[dirB]', {providers: [provider2]}); - const elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0]; - expect(elAst.providers.length).toBe(4); - expect(elAst.providers[0].providers).toEqual([provider0, provider2]); - expect(elAst.providers[1].providers).toEqual([provider1]); - }); - - it('should overwrite non multi providers', () => { - const provider1 = createProvider('service0'); - const provider2 = createProvider('service1'); - const provider3 = createProvider('service0'); - const dirA = createDir('[dirA]', {providers: [provider1, provider2]}); - const dirB = createDir('[dirB]', {providers: [provider3]}); - const elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0]; - expect(elAst.providers.length).toBe(4); - expect(elAst.providers[0].providers).toEqual([provider3]); - expect(elAst.providers[1].providers).toEqual([provider2]); - }); - - it('should overwrite component providers by directive providers', () => { - const compProvider = createProvider('service0'); - const dirProvider = createProvider('service0'); - const comp = createDir('my-comp', {providers: [compProvider]}); - const dirA = createDir('[dirA]', {providers: [dirProvider]}); - const elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[0].providers).toEqual([dirProvider]); - }); - - it('should overwrite view providers by directive providers', () => { - const viewProvider = createProvider('service0'); - const dirProvider = createProvider('service0'); - const comp = createDir('my-comp', {viewProviders: [viewProvider]}); - const dirA = createDir('[dirA]', {providers: [dirProvider]}); - const elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[0].providers).toEqual([dirProvider]); - }); - - it('should overwrite directives by providers', () => { - const dirProvider = createProvider('type:my-comp'); - const comp = createDir('my-comp', {providers: [dirProvider]}); - const elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0]; - expect(elAst.providers.length).toBe(1); - expect(elAst.providers[0].providers).toEqual([dirProvider]); - }); - - it('if mixing multi and non multi providers', () => { - const provider0 = createProvider('service0'); - const provider1 = createProvider('service0', {multi: true}); - const dirA = createDir('[dirA]', {providers: [provider0]}); - const dirB = createDir('[dirB]', {providers: [provider1]}); - expect(() => parse('<div dirA dirB>', [dirA, dirB])) - .toThrowError( - `Template parse errors:\n` + - `Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`); - }); - - it('should sort providers by their DI order, lazy providers first', () => { - const provider0 = createProvider('service0', {deps: ['type:[dir2]']}); - const provider1 = createProvider('service1'); - const dir2 = createDir('[dir2]', {deps: ['service1']}); - const comp = createDir('my-comp', {providers: [provider0, provider1]}); - const elAst: ElementAst = <ElementAst>parse('<my-comp dir2>', [comp, dir2])[0]; - expect(elAst.providers.length).toBe(4); - expect(elAst.providers[1].providers[0].useClass).toEqual(comp.type); - expect(elAst.providers[2].providers).toEqual([provider1]); - expect(elAst.providers[3].providers[0].useClass).toEqual(dir2.type); - expect(elAst.providers[0].providers).toEqual([provider0]); - }); - - it('should sort directives by their DI order', () => { - const dir0 = createDir('[dir0]', {deps: ['type:my-comp']}); - const dir1 = createDir('[dir1]', {deps: ['type:[dir0]']}); - const dir2 = createDir('[dir2]', {deps: ['type:[dir1]']}); - const comp = createDir('my-comp'); - const elAst: ElementAst = - <ElementAst>parse('<my-comp dir2 dir0 dir1>', [comp, dir2, dir0, dir1])[0]; - expect(elAst.providers.length).toBe(4); - expect(elAst.directives[0].directive).toBe(comp); - expect(elAst.directives[1].directive).toBe(dir0); - expect(elAst.directives[2].directive).toBe(dir1); - expect(elAst.directives[3].directive).toBe(dir2); - }); - - it('should mark directives and dependencies of directives as eager', () => { - const provider0 = createProvider('service0'); - const provider1 = createProvider('service1'); - const dirA = createDir('[dirA]', {providers: [provider0, provider1], deps: ['service0']}); - const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[1].providers).toEqual([provider0]); - expect(elAst.providers[1].eager).toBe(true); - expect(elAst.providers[2].providers[0].useClass).toEqual(dirA.type); - expect(elAst.providers[2].eager).toBe(true); - expect(elAst.providers[0].providers).toEqual([provider1]); - expect(elAst.providers[0].eager).toBe(false); - }); + function createDep(value: string): CompileDiDependencyMetadata { + let isOptional = false; + if (value.startsWith('optional:')) { + isOptional = true; + value = value.substring(9); + } + let isSelf = false; + if (value.startsWith('self:')) { + isSelf = true; + value = value.substring(5); + } + let isHost = false; + if (value.startsWith('host:')) { + isHost = true; + value = value.substring(5); + } + return {token: createToken(value), isOptional: isOptional, isSelf: isSelf, isHost: isHost}; + } - it('should mark dependencies on parent elements as eager', () => { - const provider0 = createProvider('service0'); - const provider1 = createProvider('service1'); - const dirA = createDir('[dirA]', {providers: [provider0, provider1]}); - const dirB = createDir('[dirB]', {deps: ['service0']}); - const elAst: ElementAst = - <ElementAst>parse('<div dirA><div dirB></div></div>', [dirA, dirB])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type); - expect(elAst.providers[1].eager).toBe(true); - expect(elAst.providers[2].providers).toEqual([provider0]); - expect(elAst.providers[2].eager).toBe(true); - expect(elAst.providers[0].providers).toEqual([provider1]); - expect(elAst.providers[0].eager).toBe(false); - }); + function createProvider( + token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}): + CompileProviderMetadata { + const compileToken = createToken(token); + return { + token: compileToken, + multi: multi, + useClass: createTypeMeta({reference: tokenReference(compileToken)}), + deps: deps.map(createDep), + useExisting: undefined, + useFactory: undefined, + useValue: undefined + }; + } - it('should mark queried providers as eager', () => { - const provider0 = createProvider('service0'); - const provider1 = createProvider('service1'); - const dirA = - createDir('[dirA]', {providers: [provider0, provider1], queries: ['service0']}); - const elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type); - expect(elAst.providers[1].eager).toBe(true); - expect(elAst.providers[2].providers).toEqual([provider0]); - expect(elAst.providers[2].eager).toBe(true); - expect(elAst.providers[0].providers).toEqual([provider1]); - expect(elAst.providers[0].eager).toBe(false); - }); + function createDir( + selector: string, + {providers = undefined, viewProviders = undefined, deps = [], queries = []}: { + providers?: CompileProviderMetadata[]|undefined, + viewProviders?: CompileProviderMetadata[]|undefined, + deps?: string[], + queries?: string[] + } = {}): CompileDirectiveSummary { + const isComponent = !selector.startsWith('['); + return compileDirectiveMetadataCreate({ + selector: selector, + type: createTypeMeta({ + reference: <any>selector, + diDeps: deps.map(createDep), + }), + isComponent: isComponent, + template: compileTemplateMetadata({ngContentSelectors: []}), + providers: providers, + viewProviders: viewProviders, + queries: queries.map((value) => { + return { + selectors: [createToken(value)], + descendants: false, + first: false, + propertyName: 'test', + read: undefined! + }; + }) + }) + .toSummary(); + } - it('should not mark dependencies across embedded views as eager', () => { - const provider0 = createProvider('service0'); - const dirA = createDir('[dirA]', {providers: [provider0]}); - const dirB = createDir('[dirB]', {deps: ['service0']}); - const elAst: ElementAst = - <ElementAst>parse('<div dirA><div *ngIf dirB></div></div>', [dirA, dirB])[0]; - expect(elAst.providers.length).toBe(2); - expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type); - expect(elAst.providers[1].eager).toBe(true); - expect(elAst.providers[0].providers).toEqual([provider0]); - expect(elAst.providers[0].eager).toBe(false); - }); + beforeEach(() => { + nextProviderId = 0; + }); + + it('should provide a component', () => { + const comp = createDir('my-comp'); + const elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0]; + expect(elAst.providers.length).toBe(1); + expect(elAst.providers[0].providerType).toBe(ProviderAstType.Component); + expect(elAst.providers[0].providers[0].useClass).toBe(comp.type); + }); + + it('should provide a directive', () => { + const dirA = createDir('[dirA]'); + const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0]; + expect(elAst.providers.length).toBe(1); + expect(elAst.providers[0].providerType).toBe(ProviderAstType.Directive); + expect(elAst.providers[0].providers[0].useClass).toBe(dirA.type); + }); + + it('should use the public providers of a directive', () => { + const provider = createProvider('service'); + const dirA = createDir('[dirA]', {providers: [provider]}); + const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0]; + expect(elAst.providers.length).toBe(2); + expect(elAst.providers[0].providerType).toBe(ProviderAstType.PublicService); + expect(elAst.providers[0].providers).toEqual([provider]); + }); + + it('should use the private providers of a component', () => { + const provider = createProvider('service'); + const comp = createDir('my-comp', {viewProviders: [provider]}); + const elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0]; + expect(elAst.providers.length).toBe(2); + expect(elAst.providers[0].providerType).toBe(ProviderAstType.PrivateService); + expect(elAst.providers[0].providers).toEqual([provider]); + }); + + it('should support multi providers', () => { + const provider0 = createProvider('service0', {multi: true}); + const provider1 = createProvider('service1', {multi: true}); + const provider2 = createProvider('service0', {multi: true}); + const dirA = createDir('[dirA]', {providers: [provider0, provider1]}); + const dirB = createDir('[dirB]', {providers: [provider2]}); + const elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0]; + expect(elAst.providers.length).toBe(4); + expect(elAst.providers[0].providers).toEqual([provider0, provider2]); + expect(elAst.providers[1].providers).toEqual([provider1]); + }); + + it('should overwrite non multi providers', () => { + const provider1 = createProvider('service0'); + const provider2 = createProvider('service1'); + const provider3 = createProvider('service0'); + const dirA = createDir('[dirA]', {providers: [provider1, provider2]}); + const dirB = createDir('[dirB]', {providers: [provider3]}); + const elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0]; + expect(elAst.providers.length).toBe(4); + expect(elAst.providers[0].providers).toEqual([provider3]); + expect(elAst.providers[1].providers).toEqual([provider2]); + }); + + it('should overwrite component providers by directive providers', () => { + const compProvider = createProvider('service0'); + const dirProvider = createProvider('service0'); + const comp = createDir('my-comp', {providers: [compProvider]}); + const dirA = createDir('[dirA]', {providers: [dirProvider]}); + const elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[0].providers).toEqual([dirProvider]); + }); + + it('should overwrite view providers by directive providers', () => { + const viewProvider = createProvider('service0'); + const dirProvider = createProvider('service0'); + const comp = createDir('my-comp', {viewProviders: [viewProvider]}); + const dirA = createDir('[dirA]', {providers: [dirProvider]}); + const elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[0].providers).toEqual([dirProvider]); + }); + + it('should overwrite directives by providers', () => { + const dirProvider = createProvider('type:my-comp'); + const comp = createDir('my-comp', {providers: [dirProvider]}); + const elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0]; + expect(elAst.providers.length).toBe(1); + expect(elAst.providers[0].providers).toEqual([dirProvider]); + }); + + it('if mixing multi and non multi providers', () => { + const provider0 = createProvider('service0'); + const provider1 = createProvider('service0', {multi: true}); + const dirA = createDir('[dirA]', {providers: [provider0]}); + const dirB = createDir('[dirB]', {providers: [provider1]}); + expect(() => parse('<div dirA dirB>', [dirA, dirB])) + .toThrowError( + `Template parse errors:\n` + + `Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`); + }); + + it('should sort providers by their DI order, lazy providers first', () => { + const provider0 = createProvider('service0', {deps: ['type:[dir2]']}); + const provider1 = createProvider('service1'); + const dir2 = createDir('[dir2]', {deps: ['service1']}); + const comp = createDir('my-comp', {providers: [provider0, provider1]}); + const elAst: ElementAst = <ElementAst>parse('<my-comp dir2>', [comp, dir2])[0]; + expect(elAst.providers.length).toBe(4); + expect(elAst.providers[1].providers[0].useClass).toEqual(comp.type); + expect(elAst.providers[2].providers).toEqual([provider1]); + expect(elAst.providers[3].providers[0].useClass).toEqual(dir2.type); + expect(elAst.providers[0].providers).toEqual([provider0]); + }); + + it('should sort directives by their DI order', () => { + const dir0 = createDir('[dir0]', {deps: ['type:my-comp']}); + const dir1 = createDir('[dir1]', {deps: ['type:[dir0]']}); + const dir2 = createDir('[dir2]', {deps: ['type:[dir1]']}); + const comp = createDir('my-comp'); + const elAst: ElementAst = + <ElementAst>parse('<my-comp dir2 dir0 dir1>', [comp, dir2, dir0, dir1])[0]; + expect(elAst.providers.length).toBe(4); + expect(elAst.directives[0].directive).toBe(comp); + expect(elAst.directives[1].directive).toBe(dir0); + expect(elAst.directives[2].directive).toBe(dir1); + expect(elAst.directives[3].directive).toBe(dir2); + }); + + it('should mark directives and dependencies of directives as eager', () => { + const provider0 = createProvider('service0'); + const provider1 = createProvider('service1'); + const dirA = createDir('[dirA]', {providers: [provider0, provider1], deps: ['service0']}); + const elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[1].providers).toEqual([provider0]); + expect(elAst.providers[1].eager).toBe(true); + expect(elAst.providers[2].providers[0].useClass).toEqual(dirA.type); + expect(elAst.providers[2].eager).toBe(true); + expect(elAst.providers[0].providers).toEqual([provider1]); + expect(elAst.providers[0].eager).toBe(false); + }); + + it('should mark dependencies on parent elements as eager', () => { + const provider0 = createProvider('service0'); + const provider1 = createProvider('service1'); + const dirA = createDir('[dirA]', {providers: [provider0, provider1]}); + const dirB = createDir('[dirB]', {deps: ['service0']}); + const elAst: ElementAst = + <ElementAst>parse('<div dirA><div dirB></div></div>', [dirA, dirB])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type); + expect(elAst.providers[1].eager).toBe(true); + expect(elAst.providers[2].providers).toEqual([provider0]); + expect(elAst.providers[2].eager).toBe(true); + expect(elAst.providers[0].providers).toEqual([provider1]); + expect(elAst.providers[0].eager).toBe(false); + }); + + it('should mark queried providers as eager', () => { + const provider0 = createProvider('service0'); + const provider1 = createProvider('service1'); + const dirA = + createDir('[dirA]', {providers: [provider0, provider1], queries: ['service0']}); + const elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type); + expect(elAst.providers[1].eager).toBe(true); + expect(elAst.providers[2].providers).toEqual([provider0]); + expect(elAst.providers[2].eager).toBe(true); + expect(elAst.providers[0].providers).toEqual([provider1]); + expect(elAst.providers[0].eager).toBe(false); + }); + + it('should not mark dependencies across embedded views as eager', () => { + const provider0 = createProvider('service0'); + const dirA = createDir('[dirA]', {providers: [provider0]}); + const dirB = createDir('[dirB]', {deps: ['service0']}); + const elAst: ElementAst = + <ElementAst>parse('<div dirA><div *ngIf dirB></div></div>', [dirA, dirB])[0]; + expect(elAst.providers.length).toBe(2); + expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type); + expect(elAst.providers[1].eager).toBe(true); + expect(elAst.providers[0].providers).toEqual([provider0]); + expect(elAst.providers[0].eager).toBe(false); + }); + + it('should report missing @Self() deps as errors', () => { + const dirA = createDir('[dirA]', {deps: ['self:provider0']}); + expect(() => parse('<div dirA></div>', [dirA])) + .toThrowError( + 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0'); + }); - it('should report missing @Self() deps as errors', () => { - const dirA = createDir('[dirA]', {deps: ['self:provider0']}); - expect(() => parse('<div dirA></div>', [dirA])) - .toThrowError( - 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0'); - }); + it('should change missing @Self() that are optional to nulls', () => { + const dirA = createDir('[dirA]', {deps: ['optional:self:provider0']}); + const elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0]; + expect(elAst.providers[0].providers[0].deps![0].isValue).toBe(true); + expect(elAst.providers[0].providers[0].deps![0].value).toBe(null); + }); - it('should change missing @Self() that are optional to nulls', () => { - const dirA = createDir('[dirA]', {deps: ['optional:self:provider0']}); - const elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0]; - expect(elAst.providers[0].providers[0].deps ![0].isValue).toBe(true); - expect(elAst.providers[0].providers[0].deps ![0].value).toBe(null); - }); + it('should report missing @Host() deps as errors', () => { + const dirA = createDir('[dirA]', {deps: ['host:provider0']}); + expect(() => parse('<div dirA></div>', [dirA])) + .toThrowError( + 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0'); + }); - it('should report missing @Host() deps as errors', () => { - const dirA = createDir('[dirA]', {deps: ['host:provider0']}); - expect(() => parse('<div dirA></div>', [dirA])) - .toThrowError( - 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0'); - }); + it('should change missing @Host() that are optional to nulls', () => { + const dirA = createDir('[dirA]', {deps: ['optional:host:provider0']}); + const elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0]; + expect(elAst.providers[0].providers[0].deps![0].isValue).toBe(true); + expect(elAst.providers[0].providers[0].deps![0].value).toBe(null); + }); + }); - it('should change missing @Host() that are optional to nulls', () => { - const dirA = createDir('[dirA]', {deps: ['optional:host:provider0']}); - const elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0]; - expect(elAst.providers[0].providers[0].deps ![0].isValue).toBe(true); - expect(elAst.providers[0].providers[0].deps ![0].value).toBe(null); - }); + describe('references', () => { + it('should parse references via #... and not report them as attributes', () => { + expect(humanizeTplAst(parse('<div #a>', []))).toEqual([ + [ElementAst, 'div'], [ReferenceAst, 'a', null] + ]); }); - describe('references', () => { + it('should parse references via ref-... and not report them as attributes', () => { + expect(humanizeTplAst(parse('<div ref-a>', []))).toEqual([ + [ElementAst, 'div'], [ReferenceAst, 'a', null] + ]); + }); - it('should parse references via #... and not report them as attributes', () => { - expect(humanizeTplAst(parse('<div #a>', [ - ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); - }); + it('should parse camel case references', () => { + expect(humanizeTplAst(parse('<div ref-someA>', []))).toEqual([ + [ElementAst, 'div'], [ReferenceAst, 'someA', null] + ]); + }); - it('should parse references via ref-... and not report them as attributes', () => { - expect(humanizeTplAst(parse('<div ref-a>', [ - ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); - }); + it('should assign references with empty value to the element', () => { + expect(humanizeTplAst(parse('<div #a></div>', []))).toEqual([ + [ElementAst, 'div'], [ReferenceAst, 'a', null] + ]); + }); - it('should parse camel case references', () => { - expect(humanizeTplAst(parse('<div ref-someA>', [ - ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'someA', null]]); - }); + it('should assign references to directives via exportAs', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + exportAs: 'dirA' + }).toSummary(); + expect(humanizeTplAst(parse('<div a #a="dirA"></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], + [AttrAst, 'a', ''], + [ReferenceAst, 'a', createTokenForReference(dirA.type.reference)], + [DirectiveAst, dirA], + ]); + }); - it('should assign references with empty value to the element', () => { - expect(humanizeTplAst(parse('<div #a></div>', [ - ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); - }); + it('should assign references to directives via exportAs with multiple names', () => { + const pizzaTestDirective = + compileDirectiveMetadataCreate({ + selector: 'pizza-test', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Pizza'}}), + exportAs: 'pizza, cheeseSauceBread' + }).toSummary(); - it('should assign references to directives via exportAs', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: '[a]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - exportAs: 'dirA' - }).toSummary(); - expect(humanizeTplAst(parse('<div a #a="dirA"></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], - [AttrAst, 'a', ''], - [ReferenceAst, 'a', createTokenForReference(dirA.type.reference)], - [DirectiveAst, dirA], - ]); - }); + const template = '<pizza-test #food="pizza" #yum="cheeseSauceBread"></pizza-test>'; - it('should assign references to directives via exportAs with multiple names', () => { - const pizzaTestDirective = - compileDirectiveMetadataCreate({ - selector: 'pizza-test', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Pizza'}}), - exportAs: 'pizza, cheeseSauceBread' - }).toSummary(); - - const template = '<pizza-test #food="pizza" #yum="cheeseSauceBread"></pizza-test>'; - - expect(humanizeTplAst(parse(template, [pizzaTestDirective]))).toEqual([ - [ElementAst, 'pizza-test'], - [ReferenceAst, 'food', createTokenForReference(pizzaTestDirective.type.reference)], - [ReferenceAst, 'yum', createTokenForReference(pizzaTestDirective.type.reference)], - [DirectiveAst, pizzaTestDirective], - ]); - }); + expect(humanizeTplAst(parse(template, [pizzaTestDirective]))).toEqual([ + [ElementAst, 'pizza-test'], + [ReferenceAst, 'food', createTokenForReference(pizzaTestDirective.type.reference)], + [ReferenceAst, 'yum', createTokenForReference(pizzaTestDirective.type.reference)], + [DirectiveAst, pizzaTestDirective], + ]); + }); - it('should report references with values that don\'t match a directive as errors', () => { - expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors: + it('should report references with values that don\'t match a directive as errors', () => { + expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors: There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`); - }); + }); - it('should report invalid reference names', () => { - expect(() => parse('<div #a-b></div>', [])).toThrowError(`Template parse errors: + it('should report invalid reference names', () => { + expect(() => parse('<div #a-b></div>', [])).toThrowError(`Template parse errors: "-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5`); - }); + }); - it('should report missing reference names', () => { - expect(() => parse('<div #></div>', [])).toThrowError(`Template parse errors: + it('should report missing reference names', () => { + expect(() => parse('<div #></div>', [])).toThrowError(`Template parse errors: Reference does not have a name ("<div [ERROR ->]#></div>"): TestComp@0:5`); - }); + }); - it('should report variables as errors', () => { - expect(() => parse('<div let-a></div>', [])).toThrowError(`Template parse errors: + it('should report variables as errors', () => { + expect(() => parse('<div let-a></div>', [])).toThrowError(`Template parse errors: "let-" is only supported on ng-template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5`); - }); + }); - it('should report missing variable names', () => { - expect(() => parse('<ng-template let-></ng-template>', [])) - .toThrowError(`Template parse errors: + it('should report missing variable names', () => { + expect(() => parse('<ng-template let-></ng-template>', [])) + .toThrowError(`Template parse errors: Variable does not have a name ("<ng-template [ERROR ->]let-></ng-template>"): TestComp@0:13`); - }); + }); - it('should report duplicate reference names', () => { - expect(() => parse('<div #a></div><div #a></div>', [])) - .toThrowError(`Template parse errors: + it('should report duplicate reference names', () => { + expect(() => parse('<div #a></div><div #a></div>', [])).toThrowError(`Template parse errors: Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19`); - - }); - - it('should report duplicate reference names when using multiple exportAs names', () => { - const pizzaDirective = - compileDirectiveMetadataCreate({ - selector: '[dessert-pizza]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Pizza'}}), - exportAs: 'dessertPizza, chocolate' - }).toSummary(); - - const chocolateDirective = - compileDirectiveMetadataCreate({ - selector: '[chocolate]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Chocolate'}}), - exportAs: 'chocolate' - }).toSummary(); - - const template = '<div dessert-pizza chocolate #snack="chocolate"></div>'; - const compileTemplate = () => parse(template, [pizzaDirective, chocolateDirective]); - const duplicateReferenceError = 'Template parse errors:\n' + - 'Reference "#snack" is defined several times ' + - '("<div dessert-pizza chocolate [ERROR ->]#snack="chocolate"></div>")' + - ': TestComp@0:29'; - - expect(compileTemplate).toThrowError(duplicateReferenceError); - }); - - it('should not throw error when there is same reference name in different templates', - () => { - expect(() => parse('<div #a><ng-template #a><span>OK</span></ng-template></div>', [])) - .not.toThrowError(); - }); - - it('should assign references with empty value to components', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: '[a]', - isComponent: true, - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - exportAs: 'dirA', - template: compileTemplateMetadata({ngContentSelectors: []}) - }).toSummary(); - expect(humanizeTplAst(parse('<div a #a></div>', [dirA]))).toEqual([ - [ElementAst, 'div'], - [AttrAst, 'a', ''], - [ReferenceAst, 'a', createTokenForReference(dirA.type.reference)], - [DirectiveAst, dirA], - ]); - }); - - it('should not locate directives in references', () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) - }).toSummary(); - expect(humanizeTplAst(parse('<div ref-a>', [dirA]))).toEqual([ - [ElementAst, 'div'], [ReferenceAst, 'a', null] - ]); - }); }); - describe('explicit templates', () => { - let reflector: JitReflector; - - beforeEach(() => { reflector = new JitReflector(); }); - - it('should create embedded templates for <ng-template> elements', () => { - expect(humanizeTplAst(parse('<ng-template></ng-template>', [ - ]))).toEqual([[EmbeddedTemplateAst]]); - }); - - it('should create embedded templates for <ng-template> elements regardless the namespace', - () => { - expect(humanizeTplAst(parse('<svg><ng-template></ng-template></svg>', []))).toEqual([ - [ElementAst, ':svg:svg'], - [EmbeddedTemplateAst], - ]); - }); - - it('should support references via #...', () => { - expect(humanizeTplAst(parse('<ng-template #a>', []))).toEqual([ - [EmbeddedTemplateAst], - [ - ReferenceAst, 'a', createTokenForExternalReference(reflector, Identifiers.TemplateRef) - ], - ]); - }); - - it('should support references via ref-...', () => { - expect(humanizeTplAst(parse('<ng-template ref-a>', []))).toEqual([ - [EmbeddedTemplateAst], - [ - ReferenceAst, 'a', createTokenForExternalReference(reflector, Identifiers.TemplateRef) - ] - ]); - }); + it('should report duplicate reference names when using multiple exportAs names', () => { + const pizzaDirective = + compileDirectiveMetadataCreate({ + selector: '[dessert-pizza]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Pizza'}}), + exportAs: 'dessertPizza, chocolate' + }).toSummary(); - it('should parse variables via let-...', () => { - expect(humanizeTplAst(parse('<ng-template let-a="b">', []))).toEqual([ - [EmbeddedTemplateAst], - [VariableAst, 'a', 'b'], - ]); - }); + const chocolateDirective = + compileDirectiveMetadataCreate({ + selector: '[chocolate]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Chocolate'}}), + exportAs: 'chocolate' + }).toSummary(); - it('should not locate directives in variables', () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) - }).toSummary(); - expect(humanizeTplAst(parse('<ng-template let-a="b"></ng-template>', [dirA]))).toEqual([ - [EmbeddedTemplateAst], - [VariableAst, 'a', 'b'], - ]); - }); + const template = '<div dessert-pizza chocolate #snack="chocolate"></div>'; + const compileTemplate = () => parse(template, [pizzaDirective, chocolateDirective]); + const duplicateReferenceError = 'Template parse errors:\n' + + 'Reference "#snack" is defined several times ' + + '("<div dessert-pizza chocolate [ERROR ->]#snack="chocolate"></div>")' + + ': TestComp@0:29'; + expect(compileTemplate).toThrowError(duplicateReferenceError); }); - describe('inline templates', () => { - it('should report an error on variables declared with #', () => { - expect(() => humanizeTplAst(parse('<div *ngIf="#a=b">', []))) - .toThrowError(/Parser Error: Unexpected token # at column 1/); - }); - - it('should parse variables via let ...', () => { - const targetAst = [ - [EmbeddedTemplateAst], - [VariableAst, 'a', 'b'], - [ElementAst, 'div'], - ]; - - expect(humanizeTplAst(parse('<div *ngIf="let a=b">', []))).toEqual(targetAst); - - expect(humanizeTplAst(parse('<div data-*ngIf="let a=b">', []))).toEqual(targetAst); - }); - - it('should parse variables via as ...', () => { - const targetAst = [ - [EmbeddedTemplateAst], - [VariableAst, 'local', 'ngIf'], - [DirectiveAst, ngIf], - [BoundDirectivePropertyAst, 'ngIf', 'expr'], - [ElementAst, 'div'], - ]; - - expect(humanizeTplAst(parse('<div *ngIf="expr as local">', [ngIf]))).toEqual(targetAst); - }); - - describe('directives', () => { - it('should locate directives in property bindings', () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a=b]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['a'] - }).toSummary(); - const dirB = - compileDirectiveMetadataCreate({ - selector: '[b]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}) - }).toSummary(); - expect(humanizeTplAst(parse('<div *a="b" b>', [dirA, dirB]))).toEqual([ - [EmbeddedTemplateAst], [DirectiveAst, dirA], [BoundDirectivePropertyAst, 'a', 'b'], - [ElementAst, 'div'], [AttrAst, 'b', ''], [DirectiveAst, dirB] - ]); - }); - - it('should not locate directives in variables', () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) - }).toSummary(); - expect( - humanizeTplAst(parse('<ng-template let-a="b"><div></div></ng-template>', [dirA]))) - .toEqual([ - [EmbeddedTemplateAst], - [VariableAst, 'a', 'b'], - [ElementAst, 'div'], - ]); - }); - - it('should not locate directives in references', () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) - }).toSummary(); - expect(humanizeTplAst(parse('<div ref-a>', [dirA]))).toEqual([ - [ElementAst, 'div'], [ReferenceAst, 'a', null] - ]); - }); - - }); - - - it('should work with *... and use the attribute name as property binding name', () => { - expect(humanizeTplAst(parse('<div *ngIf="test">', [ngIf]))).toEqual([ - [EmbeddedTemplateAst], - [DirectiveAst, ngIf], - [BoundDirectivePropertyAst, 'ngIf', 'test'], - [ElementAst, 'div'], - - ]); + it('should not throw error when there is same reference name in different templates', () => { + expect(() => parse('<div #a><ng-template #a><span>OK</span></ng-template></div>', [])) + .not.toThrowError(); + }); - // https://github.com/angular/angular/issues/13800 - expect(humanizeTplAst(parse('<div *ngIf="-1">', [ngIf]))).toEqual([ - [EmbeddedTemplateAst], - [DirectiveAst, ngIf], - [BoundDirectivePropertyAst, 'ngIf', '0 - 1'], - [ElementAst, 'div'], - ]); - }); + it('should assign references with empty value to components', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + isComponent: true, + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + exportAs: 'dirA', + template: compileTemplateMetadata({ngContentSelectors: []}) + }).toSummary(); + expect(humanizeTplAst(parse('<div a #a></div>', [dirA]))).toEqual([ + [ElementAst, 'div'], + [AttrAst, 'a', ''], + [ReferenceAst, 'a', createTokenForReference(dirA.type.reference)], + [DirectiveAst, dirA], + ]); + }); - it('should work with *... and empty value', () => { - expect(humanizeTplAst(parse('<div *ngIf>', [ngIf]))).toEqual([ - [EmbeddedTemplateAst], - [DirectiveAst, ngIf], - [BoundDirectivePropertyAst, 'ngIf', 'null'], - [ElementAst, 'div'], - ]); - }); + it('should not locate directives in references', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) + }).toSummary(); + expect(humanizeTplAst(parse('<div ref-a>', [dirA]))).toEqual([ + [ElementAst, 'div'], [ReferenceAst, 'a', null] + ]); }); }); - describe('content projection', () => { - let compCounter: number; - beforeEach(() => { compCounter = 0; }); + describe('explicit templates', () => { + let reflector: JitReflector; - function createComp(selector: string, ngContentSelectors: string[]): CompileDirectiveSummary { - return compileDirectiveMetadataCreate({ - selector: selector, - isComponent: true, - type: createTypeMeta( - {reference: {filePath: someModuleUrl, name: `SomeComp${compCounter++}`}}), - template: compileTemplateMetadata({ngContentSelectors: ngContentSelectors}) - }) - .toSummary(); - } - - function createDir(selector: string): CompileDirectiveSummary { - return compileDirectiveMetadataCreate({ - selector: selector, - type: createTypeMeta( - {reference: {filePath: someModuleUrl, name: `SomeDir${compCounter++}`}}) - }) - .toSummary(); - } - - describe('project text nodes', () => { - it('should project text nodes with wildcard selector', () => { - expect(humanizeContentProjection(parse('<div>hello</div>', [createComp('div', ['*'])]))) - .toEqual([ - ['div', null], - ['#text(hello)', 0], - ]); - }); + beforeEach(() => { + reflector = new JitReflector(); }); - describe('project elements', () => { - it('should project elements with wildcard selector', () => { - expect(humanizeContentProjection(parse('<div><span></span></div>', [ - createComp('div', ['*']) - ]))).toEqual([['div', null], ['span', 0]]); - }); - - it('should project elements with css selector', () => { - expect(humanizeContentProjection( - parse('<div><a x></a><b></b></div>', [createComp('div', ['a[x]'])]))) - .toEqual([ - ['div', null], - ['a', 0], - ['b', null], - ]); - }); + it('should create embedded templates for <ng-template> elements', () => { + expect(humanizeTplAst(parse('<ng-template></ng-template>', []))).toEqual([ + [EmbeddedTemplateAst] + ]); }); - describe('embedded templates', () => { - it('should project embedded templates with wildcard selector', () => { - expect(humanizeContentProjection( - parse('<div><ng-template></ng-template></div>', [createComp('div', ['*'])]))) - .toEqual([ - ['div', null], - ['template', 0], - ]); - }); + it('should create embedded templates for <ng-template> elements regardless the namespace', + () => { + expect(humanizeTplAst(parse('<svg><ng-template></ng-template></svg>', []))).toEqual([ + [ElementAst, ':svg:svg'], + [EmbeddedTemplateAst], + ]); + }); - it('should project embedded templates with css selector', () => { - expect(humanizeContentProjection(parse( - '<div><ng-template x></ng-template><ng-template></ng-template></div>', - [createComp('div', ['ng-template[x]'])]))) - .toEqual([ - ['div', null], - ['template', 0], - ['template', null], - ]); - }); + it('should support references via #...', () => { + expect(humanizeTplAst(parse('<ng-template #a>', []))).toEqual([ + [EmbeddedTemplateAst], + [ReferenceAst, 'a', createTokenForExternalReference(reflector, Identifiers.TemplateRef)], + ]); }); - describe('ng-content', () => { - it('should project ng-content with wildcard selector', () => { - expect(humanizeContentProjection(parse('<div><ng-content></ng-content></div>', [ - createComp('div', ['*']) - ]))).toEqual([['div', null], ['ng-content', 0]]); - }); - - it('should project ng-content with css selector', () => { - expect(humanizeContentProjection(parse( - '<div><ng-content x></ng-content><ng-content></ng-content></div>', - [createComp('div', ['ng-content[x]'])]))) - .toEqual([['div', null], ['ng-content', 0], ['ng-content', null]]); - }); + it('should support references via ref-...', () => { + expect(humanizeTplAst(parse('<ng-template ref-a>', []))).toEqual([ + [EmbeddedTemplateAst], + [ReferenceAst, 'a', createTokenForExternalReference(reflector, Identifiers.TemplateRef)] + ]); }); - it('should project into the first matching ng-content', () => { - expect(humanizeContentProjection(parse('<div>hello<b></b><a></a></div>', [ - createComp('div', ['a', 'b', '*']) - ]))).toEqual([['div', null], ['#text(hello)', 2], ['b', 1], ['a', 0]]); + it('should parse variables via let-...', () => { + expect(humanizeTplAst(parse('<ng-template let-a="b">', []))).toEqual([ + [EmbeddedTemplateAst], + [VariableAst, 'a', 'b'], + ]); }); - it('should project into wildcard ng-content last', () => { - expect(humanizeContentProjection(parse('<div>hello<a></a></div>', [ - createComp('div', ['*', 'a']) - ]))).toEqual([['div', null], ['#text(hello)', 0], ['a', 1]]); + it('should not locate directives in variables', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) + }).toSummary(); + expect(humanizeTplAst(parse('<ng-template let-a="b"></ng-template>', [dirA]))).toEqual([ + [EmbeddedTemplateAst], + [VariableAst, 'a', 'b'], + ]); }); + }); - it('should only project direct child nodes', () => { - expect(humanizeContentProjection(parse('<div><span><a></a></span><a></a></div>', [ - createComp('div', ['a']) - ]))).toEqual([['div', null], ['span', null], ['a', null], ['a', 0]]); + describe('inline templates', () => { + it('should report an error on variables declared with #', () => { + expect(() => humanizeTplAst(parse('<div *ngIf="#a=b">', []))) + .toThrowError(/Parser Error: Unexpected token # at column 1/); }); - it('should project nodes of nested components', () => { - expect(humanizeContentProjection(parse('<a><b>hello</b></a>', [ - createComp('a', ['*']), createComp('b', ['*']) - ]))).toEqual([['a', null], ['b', 0], ['#text(hello)', 0]]); - }); + it('should parse variables via let ...', () => { + const targetAst = [ + [EmbeddedTemplateAst], + [VariableAst, 'a', 'b'], + [ElementAst, 'div'], + ]; - it('should project children of components with ngNonBindable', () => { - expect(humanizeContentProjection(parse('<div ngNonBindable>{{hello}}<span></span></div>', [ - createComp('div', ['*']) - ]))).toEqual([['div', null], ['#text({{hello}})', 0], ['span', 0]]); - }); + expect(humanizeTplAst(parse('<div *ngIf="let a=b">', []))).toEqual(targetAst); - it('should match the element when there is an inline template', () => { - expect(humanizeContentProjection(parse('<div><b *ngIf="cond"></b></div>', [ - createComp('div', ['a', 'b']), ngIf - ]))).toEqual([['div', null], ['template', 1], ['b', null]]); + expect(humanizeTplAst(parse('<div data-*ngIf="let a=b">', []))).toEqual(targetAst); }); - describe('ngProjectAs', () => { - it('should override elements', () => { - expect(humanizeContentProjection(parse('<div><a ngProjectAs="b"></a></div>', [ - createComp('div', ['a', 'b']) - ]))).toEqual([['div', null], ['a', 1]]); - }); + it('should parse variables via as ...', () => { + const targetAst = [ + [EmbeddedTemplateAst], + [VariableAst, 'local', 'ngIf'], + [DirectiveAst, ngIf], + [BoundDirectivePropertyAst, 'ngIf', 'expr'], + [ElementAst, 'div'], + ]; + + expect(humanizeTplAst(parse('<div *ngIf="expr as local">', [ngIf]))).toEqual(targetAst); + }); - it('should override <ng-content>', () => { - expect(humanizeContentProjection(parse( - '<div><ng-content ngProjectAs="b"></ng-content></div>', - [createComp('div', ['ng-content', 'b'])]))) - .toEqual([['div', null], ['ng-content', 1]]); + describe('directives', () => { + it('should locate directives in property bindings', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a=b]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['a'] + }).toSummary(); + const dirB = compileDirectiveMetadataCreate({ + selector: '[b]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}) + }).toSummary(); + expect(humanizeTplAst(parse('<div *a="b" b>', [dirA, dirB]))).toEqual([ + [EmbeddedTemplateAst], [DirectiveAst, dirA], [BoundDirectivePropertyAst, 'a', 'b'], + [ElementAst, 'div'], [AttrAst, 'b', ''], [DirectiveAst, dirB] + ]); }); - it('should override <ng-template>', () => { - expect(humanizeContentProjection(parse( - '<div><ng-template ngProjectAs="b"></ng-template></div>', - [createComp('div', ['template', 'b'])]))) + it('should not locate directives in variables', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) + }).toSummary(); + expect(humanizeTplAst(parse('<ng-template let-a="b"><div></div></ng-template>', [dirA]))) .toEqual([ - ['div', null], - ['template', 1], + [EmbeddedTemplateAst], + [VariableAst, 'a', 'b'], + [ElementAst, 'div'], ]); }); - it('should override inline templates', () => { - expect(humanizeContentProjection(parse( - '<div><a *ngIf="cond" ngProjectAs="b"></a></div>', - [createComp('div', ['a', 'b']), ngIf]))) - .toEqual([ - ['div', null], - ['template', 1], - ['a', null], - ]); + it('should not locate directives in references', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) + }).toSummary(); + expect(humanizeTplAst(parse('<div ref-a>', [dirA]))).toEqual([ + [ElementAst, 'div'], [ReferenceAst, 'a', null] + ]); }); }); - it('should support other directives before the component', () => { - expect(humanizeContentProjection(parse('<div>hello</div>', [ - createDir('div'), createComp('div', ['*']) - ]))).toEqual([['div', null], ['#text(hello)', 0]]); - }); - }); - describe('splitClasses', () => { - it('should keep an empty class', () => { expect(splitClasses('a')).toEqual(['a']); }); + it('should work with *... and use the attribute name as property binding name', () => { + expect(humanizeTplAst(parse('<div *ngIf="test">', [ngIf]))).toEqual([ + [EmbeddedTemplateAst], + [DirectiveAst, ngIf], + [BoundDirectivePropertyAst, 'ngIf', 'test'], + [ElementAst, 'div'], - it('should split 2 classes', () => { expect(splitClasses('a b')).toEqual(['a', 'b']); }); + ]); - it('should trim classes', () => { expect(splitClasses(' a b ')).toEqual(['a', 'b']); }); - }); + // https://github.com/angular/angular/issues/13800 + expect(humanizeTplAst(parse('<div *ngIf="-1">', [ngIf]))).toEqual([ + [EmbeddedTemplateAst], + [DirectiveAst, ngIf], + [BoundDirectivePropertyAst, 'ngIf', '0 - 1'], + [ElementAst, 'div'], + ]); + }); - describe('error cases', () => { - it('should report when ng-content has non WS content', () => { - expect(() => parse('<ng-content>content</ng-content>', [])) - .toThrowError( - `Template parse errors:\n` + - `<ng-content> element cannot have content. ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`); + it('should work with *... and empty value', () => { + expect(humanizeTplAst(parse('<div *ngIf>', [ngIf]))).toEqual([ + [EmbeddedTemplateAst], + [DirectiveAst, ngIf], + [BoundDirectivePropertyAst, 'ngIf', 'null'], + [ElementAst, 'div'], + ]); }); + }); + }); - it('should treat *attr on a template element as valid', - () => { expect(() => parse('<ng-template *ngIf>', [])).not.toThrowError(); }); + describe('content projection', () => { + let compCounter: number; + beforeEach(() => { + compCounter = 0; + }); - it('should report when multiple *attrs are used on the same element', () => { - expect(() => parse('<div *ngIf *ngFor>', [])).toThrowError(`Template parse errors: -Can't have multiple template bindings on one element. Use only one attribute prefixed with * ("<div *ngIf [ERROR ->]*ngFor>"): TestComp@0:11`); - }); + function createComp(selector: string, ngContentSelectors: string[]): CompileDirectiveSummary { + return compileDirectiveMetadataCreate({ + selector: selector, + isComponent: true, + type: createTypeMeta( + {reference: {filePath: someModuleUrl, name: `SomeComp${compCounter++}`}}), + template: compileTemplateMetadata({ngContentSelectors: ngContentSelectors}) + }) + .toSummary(); + } + function createDir(selector: string): CompileDirectiveSummary { + return compileDirectiveMetadataCreate({ + selector: selector, + type: createTypeMeta( + {reference: {filePath: someModuleUrl, name: `SomeDir${compCounter++}`}}) + }) + .toSummary(); + } - it('should report invalid property names', () => { - expect(() => parse('<div [invalidProp]></div>', [])).toThrowError(`Template parse errors: -Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ERROR ->][invalidProp]></div>"): TestComp@0:5`); + describe('project text nodes', () => { + it('should project text nodes with wildcard selector', () => { + expect(humanizeContentProjection(parse('<div>hello</div>', [createComp('div', ['*'])]))) + .toEqual([ + ['div', null], + ['#text(hello)', 0], + ]); }); + }); - it('should report invalid host property names', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - host: {'[invalidProp]': 'someProp'} - }).toSummary(); - expect(() => parse('<div></div>', [dirA])).toThrowError(`Template parse errors: -Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("[ERROR ->]<div></div>"): TestComp@0:0, Directive DirA`); + describe('project elements', () => { + it('should project elements with wildcard selector', () => { + expect(humanizeContentProjection(parse('<div><span></span></div>', [ + createComp('div', ['*']) + ]))).toEqual([['div', null], ['span', 0]]); }); - it('should report errors in expressions', () => { - expect(() => parse('<div [prop]="a b"></div>', [])).toThrowError(`Template parse errors: -Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:13 ("<div [prop]="[ERROR ->]a b"></div>"): TestComp@0:13`); + it('should project elements with css selector', () => { + expect(humanizeContentProjection( + parse('<div><a x></a><b></b></div>', [createComp('div', ['a[x]'])]))) + .toEqual([ + ['div', null], + ['a', 0], + ['b', null], + ]); }); + }); - it('should not throw on invalid property names if the property is used by a directive', - () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['invalidProp'] - }).toSummary(); - expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow(); - }); - - it('should not allow more than 1 component per element', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - isComponent: true, - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - template: compileTemplateMetadata({ngContentSelectors: []}) - }).toSummary(); - const dirB = compileDirectiveMetadataCreate({ - selector: 'div', - isComponent: true, - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}), - template: compileTemplateMetadata({ngContentSelectors: []}) - }).toSummary(); - expect(() => parse('<div>', [dirB, dirA])) - .toThrowError( - `Template parse errors:\n` + - `More than one component matched on this element.\n` + - `Make sure that only one component's selector can match a given element.\n` + - `Conflicting components: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`); + describe('embedded templates', () => { + it('should project embedded templates with wildcard selector', () => { + expect(humanizeContentProjection( + parse('<div><ng-template></ng-template></div>', [createComp('div', ['*'])]))) + .toEqual([ + ['div', null], + ['template', 0], + ]); }); - it('should not allow components or element bindings nor dom events on explicit embedded templates', - () => { - const dirA = - compileDirectiveMetadataCreate({ - selector: '[a]', - isComponent: true, - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - template: compileTemplateMetadata({ngContentSelectors: []}) - }).toSummary(); - - expect(() => parse('<ng-template [a]="b" (e)="f"></ng-template>', [dirA])) - .toThrowError(`Template parse errors: -Event binding e not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("<ng-template [a]="b" [ERROR ->](e)="f"></ng-template>"): TestComp@0:21 -Components on an embedded template: DirA ("[ERROR ->]<ng-template [a]="b" (e)="f"></ng-template>"): TestComp@0:0 -Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<ng-template [a]="b" (e)="f"></ng-template>"): TestComp@0:0`); - }); - - it('should not allow components or element bindings on inline embedded templates', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: '[a]', - isComponent: true, - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - template: compileTemplateMetadata({ngContentSelectors: []}) - }).toSummary(); - expect(() => parse('<div *a="b"></div>', [dirA])).toThrowError(`Template parse errors: -Components on an embedded template: DirA ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0 -Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0`); + it('should project embedded templates with css selector', () => { + expect(humanizeContentProjection(parse( + '<div><ng-template x></ng-template><ng-template></ng-template></div>', + [createComp('div', ['ng-template[x]'])]))) + .toEqual([ + ['div', null], + ['template', 0], + ['template', null], + ]); }); }); - describe('ignore elements', () => { - it('should ignore <script> elements', () => { - expect(humanizeTplAst(parse('<script></script>a', []))).toEqual([[TextAst, 'a']]); - + describe('ng-content', () => { + it('should project ng-content with wildcard selector', () => { + expect(humanizeContentProjection(parse('<div><ng-content></ng-content></div>', [ + createComp('div', ['*']) + ]))).toEqual([['div', null], ['ng-content', 0]]); }); - it('should ignore <style> elements', () => { - expect(humanizeTplAst(parse('<style></style>a', []))).toEqual([[TextAst, 'a']]); + it('should project ng-content with css selector', () => { + expect(humanizeContentProjection(parse( + '<div><ng-content x></ng-content><ng-content></ng-content></div>', + [createComp('div', ['ng-content[x]'])]))) + .toEqual([['div', null], ['ng-content', 0], ['ng-content', null]]); }); + }); - describe('<link rel="stylesheet">', () => { + it('should project into the first matching ng-content', () => { + expect(humanizeContentProjection(parse('<div>hello<b></b><a></a></div>', [ + createComp('div', ['a', 'b', '*']) + ]))).toEqual([['div', null], ['#text(hello)', 2], ['b', 1], ['a', 0]]); + }); - it('should keep <link rel="stylesheet"> elements if they have an absolute url', () => { - expect(humanizeTplAst(parse('<link rel="stylesheet" href="http://someurl">a', []))) - .toEqual([ - [ElementAst, 'link'], [AttrAst, 'rel', 'stylesheet'], - [AttrAst, 'href', 'http://someurl'], [TextAst, 'a'] - ]); - }); + it('should project into wildcard ng-content last', () => { + expect(humanizeContentProjection(parse('<div>hello<a></a></div>', [ + createComp('div', ['*', 'a']) + ]))).toEqual([['div', null], ['#text(hello)', 0], ['a', 1]]); + }); - it('should keep <link rel="stylesheet"> elements if they have no uri', () => { - expect(humanizeTplAst(parse('<link rel="stylesheet">a', [ - ]))).toEqual([[ElementAst, 'link'], [AttrAst, 'rel', 'stylesheet'], [TextAst, 'a']]); - expect(humanizeTplAst(parse('<link REL="stylesheet">a', [ - ]))).toEqual([[ElementAst, 'link'], [AttrAst, 'REL', 'stylesheet'], [TextAst, 'a']]); - }); + it('should only project direct child nodes', () => { + expect(humanizeContentProjection(parse('<div><span><a></a></span><a></a></div>', [ + createComp('div', ['a']) + ]))).toEqual([['div', null], ['span', null], ['a', null], ['a', 0]]); + }); - it('should ignore <link rel="stylesheet"> elements if they have a relative uri', () => { - expect(humanizeTplAst(parse('<link rel="stylesheet" href="./other.css">a', [ - ]))).toEqual([[TextAst, 'a']]); - expect(humanizeTplAst(parse('<link rel="stylesheet" HREF="./other.css">a', [ - ]))).toEqual([[TextAst, 'a']]); - }); + it('should project nodes of nested components', () => { + expect(humanizeContentProjection(parse('<a><b>hello</b></a>', [ + createComp('a', ['*']), createComp('b', ['*']) + ]))).toEqual([['a', null], ['b', 0], ['#text(hello)', 0]]); + }); - it('should ignore <link rel="stylesheet"> elements if they have a package: uri', () => { - expect(humanizeTplAst(parse('<link rel="stylesheet" href="package:somePackage">a', [ - ]))).toEqual([[TextAst, 'a']]); - }); + it('should project children of components with ngNonBindable', () => { + expect(humanizeContentProjection(parse('<div ngNonBindable>{{hello}}<span></span></div>', [ + createComp('div', ['*']) + ]))).toEqual([['div', null], ['#text({{hello}})', 0], ['span', 0]]); + }); - }); + it('should match the element when there is an inline template', () => { + expect(humanizeContentProjection(parse('<div><b *ngIf="cond"></b></div>', [ + createComp('div', ['a', 'b']), ngIf + ]))).toEqual([['div', null], ['template', 1], ['b', null]]); + }); - it('should ignore bindings on children of elements with ngNonBindable', () => { - expect(humanizeTplAst(parse('<div ngNonBindable>{{b}}</div>', [ - ]))).toEqual([[ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, '{{b}}']]); + describe('ngProjectAs', () => { + it('should override elements', () => { + expect(humanizeContentProjection(parse('<div><a ngProjectAs="b"></a></div>', [ + createComp('div', ['a', 'b']) + ]))).toEqual([['div', null], ['a', 1]]); }); - it('should keep nested children of elements with ngNonBindable', () => { - expect(humanizeTplAst(parse('<div ngNonBindable><span>{{b}}</span></div>', []))).toEqual([ - [ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [ElementAst, 'span'], - [TextAst, '{{b}}'] - ]); + it('should override <ng-content>', () => { + expect(humanizeContentProjection(parse( + '<div><ng-content ngProjectAs="b"></ng-content></div>', + [createComp('div', ['ng-content', 'b'])]))) + .toEqual([['div', null], ['ng-content', 1]]); }); - it('should ignore <script> elements inside of elements with ngNonBindable', () => { - expect(humanizeTplAst(parse('<div ngNonBindable><script></script>a</div>', [ - ]))).toEqual([[ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, 'a']]); + it('should override <ng-template>', () => { + expect(humanizeContentProjection(parse( + '<div><ng-template ngProjectAs="b"></ng-template></div>', + [createComp('div', ['template', 'b'])]))) + .toEqual([ + ['div', null], + ['template', 1], + ]); }); - it('should ignore <style> elements inside of elements with ngNonBindable', () => { - expect(humanizeTplAst(parse('<div ngNonBindable><style></style>a</div>', [ - ]))).toEqual([[ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, 'a']]); + it('should override inline templates', () => { + expect(humanizeContentProjection(parse( + '<div><a *ngIf="cond" ngProjectAs="b"></a></div>', + [createComp('div', ['a', 'b']), ngIf]))) + .toEqual([ + ['div', null], + ['template', 1], + ['a', null], + ]); }); + }); - it('should ignore <link rel="stylesheet"> elements inside of elements with ngNonBindable', - () => { - expect(humanizeTplAst(parse('<div ngNonBindable><link rel="stylesheet">a</div>', [ - ]))).toEqual([[ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, 'a']]); - }); + it('should support other directives before the component', () => { + expect(humanizeContentProjection(parse('<div>hello</div>', [ + createDir('div'), createComp('div', ['*']) + ]))).toEqual([['div', null], ['#text(hello)', 0]]); + }); + }); - it('should convert <ng-content> elements into regular elements inside of elements with ngNonBindable', - () => { - expect(humanizeTplAst(parse('<div ngNonBindable><ng-content></ng-content>a</div>', []))) - .toEqual([ - [ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [ElementAst, 'ng-content'], - [TextAst, 'a'] - ]); - }); + describe('splitClasses', () => { + it('should keep an empty class', () => { + expect(splitClasses('a')).toEqual(['a']); + }); + it('should split 2 classes', () => { + expect(splitClasses('a b')).toEqual(['a', 'b']); }); - describe('source spans', () => { - it('should support ng-content', () => { - const parsed = parse('<ng-content select="a">', []); - expect(humanizeTplAstSourceSpans(parsed)).toEqual([ - [NgContentAst, '<ng-content select="a">'] - ]); - }); + it('should trim classes', () => { + expect(splitClasses(' a b ')).toEqual(['a', 'b']); + }); + }); - it('should support embedded template', () => { - expect(humanizeTplAstSourceSpans(parse('<ng-template></ng-template>', [ - ]))).toEqual([[EmbeddedTemplateAst, '<ng-template>']]); - }); + describe('error cases', () => { + it('should report when ng-content has non WS content', () => { + expect(() => parse('<ng-content>content</ng-content>', [])) + .toThrowError( + `Template parse errors:\n` + + `<ng-content> element cannot have content. ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`); + }); - it('should support element and attributes', () => { - expect(humanizeTplAstSourceSpans(parse('<div key=value>', []))).toEqual([ - [ElementAst, 'div', '<div key=value>'], [AttrAst, 'key', 'value', 'key=value'] - ]); + it('should treat *attr on a template element as valid', () => { + expect(() => parse('<ng-template *ngIf>', [])).not.toThrowError(); + }); - }); + it('should report when multiple *attrs are used on the same element', () => { + expect(() => parse('<div *ngIf *ngFor>', [])).toThrowError(`Template parse errors: +Can't have multiple template bindings on one element. Use only one attribute prefixed with * ("<div *ngIf [ERROR ->]*ngFor>"): TestComp@0:11`); + }); - it('should support references', () => { - expect(humanizeTplAstSourceSpans(parse('<div #a></div>', [ - ]))).toEqual([[ElementAst, 'div', '<div #a>'], [ReferenceAst, 'a', null, '#a']]); - }); - it('should support variables', () => { - expect(humanizeTplAstSourceSpans(parse('<ng-template let-a="b"></ng-template>', []))) - .toEqual([ - [EmbeddedTemplateAst, '<ng-template let-a="b">'], - [VariableAst, 'a', 'b', 'let-a="b"'], - ]); - }); + it('should report invalid property names', () => { + expect(() => parse('<div [invalidProp]></div>', [])).toThrowError(`Template parse errors: +Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ERROR ->][invalidProp]></div>"): TestComp@0:5`); + }); - it('should support events', () => { - expect(humanizeTplAstSourceSpans(parse('<div (window:event)="v">', []))).toEqual([ - [ElementAst, 'div', '<div (window:event)="v">'], - [BoundEventAst, 'event', 'window', 'v', '(window:event)="v"'] - ]); + it('should report invalid host property names', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + host: {'[invalidProp]': 'someProp'} + }).toSummary(); + expect(() => parse('<div></div>', [dirA])).toThrowError(`Template parse errors: +Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("[ERROR ->]<div></div>"): TestComp@0:0, Directive DirA`); + }); - }); + it('should report errors in expressions', () => { + expect(() => parse('<div [prop]="a b"></div>', [])).toThrowError(`Template parse errors: +Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:13 ("<div [prop]="[ERROR ->]a b"></div>"): TestComp@0:13`); + }); - it('should support element property', () => { - expect(humanizeTplAstSourceSpans(parse('<div [someProp]="v">', []))).toEqual([ - [ElementAst, 'div', '<div [someProp]="v">'], - [ - BoundElementPropertyAst, PropertyBindingType.Property, 'someProp', 'v', null, - '[someProp]="v"' - ] - ]); - }); + it('should not throw on invalid property names if the property is used by a directive', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['invalidProp'] + }).toSummary(); + expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow(); + }); - it('should support bound text', () => { - expect(humanizeTplAstSourceSpans(parse('{{a}}', [ - ]))).toEqual([[BoundTextAst, '{{ a }}', '{{a}}']]); - }); + it('should not allow more than 1 component per element', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + isComponent: true, + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + template: compileTemplateMetadata({ngContentSelectors: []}) + }).toSummary(); + const dirB = compileDirectiveMetadataCreate({ + selector: 'div', + isComponent: true, + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}), + template: compileTemplateMetadata({ngContentSelectors: []}) + }).toSummary(); + expect(() => parse('<div>', [dirB, dirA])) + .toThrowError( + `Template parse errors:\n` + + `More than one component matched on this element.\n` + + `Make sure that only one component's selector can match a given element.\n` + + `Conflicting components: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`); + }); - it('should support text nodes', () => { - expect(humanizeTplAstSourceSpans(parse('a', []))).toEqual([[TextAst, 'a', 'a']]); - }); + it('should not allow components or element bindings nor dom events on explicit embedded templates', + () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + isComponent: true, + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + template: compileTemplateMetadata({ngContentSelectors: []}) + }).toSummary(); + + expect(() => parse('<ng-template [a]="b" (e)="f"></ng-template>', [dirA])) + .toThrowError(`Template parse errors: +Event binding e not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("<ng-template [a]="b" [ERROR ->](e)="f"></ng-template>"): TestComp@0:21 +Components on an embedded template: DirA ("[ERROR ->]<ng-template [a]="b" (e)="f"></ng-template>"): TestComp@0:0 +Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<ng-template [a]="b" (e)="f"></ng-template>"): TestComp@0:0`); + }); + + it('should not allow components or element bindings on inline embedded templates', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + isComponent: true, + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + template: compileTemplateMetadata({ngContentSelectors: []}) + }).toSummary(); + expect(() => parse('<div *a="b"></div>', [dirA])).toThrowError(`Template parse errors: +Components on an embedded template: DirA ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0 +Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0`); + }); + }); - it('should support directive', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: '[a]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) - }).toSummary(); - const comp = compileDirectiveMetadataCreate({ - selector: 'div', - isComponent: true, - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'ZComp'}}), - template: compileTemplateMetadata({ngContentSelectors: []}) - }).toSummary(); - expect(humanizeTplAstSourceSpans(parse('<div a>', [dirA, comp]))).toEqual([ - [ElementAst, 'div', '<div a>'], [AttrAst, 'a', '', 'a'], [DirectiveAst, dirA, '<div a>'], - [DirectiveAst, comp, '<div a>'] - ]); - }); + describe('ignore elements', () => { + it('should ignore <script> elements', () => { + expect(humanizeTplAst(parse('<script></script>a', []))).toEqual([[TextAst, 'a']]); + }); - it('should support directive in namespace', () => { - const tagSel = - compileDirectiveMetadataCreate({ - selector: 'circle', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'elDir'}}) - }).toSummary(); - const attrSel = - compileDirectiveMetadataCreate({ - selector: '[href]', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'attrDir'}}) - }).toSummary(); + it('should ignore <style> elements', () => { + expect(humanizeTplAst(parse('<style></style>a', []))).toEqual([[TextAst, 'a']]); + }); - expect(humanizeTplAstSourceSpans( - parse('<svg><circle /><use xlink:href="Port" /></svg>', [tagSel, attrSel]))) + describe('<link rel="stylesheet">', () => { + it('should keep <link rel="stylesheet"> elements if they have an absolute url', () => { + expect(humanizeTplAst(parse('<link rel="stylesheet" href="http://someurl">a', []))) .toEqual([ - [ElementAst, ':svg:svg', '<svg>'], - [ElementAst, ':svg:circle', '<circle />'], - [DirectiveAst, tagSel, '<circle />'], - [ElementAst, ':svg:use', '<use xlink:href="Port" />'], - [AttrAst, ':xlink:href', 'Port', 'xlink:href="Port"'], - [DirectiveAst, attrSel, '<use xlink:href="Port" />'], + [ElementAst, 'link'], [AttrAst, 'rel', 'stylesheet'], + [AttrAst, 'href', 'http://someurl'], [TextAst, 'a'] ]); }); - it('should support directive property', () => { - const dirA = compileDirectiveMetadataCreate({ - selector: 'div', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - inputs: ['aProp'] - }).toSummary(); - expect(humanizeTplAstSourceSpans(parse('<div [aProp]="foo"></div>', [dirA]))).toEqual([ - [ElementAst, 'div', '<div [aProp]="foo">'], [DirectiveAst, dirA, '<div [aProp]="foo">'], - [BoundDirectivePropertyAst, 'aProp', 'foo', '[aProp]="foo"'] + it('should keep <link rel="stylesheet"> elements if they have no uri', () => { + expect(humanizeTplAst(parse('<link rel="stylesheet">a', []))).toEqual([ + [ElementAst, 'link'], [AttrAst, 'rel', 'stylesheet'], [TextAst, 'a'] + ]); + expect(humanizeTplAst(parse('<link REL="stylesheet">a', []))).toEqual([ + [ElementAst, 'link'], [AttrAst, 'REL', 'stylesheet'], [TextAst, 'a'] ]); }); - it('should support endSourceSpan for elements', () => { - const tagSel = - compileDirectiveMetadataCreate({ - selector: 'circle', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'elDir'}}) - }).toSummary(); - const result = parse('<circle></circle>', [tagSel]); - const circle = result[0] as ElementAst; - expect(circle.endSourceSpan).toBeDefined(); - expect(circle.endSourceSpan !.start.offset).toBe(8); - expect(circle.endSourceSpan !.end.offset).toBe(17); + it('should ignore <link rel="stylesheet"> elements if they have a relative uri', () => { + expect(humanizeTplAst(parse('<link rel="stylesheet" href="./other.css">a', []))).toEqual([ + [TextAst, 'a'] + ]); + expect(humanizeTplAst(parse('<link rel="stylesheet" HREF="./other.css">a', []))).toEqual([ + [TextAst, 'a'] + ]); }); - it('should report undefined for endSourceSpan for elements without an end-tag', () => { - const ulSel = - compileDirectiveMetadataCreate({ - selector: 'ul', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'ulDir'}}) - }).toSummary(); - const liSel = - compileDirectiveMetadataCreate({ - selector: 'li', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'liDir'}}) - }).toSummary(); - const result = parse('<ul><li><li></ul>', [ulSel, liSel]); - const ul = result[0] as ElementAst; - const li = ul.children[0] as ElementAst; - expect(li.endSourceSpan).toBe(null); + it('should ignore <link rel="stylesheet"> elements if they have a package: uri', () => { + expect(humanizeTplAst(parse('<link rel="stylesheet" href="package:somePackage">a', []))) + .toEqual([[TextAst, 'a']]); }); }); - describe('pipes', () => { - it('should allow pipes that have been defined as dependencies', () => { - const testPipe = - new CompilePipeMetadata({ - name: 'test', - type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), - pure: false - }).toSummary(); - expect(() => parse('{{a | test}}', [], [testPipe])).not.toThrow(); - }); + it('should ignore bindings on children of elements with ngNonBindable', () => { + expect(humanizeTplAst(parse('<div ngNonBindable>{{b}}</div>', []))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, '{{b}}'] + ]); + }); - it('should report pipes as error that have not been defined as dependencies', () => { - expect(() => parse('{{a | test}}', [])).toThrowError(`Template parse errors: -The pipe 'test' could not be found ("{{[ERROR ->]a | test}}"): TestComp@0:2`); - }); + it('should keep nested children of elements with ngNonBindable', () => { + expect(humanizeTplAst(parse('<div ngNonBindable><span>{{b}}</span></div>', []))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [ElementAst, 'span'], + [TextAst, '{{b}}'] + ]); + }); + it('should ignore <script> elements inside of elements with ngNonBindable', () => { + expect(humanizeTplAst(parse('<div ngNonBindable><script></script>a</div>', []))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, 'a'] + ]); }); - describe('ICU messages', () => { - it('should expand plural messages', () => { - const shortForm = '{ count, plural, =0 {small} many {big} }'; - const expandedForm = '<ng-container [ngPlural]="count">' + - '<ng-template ngPluralCase="=0">small</ng-template>' + - '<ng-template ngPluralCase="many">big</ng-template>' + - '</ng-container>'; + it('should ignore <style> elements inside of elements with ngNonBindable', () => { + expect(humanizeTplAst(parse('<div ngNonBindable><style></style>a</div>', []))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, 'a'] + ]); + }); - expect(humanizeTplAst(parse(shortForm, []))).toEqual(humanizeTplAst(parse(expandedForm, [ - ]))); - }); + it('should ignore <link rel="stylesheet"> elements inside of elements with ngNonBindable', + () => { + expect(humanizeTplAst(parse('<div ngNonBindable><link rel="stylesheet">a</div>', []))) + .toEqual([[ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [TextAst, 'a']]); + }); + + it('should convert <ng-content> elements into regular elements inside of elements with ngNonBindable', + () => { + expect(humanizeTplAst(parse('<div ngNonBindable><ng-content></ng-content>a</div>', []))) + .toEqual([ + [ElementAst, 'div'], [AttrAst, 'ngNonBindable', ''], [ElementAst, 'ng-content'], + [TextAst, 'a'] + ]); + }); + }); - it('should expand select messages', () => { - const shortForm = '{ sex, select, female {foo} other {bar} }'; - const expandedForm = '<ng-container [ngSwitch]="sex">' + - '<ng-template ngSwitchCase="female">foo</ng-template>' + - '<ng-template ngSwitchDefault>bar</ng-template>' + - '</ng-container>'; + describe('source spans', () => { + it('should support ng-content', () => { + const parsed = parse('<ng-content select="a">', []); + expect(humanizeTplAstSourceSpans(parsed)).toEqual([ + [NgContentAst, '<ng-content select="a">'] + ]); + }); - expect(humanizeTplAst(parse(shortForm, []))).toEqual(humanizeTplAst(parse(expandedForm, [ - ]))); - }); + it('should support embedded template', () => { + expect(humanizeTplAstSourceSpans(parse('<ng-template></ng-template>', []))).toEqual([ + [EmbeddedTemplateAst, '<ng-template>'] + ]); + }); - it('should be possible to escape ICU messages', () => { - const escapedForm = 'escaped {{ "{" }} }'; + it('should support element and attributes', () => { + expect(humanizeTplAstSourceSpans(parse('<div key=value>', []))).toEqual([ + [ElementAst, 'div', '<div key=value>'], [AttrAst, 'key', 'value', 'key=value'] + ]); + }); - expect(humanizeTplAst(parse(escapedForm, []))).toEqual([ - [BoundTextAst, 'escaped {{ "{" }} }'], - ]); - }); + it('should support references', () => { + expect(humanizeTplAstSourceSpans(parse('<div #a></div>', []))).toEqual([ + [ElementAst, 'div', '<div #a>'], [ReferenceAst, 'a', null, '#a'] + ]); }); - }); - describe('whitespaces removal', () => { + it('should support variables', () => { + expect(humanizeTplAstSourceSpans(parse('<ng-template let-a="b"></ng-template>', []))) + .toEqual([ + [EmbeddedTemplateAst, '<ng-template let-a="b">'], + [VariableAst, 'a', 'b', 'let-a="b"'], + ]); + }); - beforeEach(() => { - TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); - }); - - commonBeforeEach(); - - it('should not remove whitespaces by default', () => { - expect(humanizeTplAst(parse(' <br> <br>\t<br>\n<br> ', []))).toEqual([ - [TextAst, ' '], - [ElementAst, 'br'], - [TextAst, ' '], - [ElementAst, 'br'], - [TextAst, '\t'], - [ElementAst, 'br'], - [TextAst, '\n'], - [ElementAst, 'br'], - [TextAst, ' '], + it('should support events', () => { + expect(humanizeTplAstSourceSpans(parse('<div (window:event)="v">', []))).toEqual([ + [ElementAst, 'div', '<div (window:event)="v">'], + [BoundEventAst, 'event', 'window', 'v', '(window:event)="v"'] + ]); + }); + + it('should support element property', () => { + expect(humanizeTplAstSourceSpans(parse('<div [someProp]="v">', []))).toEqual([ + [ElementAst, 'div', '<div [someProp]="v">'], + [ + BoundElementPropertyAst, PropertyBindingType.Property, 'someProp', 'v', null, + '[someProp]="v"' + ] ]); }); - it('should replace each &ngsp; with a space when preserveWhitespaces is true', () => { - expect(humanizeTplAst(parse('foo&ngsp;&ngsp;&ngsp;bar', [], [], [], true))).toEqual([ - [TextAst, 'foo bar'], + it('should support bound text', () => { + expect(humanizeTplAstSourceSpans(parse('{{a}}', []))).toEqual([ + [BoundTextAst, '{{ a }}', '{{a}}'] ]); }); - it('should replace every &ngsp; with a single space when preserveWhitespaces is false', () => { - expect(humanizeTplAst(parse('foo&ngsp;&ngsp;&ngsp;bar', [], [], [], false))).toEqual([ - [TextAst, 'foo bar'], + it('should support text nodes', () => { + expect(humanizeTplAstSourceSpans(parse('a', []))).toEqual([[TextAst, 'a', 'a']]); + }); + + it('should support directive', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: '[a]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}) + }).toSummary(); + const comp = compileDirectiveMetadataCreate({ + selector: 'div', + isComponent: true, + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'ZComp'}}), + template: compileTemplateMetadata({ngContentSelectors: []}) + }).toSummary(); + expect(humanizeTplAstSourceSpans(parse('<div a>', [dirA, comp]))).toEqual([ + [ElementAst, 'div', '<div a>'], [AttrAst, 'a', '', 'a'], [DirectiveAst, dirA, '<div a>'], + [DirectiveAst, comp, '<div a>'] ]); }); - it('should remove whitespaces when explicitly requested', () => { - expect(humanizeTplAst(parse(' <br> <br>\t<br>\n<br> ', [], [], [], false))).toEqual([ - [ElementAst, 'br'], - [ElementAst, 'br'], - [ElementAst, 'br'], - [ElementAst, 'br'], + it('should support directive in namespace', () => { + const tagSel = compileDirectiveMetadataCreate({ + selector: 'circle', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'elDir'}}) + }).toSummary(); + const attrSel = + compileDirectiveMetadataCreate({ + selector: '[href]', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'attrDir'}}) + }).toSummary(); + + expect(humanizeTplAstSourceSpans( + parse('<svg><circle /><use xlink:href="Port" /></svg>', [tagSel, attrSel]))) + .toEqual([ + [ElementAst, ':svg:svg', '<svg>'], + [ElementAst, ':svg:circle', '<circle />'], + [DirectiveAst, tagSel, '<circle />'], + [ElementAst, ':svg:use', '<use xlink:href="Port" />'], + [AttrAst, ':xlink:href', 'Port', 'xlink:href="Port"'], + [DirectiveAst, attrSel, '<use xlink:href="Port" />'], + ]); + }); + + it('should support directive property', () => { + const dirA = compileDirectiveMetadataCreate({ + selector: 'div', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + inputs: ['aProp'] + }).toSummary(); + expect(humanizeTplAstSourceSpans(parse('<div [aProp]="foo"></div>', [dirA]))).toEqual([ + [ElementAst, 'div', '<div [aProp]="foo">'], [DirectiveAst, dirA, '<div [aProp]="foo">'], + [BoundDirectivePropertyAst, 'aProp', 'foo', '[aProp]="foo"'] ]); }); - it('should remove whitespace between ICU expansions when not preserving whitespaces', () => { + it('should support endSourceSpan for elements', () => { + const tagSel = compileDirectiveMetadataCreate({ + selector: 'circle', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'elDir'}}) + }).toSummary(); + const result = parse('<circle></circle>', [tagSel]); + const circle = result[0] as ElementAst; + expect(circle.endSourceSpan).toBeDefined(); + expect(circle.endSourceSpan!.start.offset).toBe(8); + expect(circle.endSourceSpan!.end.offset).toBe(17); + }); + + it('should report undefined for endSourceSpan for elements without an end-tag', () => { + const ulSel = compileDirectiveMetadataCreate({ + selector: 'ul', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'ulDir'}}) + }).toSummary(); + const liSel = compileDirectiveMetadataCreate({ + selector: 'li', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'liDir'}}) + }).toSummary(); + const result = parse('<ul><li><li></ul>', [ulSel, liSel]); + const ul = result[0] as ElementAst; + const li = ul.children[0] as ElementAst; + expect(li.endSourceSpan).toBe(null); + }); + }); + + describe('pipes', () => { + it('should allow pipes that have been defined as dependencies', () => { + const testPipe = new CompilePipeMetadata({ + name: 'test', + type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}), + pure: false + }).toSummary(); + expect(() => parse('{{a | test}}', [], [testPipe])).not.toThrow(); + }); + + it('should report pipes as error that have not been defined as dependencies', () => { + expect(() => parse('{{a | test}}', [])).toThrowError(`Template parse errors: +The pipe 'test' could not be found ("{{[ERROR ->]a | test}}"): TestComp@0:2`); + }); + }); + + describe('ICU messages', () => { + it('should expand plural messages', () => { const shortForm = '{ count, plural, =0 {small} many {big} }'; const expandedForm = '<ng-container [ngPlural]="count">' + '<ng-template ngPluralCase="=0">small</ng-template>' + '<ng-template ngPluralCase="many">big</ng-template>' + '</ng-container>'; - const humanizedExpandedForm = humanizeTplAst(parse(expandedForm, [])); - // ICU expansions are converted to `<ng-container>` tags and all blank text nodes are reomved - // so any whitespace between ICU exansions are removed as well - expect(humanizeTplAst(parse(`${shortForm} ${shortForm}`, [], [], [], false))).toEqual([ - ...humanizedExpandedForm, ...humanizedExpandedForm + expect(humanizeTplAst(parse(shortForm, []))).toEqual(humanizeTplAst(parse(expandedForm, []))); + }); + + it('should expand select messages', () => { + const shortForm = '{ sex, select, female {foo} other {bar} }'; + const expandedForm = '<ng-container [ngSwitch]="sex">' + + '<ng-template ngSwitchCase="female">foo</ng-template>' + + '<ng-template ngSwitchDefault>bar</ng-template>' + + '</ng-container>'; + + expect(humanizeTplAst(parse(shortForm, []))).toEqual(humanizeTplAst(parse(expandedForm, []))); + }); + + it('should be possible to escape ICU messages', () => { + const escapedForm = 'escaped {{ "{" }} }'; + + expect(humanizeTplAst(parse(escapedForm, []))).toEqual([ + [BoundTextAst, 'escaped {{ "{" }} }'], ]); }); }); +}); + +describe('whitespaces removal', () => { + beforeEach(() => { + TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); + }); + + commonBeforeEach(); + + it('should not remove whitespaces by default', () => { + expect(humanizeTplAst(parse(' <br> <br>\t<br>\n<br> ', []))).toEqual([ + [TextAst, ' '], + [ElementAst, 'br'], + [TextAst, ' '], + [ElementAst, 'br'], + [TextAst, '\t'], + [ElementAst, 'br'], + [TextAst, '\n'], + [ElementAst, 'br'], + [TextAst, ' '], + ]); + }); + + it('should replace each &ngsp; with a space when preserveWhitespaces is true', () => { + expect(humanizeTplAst(parse('foo&ngsp;&ngsp;&ngsp;bar', [], [], [], true))).toEqual([ + [TextAst, 'foo bar'], + ]); + }); + + it('should replace every &ngsp; with a single space when preserveWhitespaces is false', () => { + expect(humanizeTplAst(parse('foo&ngsp;&ngsp;&ngsp;bar', [], [], [], false))).toEqual([ + [TextAst, 'foo bar'], + ]); + }); + + it('should remove whitespaces when explicitly requested', () => { + expect(humanizeTplAst(parse(' <br> <br>\t<br>\n<br> ', [], [], [], false))).toEqual([ + [ElementAst, 'br'], + [ElementAst, 'br'], + [ElementAst, 'br'], + [ElementAst, 'br'], + ]); + }); + it('should remove whitespace between ICU expansions when not preserving whitespaces', () => { + const shortForm = '{ count, plural, =0 {small} many {big} }'; + const expandedForm = '<ng-container [ngPlural]="count">' + + '<ng-template ngPluralCase="=0">small</ng-template>' + + '<ng-template ngPluralCase="many">big</ng-template>' + + '</ng-container>'; + const humanizedExpandedForm = humanizeTplAst(parse(expandedForm, [])); + + // ICU expansions are converted to `<ng-container>` tags and all blank text nodes are reomved + // so any whitespace between ICU exansions are removed as well + expect(humanizeTplAst(parse(`${shortForm} ${shortForm}`, [], [], [], false))).toEqual([ + ...humanizedExpandedForm, ...humanizedExpandedForm + ]); + }); +}); })(); diff --git a/packages/compiler/test/template_parser/template_preparser_spec.ts b/packages/compiler/test/template_parser/template_preparser_spec.ts index a5fd04163263c..27d9ebba4f949 100644 --- a/packages/compiler/test/template_parser/template_preparser_spec.ts +++ b/packages/compiler/test/template_parser/template_preparser_spec.ts @@ -14,7 +14,9 @@ import {PreparsedElement, PreparsedElementType, preparseElement} from '../../src { describe('preparseElement', () => { let htmlParser: HtmlParser; - beforeEach(inject([HtmlParser], (_htmlParser: HtmlParser) => { htmlParser = _htmlParser; })); + beforeEach(inject([HtmlParser], (_htmlParser: HtmlParser) => { + htmlParser = _htmlParser; + })); function preparse(html: string): PreparsedElement { return preparseElement(htmlParser.parse(html, 'TestComp').rootNodes[0] as Element); diff --git a/packages/compiler/test/template_parser/util/expression.ts b/packages/compiler/test/template_parser/util/expression.ts index a74b60f26add3..281d86eb4a365 100644 --- a/packages/compiler/test/template_parser/util/expression.ts +++ b/packages/compiler/test/template_parser/util/expression.ts @@ -15,12 +15,16 @@ type HumanizedExpressionSource = [string, AbsoluteSourceSpan]; class ExpressionSourceHumanizer extends e.RecursiveAstVisitor implements t.TemplateAstVisitor { result: HumanizedExpressionSource[] = []; - private recordAst(ast: e.AST) { this.result.push([unparse(ast), ast.sourceSpan]); } + private recordAst(ast: e.AST) { + this.result.push([unparse(ast), ast.sourceSpan]); + } // This method is defined to reconcile the type of ExpressionSourceHumanizer // since both RecursiveAstVisitor and TemplateAstVisitor define the visit() // method in their interfaces. - visit(node: e.AST|t.TemplateAst, context?: any) { node.visit(this, context); } + visit(node: e.AST|t.TemplateAst, context?: any) { + node.visit(this, context); + } visitASTWithSource(ast: e.ASTWithSource) { this.recordAst(ast); @@ -128,17 +132,25 @@ class ExpressionSourceHumanizer extends e.RecursiveAstVisitor implements t.Templ } visitReference(ast: t.ReferenceAst) {} visitVariable(ast: t.VariableAst) {} - visitEvent(ast: t.BoundEventAst) { ast.handler.visit(this); } - visitElementProperty(ast: t.BoundElementPropertyAst) { ast.value.visit(this); } + visitEvent(ast: t.BoundEventAst) { + ast.handler.visit(this); + } + visitElementProperty(ast: t.BoundElementPropertyAst) { + ast.value.visit(this); + } visitAttr(ast: t.AttrAst) {} - visitBoundText(ast: t.BoundTextAst) { ast.value.visit(this); } + visitBoundText(ast: t.BoundTextAst) { + ast.value.visit(this); + } visitText(ast: t.TextAst) {} visitDirective(ast: t.DirectiveAst) { t.templateVisitAll(this, ast.hostEvents); t.templateVisitAll(this, ast.hostProperties); t.templateVisitAll(this, ast.inputs); } - visitDirectiveProperty(ast: t.BoundDirectivePropertyAst) { ast.value.visit(this); } + visitDirectiveProperty(ast: t.BoundDirectivePropertyAst) { + ast.value.visit(this); + } } /** diff --git a/packages/compiler/test/template_parser/util/metadata.ts b/packages/compiler/test/template_parser/util/metadata.ts index 433f3485a9f0b..8da54abd9eb41 100644 --- a/packages/compiler/test/template_parser/util/metadata.ts +++ b/packages/compiler/test/template_parser/util/metadata.ts @@ -5,8 +5,9 @@ * 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 {CompileDirectiveMetadata, CompileEntryComponentMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata, ProxyClass, StaticSymbol, preserveWhitespacesDefault} from '@angular/compiler'; +import {CompileDirectiveMetadata, CompileEntryComponentMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata, preserveWhitespacesDefault, ProxyClass, StaticSymbol} from '@angular/compiler'; import {ChangeDetectionStrategy, RendererType2, ViewEncapsulation} from '@angular/core'; + import {noUndefined} from '../../../src/util'; export function createTypeMeta({reference, diDeps}: {reference: any, diDeps?: any[]}): @@ -14,13 +15,28 @@ export function createTypeMeta({reference, diDeps}: {reference: any, diDeps?: an return {reference: reference, diDeps: diDeps || [], lifecycleHooks: []}; } -export function compileDirectiveMetadataCreate( - {isHost, type, isComponent, selector, exportAs, inputs, outputs, host, providers, viewProviders, - queries, guards, viewQueries, entryComponents, template, componentViewType, - rendererType}: Partial<Parameters<typeof CompileDirectiveMetadata.create>[0]>) { +export function compileDirectiveMetadataCreate({ + isHost, + type, + isComponent, + selector, + exportAs, + inputs, + outputs, + host, + providers, + viewProviders, + queries, + guards, + viewQueries, + entryComponents, + template, + componentViewType, + rendererType +}: Partial<Parameters<typeof CompileDirectiveMetadata.create>[0]>) { return CompileDirectiveMetadata.create({ isHost: !!isHost, - type: noUndefined(type) !, + type: noUndefined(type)!, isComponent: !!isComponent, selector: noUndefined(selector), exportAs: noUndefined(exportAs), @@ -34,17 +50,26 @@ export function compileDirectiveMetadataCreate( guards: guards || {}, viewQueries: viewQueries || [], entryComponents: entryComponents || [], - template: noUndefined(template) !, + template: noUndefined(template)!, componentViewType: noUndefined(componentViewType), rendererType: noUndefined(rendererType), componentFactory: null, }); } -export function compileTemplateMetadata( - {encapsulation, template, templateUrl, styles, styleUrls, externalStylesheets, animations, - ngContentSelectors, interpolation, isInline, - preserveWhitespaces}: Partial<CompileTemplateMetadata>): CompileTemplateMetadata { +export function compileTemplateMetadata({ + encapsulation, + template, + templateUrl, + styles, + styleUrls, + externalStylesheets, + animations, + ngContentSelectors, + interpolation, + isInline, + preserveWhitespaces +}: Partial<CompileTemplateMetadata>): CompileTemplateMetadata { return new CompileTemplateMetadata({ encapsulation: noUndefined(encapsulation), template: noUndefined(template), diff --git a/packages/compiler/test/url_resolver_spec.ts b/packages/compiler/test/url_resolver_spec.ts index c6dc97c8a36fc..04f295dde3bed 100644 --- a/packages/compiler/test/url_resolver_spec.ts +++ b/packages/compiler/test/url_resolver_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {UrlResolver, createOfflineCompileUrlResolver} from '@angular/compiler/src/url_resolver'; +import {createOfflineCompileUrlResolver, UrlResolver} from '@angular/compiler/src/url_resolver'; import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; { @@ -65,7 +65,6 @@ import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/sr expect(resolver.resolve('foo/', './bar')).toEqual('foo/bar'); expect(resolver.resolve('foo/baz', './bar')).toEqual('foo/bar'); expect(resolver.resolve('foo/baz', 'bar')).toEqual('foo/bar'); - }); it('should support ".." in the path', () => { @@ -89,14 +88,14 @@ import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/sr describe('packages', () => { it('should resolve a url based on the application package', () => { resolver = new UrlResolver('my_packages_dir'); - expect(resolver.resolve(null !, 'package:some/dir/file.txt')) + expect(resolver.resolve(null!, 'package:some/dir/file.txt')) .toEqual('my_packages_dir/some/dir/file.txt'); - expect(resolver.resolve(null !, 'some/dir/file.txt')).toEqual('some/dir/file.txt'); + expect(resolver.resolve(null!, 'some/dir/file.txt')).toEqual('some/dir/file.txt'); }); it('should contain a default value of "/" when nothing is provided', inject([UrlResolver], (resolver: UrlResolver) => { - expect(resolver.resolve(null !, 'package:file')).toEqual('/file'); + expect(resolver.resolve(null!, 'package:file')).toEqual('/file'); })); it('should resolve a package value when present within the baseurl', () => { diff --git a/packages/compiler/test/util_spec.ts b/packages/compiler/test/util_spec.ts index 5f082f04f9793..dfa04554435ec 100644 --- a/packages/compiler/test/util_spec.ts +++ b/packages/compiler/test/util_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {escapeRegExp, splitAtColon, stringify, utf8Encode} from '../src/util'; +import {escapeRegExp, partitionArray, splitAtColon, stringify, utf8Encode} from '../src/util'; { describe('util', () => { @@ -15,7 +15,9 @@ import {escapeRegExp, splitAtColon, stringify, utf8Encode} from '../src/util'; expect(splitAtColon('a:b', [])).toEqual(['a', 'b']); }); - it('should trim parts', () => { expect(splitAtColon(' a : b ', [])).toEqual(['a', 'b']); }); + it('should trim parts', () => { + expect(splitAtColon(' a : b ', [])).toEqual(['a', 'b']); + }); it('should support multiple ":"', () => { expect(splitAtColon('a:b:c', [])).toEqual(['a', 'b:c']); @@ -70,13 +72,32 @@ import {escapeRegExp, splitAtColon, stringify, utf8Encode} from '../src/util'; ['\uDEEE', '\xED\xBB\xAE'], ['\uDFFF', '\xED\xBF\xBF'], ]; - tests.forEach(([input, output]) => { expect(utf8Encode(input)).toEqual(output); }); + tests.forEach(([input, output]) => { + expect(utf8Encode(input)).toEqual(output); + }); }); }); describe('stringify()', () => { - it('should handle objects with no prototype.', - () => { expect(stringify(Object.create(null))).toEqual('object'); }); + it('should handle objects with no prototype.', () => { + expect(stringify(Object.create(null))).toEqual('object'); + }); + }); + + describe('partitionArray()', () => { + it('should handle empty arrays', () => { + expect(partitionArray([], () => true)).toEqual([[], []]); + }); + + it('should handle arrays with primitive type values', () => { + expect(partitionArray([1, 2, 3], (el: number) => el < 2)).toEqual([[1], [2, 3]]); + }); + + it('should handle arrays of objects', () => { + expect(partitionArray([{id: 1}, {id: 2}, {id: 3}], (el: any) => el.id < 2)).toEqual([ + [{id: 1}], [{id: 2}, {id: 3}] + ]); + }); }); }); } diff --git a/packages/compiler/testing/src/directive_resolver_mock.ts b/packages/compiler/testing/src/directive_resolver_mock.ts index f3de0fe888c76..cfb91dc3c2c72 100644 --- a/packages/compiler/testing/src/directive_resolver_mock.ts +++ b/packages/compiler/testing/src/directive_resolver_mock.ts @@ -5,7 +5,7 @@ * 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 {CompileReflector, DirectiveResolver, core} from '@angular/compiler'; +import {CompileReflector, core, DirectiveResolver} from '@angular/compiler'; /** * An implementation of {@link DirectiveResolver} that allows overriding @@ -14,7 +14,9 @@ import {CompileReflector, DirectiveResolver, core} from '@angular/compiler'; export class MockDirectiveResolver extends DirectiveResolver { private _directives = new Map<core.Type, core.Directive>(); - constructor(reflector: CompileReflector) { super(reflector); } + constructor(reflector: CompileReflector) { + super(reflector); + } resolve(type: core.Type): core.Directive; resolve(type: core.Type, throwIfNotFound: true): core.Directive; diff --git a/packages/compiler/testing/src/ng_module_resolver_mock.ts b/packages/compiler/testing/src/ng_module_resolver_mock.ts index 619435c9e4424..609ae27947896 100644 --- a/packages/compiler/testing/src/ng_module_resolver_mock.ts +++ b/packages/compiler/testing/src/ng_module_resolver_mock.ts @@ -6,12 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileReflector, NgModuleResolver, core} from '@angular/compiler'; +import {CompileReflector, core, NgModuleResolver} from '@angular/compiler'; export class MockNgModuleResolver extends NgModuleResolver { private _ngModules = new Map<core.Type, core.NgModule>(); - constructor(reflector: CompileReflector) { super(reflector); } + constructor(reflector: CompileReflector) { + super(reflector); + } /** * Overrides the {@link NgModule} for a module. @@ -27,6 +29,6 @@ export class MockNgModuleResolver extends NgModuleResolver { * `NgModuleResolver`, see `setNgModule`. */ resolve(type: core.Type, throwIfNotFound = true): core.NgModule { - return this._ngModules.get(type) || super.resolve(type, throwIfNotFound) !; + return this._ngModules.get(type) || super.resolve(type, throwIfNotFound)!; } } diff --git a/packages/compiler/testing/src/output/source_map_util.ts b/packages/compiler/testing/src/output/source_map_util.ts index 941a735c29ea1..e07d599c4fac2 100644 --- a/packages/compiler/testing/src/output/source_map_util.ts +++ b/packages/compiler/testing/src/output/source_map_util.ts @@ -17,8 +17,7 @@ export interface SourceLocation { } export function originalPositionFor( - sourceMap: SourceMap, - genPosition: {line: number | null, column: number | null}): SourceLocation { + sourceMap: SourceMap, genPosition: {line: number|null, column: number|null}): SourceLocation { const smc = new SourceMapConsumer(sourceMap); // Note: We don't return the original object as it also contains a `name` property // which is always null and we don't want to include that in our assertions... diff --git a/packages/compiler/testing/src/pipe_resolver_mock.ts b/packages/compiler/testing/src/pipe_resolver_mock.ts index 36fb91667a606..5fa1396e0bf19 100644 --- a/packages/compiler/testing/src/pipe_resolver_mock.ts +++ b/packages/compiler/testing/src/pipe_resolver_mock.ts @@ -6,17 +6,21 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileReflector, PipeResolver, core} from '@angular/compiler'; +import {CompileReflector, core, PipeResolver} from '@angular/compiler'; export class MockPipeResolver extends PipeResolver { private _pipes = new Map<core.Type, core.Pipe>(); - constructor(refector: CompileReflector) { super(refector); } + constructor(refector: CompileReflector) { + super(refector); + } /** * Overrides the {@link Pipe} for a pipe. */ - setPipe(type: core.Type, metadata: core.Pipe): void { this._pipes.set(type, metadata); } + setPipe(type: core.Type, metadata: core.Pipe): void { + this._pipes.set(type, metadata); + } /** * Returns the {@link Pipe} for a pipe: @@ -27,7 +31,7 @@ export class MockPipeResolver extends PipeResolver { resolve(type: core.Type, throwIfNotFound = true): core.Pipe { let metadata = this._pipes.get(type); if (!metadata) { - metadata = super.resolve(type, throwIfNotFound) !; + metadata = super.resolve(type, throwIfNotFound)!; } return metadata; } diff --git a/packages/compiler/testing/src/resource_loader_mock.ts b/packages/compiler/testing/src/resource_loader_mock.ts index e64c744960483..7d9f281e3d559 100644 --- a/packages/compiler/testing/src/resource_loader_mock.ts +++ b/packages/compiler/testing/src/resource_loader_mock.ts @@ -23,7 +23,9 @@ export class MockResourceLoader extends ResourceLoader { return request.getPromise(); } - hasPendingRequests() { return !!this._requests.length; } + hasPendingRequests() { + return !!this._requests.length; + } /** * Add an expectation for the given URL. Incoming requests will be checked against @@ -43,7 +45,9 @@ export class MockResourceLoader extends ResourceLoader { * unlike expectations, unused definitions do not cause `verifyNoOutstandingExpectations` * to return an error. */ - when(url: string, response: string) { this._definitions.set(url, response); } + when(url: string, response: string) { + this._definitions.set(url, response); + } /** * Process pending requests and verify there are no outstanding expectations. Also fails @@ -55,7 +59,7 @@ export class MockResourceLoader extends ResourceLoader { } do { - this._processRequest(this._requests.shift() !); + this._processRequest(this._requests.shift()!); } while (this._requests.length > 0); this.verifyNoOutstandingExpectations(); @@ -100,9 +104,9 @@ export class MockResourceLoader extends ResourceLoader { class _PendingRequest { // TODO(issue/24571): remove '!'. - resolve !: (result: string) => void; + resolve!: (result: string) => void; // TODO(issue/24571): remove '!'. - reject !: (error: any) => void; + reject!: (error: any) => void; promise: Promise<string>; constructor(public url: string) { @@ -120,7 +124,9 @@ class _PendingRequest { } } - getPromise(): Promise<string> { return this.promise; } + getPromise(): Promise<string> { + return this.promise; + } } class _Expectation { diff --git a/packages/compiler/testing/src/schema_registry_mock.ts b/packages/compiler/testing/src/schema_registry_mock.ts index b1c5b25ccfe07..485a1dde5a5e5 100644 --- a/packages/compiler/testing/src/schema_registry_mock.ts +++ b/packages/compiler/testing/src/schema_registry_mock.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementSchemaRegistry, core} from '@angular/compiler'; +import {core, ElementSchemaRegistry} from '@angular/compiler'; export class MockSchemaRegistry implements ElementSchemaRegistry { constructor( @@ -25,15 +25,21 @@ export class MockSchemaRegistry implements ElementSchemaRegistry { return value === void 0 ? true : value; } - allKnownElementNames(): string[] { return Object.keys(this.existingElements); } + allKnownElementNames(): string[] { + return Object.keys(this.existingElements); + } securityContext(selector: string, property: string, isAttribute: boolean): core.SecurityContext { return core.SecurityContext.NONE; } - getMappedPropName(attrName: string): string { return this.attrPropMapping[attrName] || attrName; } + getMappedPropName(attrName: string): string { + return this.attrPropMapping[attrName] || attrName; + } - getDefaultComponentElementName(): string { return 'ng-component'; } + getDefaultComponentElementName(): string { + return 'ng-component'; + } validateProperty(name: string): {error: boolean, msg?: string} { if (this.invalidProperties.indexOf(name) > -1) { @@ -54,9 +60,11 @@ export class MockSchemaRegistry implements ElementSchemaRegistry { } } - normalizeAnimationStyleProperty(propName: string): string { return propName; } + normalizeAnimationStyleProperty(propName: string): string { + return propName; + } normalizeAnimationStyleValue(camelCaseProp: string, userProvidedProp: string, val: string|number): {error: string, value: string} { - return {error: null !, value: val.toString()}; + return {error: null!, value: val.toString()}; } } diff --git a/packages/core/package.json b/packages/core/package.json index d0bbdd9760758..e2de2562cecac 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -15,7 +15,7 @@ "peerDependencies": { "rxjs": "^6.5.3", "tslib": "^1.10.0", - "zone.js": "~0.10.2" + "zone.js": "~0.10.3" }, "repository": { "type": "git", @@ -27,7 +27,7 @@ "packageGroup": "NG_UPDATE_PACKAGE_GROUP" }, "sideEffects": false, - "publishConfig":{ - "registry":"https://wombat-dressing-room.appspot.com" + "publishConfig": { + "registry": "https://wombat-dressing-room.appspot.com" } } diff --git a/packages/core/schematics/migrations/dynamic-queries/index.ts b/packages/core/schematics/migrations/dynamic-queries/index.ts index e84e3940cf93b..ececbb9a434bf 100644 --- a/packages/core/schematics/migrations/dynamic-queries/index.ts +++ b/packages/core/schematics/migrations/dynamic-queries/index.ts @@ -6,23 +6,21 @@ * found in the LICENSE file at https://angular.io/license */ -import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {dirname, relative} from 'path'; +import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; -import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; -import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; import {identifyDynamicQueryNodes, removeOptionsParameter, removeStaticFlag} from './util'; - /** * Runs the dynamic queries migration for all TypeScript projects in the current CLI workspace. */ export default function(): Rule { - return (tree: Tree, ctx: SchematicContext) => { + return (tree: Tree) => { const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); const basePath = process.cwd(); const allPaths = [...buildPaths, ...testPaths]; @@ -39,9 +37,7 @@ export default function(): Rule { } function runDynamicQueryMigration(tree: Tree, tsconfigPath: string, basePath: string) { - const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath); - const program = ts.createProgram(parsed.fileNames, parsed.options, host); + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); const typeChecker = program.getTypeChecker(); const sourceFiles = program.getSourceFiles().filter( f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); diff --git a/packages/core/schematics/migrations/google3/BUILD.bazel b/packages/core/schematics/migrations/google3/BUILD.bazel index d078463aefbc9..1a68dcf1d0aad 100644 --- a/packages/core/schematics/migrations/google3/BUILD.bazel +++ b/packages/core/schematics/migrations/google3/BUILD.bazel @@ -13,6 +13,7 @@ ts_library( "//packages/core/schematics/migrations/static-queries", "//packages/core/schematics/migrations/template-var-assignment", "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields", + "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3", "//packages/core/schematics/utils", "//packages/core/schematics/utils/tslint", "@npm//tslint", diff --git a/packages/core/schematics/migrations/google3/explicitQueryTimingRule.ts b/packages/core/schematics/migrations/google3/explicitQueryTimingRule.ts index 4a218c8aca536..895cb276dbbeb 100644 --- a/packages/core/schematics/migrations/google3/explicitQueryTimingRule.ts +++ b/packages/core/schematics/migrations/google3/explicitQueryTimingRule.ts @@ -28,7 +28,7 @@ export class Rule extends Rules.TypedRule { const typeChecker = program.getTypeChecker(); const queryVisitor = new NgQueryResolveVisitor(program.getTypeChecker()); const templateVisitor = new NgComponentTemplateVisitor(typeChecker); - const rootSourceFiles = program.getRootFileNames().map(f => program.getSourceFile(f) !); + const rootSourceFiles = program.getRootFileNames().map(f => program.getSourceFile(f)!); const printer = ts.createPrinter(); const failures: RuleFailure[] = []; @@ -44,7 +44,7 @@ export class Rule extends Rules.TypedRule { // check component templates for static query usage. templateVisitor.resolvedTemplates.forEach(template => { if (classMetadata.has(template.container)) { - classMetadata.get(template.container) !.template = template; + classMetadata.get(template.container)!.template = template; } }); diff --git a/packages/core/schematics/migrations/google3/noMissingInjectableRule.ts b/packages/core/schematics/migrations/google3/noMissingInjectableRule.ts index 5fa6511c9c483..ffb810cdac25c 100644 --- a/packages/core/schematics/migrations/google3/noMissingInjectableRule.ts +++ b/packages/core/schematics/migrations/google3/noMissingInjectableRule.ts @@ -50,7 +50,7 @@ export class Rule extends Rules.TypedRule { transformer.recordChanges(); if (updateRecorders.has(sourceFile)) { - failures.push(...updateRecorders.get(sourceFile) !.failures); + failures.push(...updateRecorders.get(sourceFile)!.failures); } return failures; @@ -58,7 +58,7 @@ export class Rule extends Rules.TypedRule { /** Gets the update recorder for the specified source file. */ function getUpdateRecorder(sourceFile: ts.SourceFile): TslintUpdateRecorder { if (updateRecorders.has(sourceFile)) { - return updateRecorders.get(sourceFile) !; + return updateRecorders.get(sourceFile)!; } const recorder = new TslintUpdateRecorder(ruleName, sourceFile); updateRecorders.set(sourceFile, recorder); diff --git a/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts b/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts index 767621218f6c4..c0e1108f752ae 100644 --- a/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts +++ b/packages/core/schematics/migrations/google3/rendererToRenderer2Rule.ts @@ -9,7 +9,7 @@ import {Replacement, RuleFailure, Rules} from 'tslint'; import * as ts from 'typescript'; -import {HelperFunction, getHelper} from '../renderer-to-renderer2/helpers'; +import {getHelper, HelperFunction} from '../renderer-to-renderer2/helpers'; import {migrateExpression, replaceImport} from '../renderer-to-renderer2/migration'; import {findCoreImport, findRendererReferences} from '../renderer-to-renderer2/util'; @@ -75,7 +75,7 @@ export class Rule extends Rules.TypedRule { private _getTypedNodeFailure( node: ts.ParameterDeclaration|ts.PropertyDeclaration|ts.AsExpression, sourceFile: ts.SourceFile): RuleFailure { - const type = node.type !; + const type = node.type!; return new RuleFailure( sourceFile, type.getStart(), type.getEnd(), diff --git a/packages/core/schematics/migrations/google3/undecoratedClassesWithDecoratedFieldsRule.ts b/packages/core/schematics/migrations/google3/undecoratedClassesWithDecoratedFieldsRule.ts index b260f63b99a00..2b2a32836498a 100644 --- a/packages/core/schematics/migrations/google3/undecoratedClassesWithDecoratedFieldsRule.ts +++ b/packages/core/schematics/migrations/google3/undecoratedClassesWithDecoratedFieldsRule.ts @@ -6,12 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {Replacement, RuleFailure, Rules} from 'tslint'; +import {RuleFailure, Rules} from 'tslint'; import * as ts from 'typescript'; - -import {FALLBACK_DECORATOR, addImport, getNamedImports, getUndecoratedClassesWithDecoratedFields, hasNamedImport} from '../undecorated-classes-with-decorated-fields/utils'; - - +import {TslintUpdateRecorder} from '../undecorated-classes-with-decorated-fields/google3/tslint_update_recorder'; +import {UndecoratedClassesWithDecoratedFieldsTransform} from '../undecorated-classes-with-decorated-fields/transform'; /** * TSLint rule that adds an Angular decorator to classes that have Angular field decorators. @@ -20,37 +18,32 @@ import {FALLBACK_DECORATOR, addImport, getNamedImports, getUndecoratedClassesWit export class Rule extends Rules.TypedRule { applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] { const typeChecker = program.getTypeChecker(); - const printer = ts.createPrinter(); - const classes = getUndecoratedClassesWithDecoratedFields(sourceFile, typeChecker); - - return classes.map((current, index) => { - const {classDeclaration: declaration, importDeclaration} = current; - const name = declaration.name; - - // Set the class identifier node (if available) as the failing node so IDEs don't highlight - // the entire class with red. This is similar to how errors are shown for classes in other - // cases like an interface not being implemented correctly. - const start = (name || declaration).getStart(); - const end = (name || declaration).getEnd(); - const fixes = [Replacement.appendText(declaration.getStart(), `@${FALLBACK_DECORATOR}()\n`)]; - - // If it's the first class that we're processing in this file, add `Directive` to the imports. - if (index === 0 && !hasNamedImport(importDeclaration, FALLBACK_DECORATOR)) { - const namedImports = getNamedImports(importDeclaration); - - if (namedImports) { - fixes.push(new Replacement( - namedImports.getStart(), namedImports.getWidth(), - printer.printNode( - ts.EmitHint.Unspecified, addImport(namedImports, FALLBACK_DECORATOR), - sourceFile))); - } + const ruleName = this.ruleName; + const sourceFiles = program.getSourceFiles().filter( + s => !s.isDeclarationFile && !program.isSourceFileFromExternalLibrary(s)); + const updateRecorders = new Map<ts.SourceFile, TslintUpdateRecorder>(); + const transform = + new UndecoratedClassesWithDecoratedFieldsTransform(typeChecker, getUpdateRecorder); + + // Migrate all source files in the project. + transform.migrate(sourceFiles); + + // Record the changes collected in the import manager. + transform.recordChanges(); + + if (updateRecorders.has(sourceFile)) { + return updateRecorders.get(sourceFile)!.failures; + } + return []; + + /** Gets the update recorder for the specified source file. */ + function getUpdateRecorder(sourceFile: ts.SourceFile): TslintUpdateRecorder { + if (updateRecorders.has(sourceFile)) { + return updateRecorders.get(sourceFile)!; } - - return new RuleFailure( - sourceFile, start, end, - 'Classes with decorated fields must have an Angular decorator as well.', - 'undecorated-classes-with-decorated-fields', fixes); - }); + const recorder = new TslintUpdateRecorder(ruleName, sourceFile); + updateRecorders.set(sourceFile, recorder); + return recorder; + } } } diff --git a/packages/core/schematics/migrations/missing-injectable/definition_collector.ts b/packages/core/schematics/migrations/missing-injectable/definition_collector.ts index b2d5cee32edde..abf5f5842b039 100644 --- a/packages/core/schematics/migrations/missing-injectable/definition_collector.ts +++ b/packages/core/schematics/migrations/missing-injectable/definition_collector.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {NgDecorator, getAngularDecorators} from '../../utils/ng_decorators'; +import {getAngularDecorators, NgDecorator} from '../../utils/ng_decorators'; import {getPropertyNameText} from '../../utils/typescript/property_name'; export interface ResolvedNgModule { diff --git a/packages/core/schematics/migrations/missing-injectable/google3/tslint_update_recorder.ts b/packages/core/schematics/migrations/missing-injectable/google3/tslint_update_recorder.ts index 0f3da184da8b9..195469b2688fa 100644 --- a/packages/core/schematics/migrations/missing-injectable/google3/tslint_update_recorder.ts +++ b/packages/core/schematics/migrations/missing-injectable/google3/tslint_update_recorder.ts @@ -21,7 +21,8 @@ export class TslintUpdateRecorder implements UpdateRecorder { // are handled in reverse and in case a decorator and import are inserted at // the start of the file, the class decorator should come after the import. this.failures.unshift(new RuleFailure( - this.sourceFile, node.getStart(), 0, `Class needs to be decorated with ` + + this.sourceFile, node.getStart(), 0, + `Class needs to be decorated with ` + `"${decoratorText}" because it has been provided by "${className}".`, this.ruleName, Replacement.appendText(node.getStart(), `${decoratorText}\n`))); } diff --git a/packages/core/schematics/migrations/missing-injectable/index.ts b/packages/core/schematics/migrations/missing-injectable/index.ts index dcdad8eaeaf1b..7212f0551417a 100644 --- a/packages/core/schematics/migrations/missing-injectable/index.ts +++ b/packages/core/schematics/migrations/missing-injectable/index.ts @@ -7,11 +7,10 @@ */ import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {dirname, relative} from 'path'; +import {relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; -import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; -import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; import {NgDefinitionCollector} from './definition_collector'; import {MissingInjectableTransform} from './transform'; import {UpdateRecorder} from './update_recorder'; @@ -43,11 +42,8 @@ export default function(): Rule { function runMissingInjectableMigration( tree: Tree, tsconfigPath: string, basePath: string): string[] { - const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath); + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); const failures: string[] = []; - - const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); const definitionCollector = new NgDefinitionCollector(typeChecker); const sourceFiles = program.getSourceFiles().filter( @@ -83,7 +79,7 @@ function runMissingInjectableMigration( /** Gets the update recorder for the specified source file. */ function getUpdateRecorder(sourceFile: ts.SourceFile): UpdateRecorder { if (updateRecorders.has(sourceFile)) { - return updateRecorders.get(sourceFile) !; + return updateRecorders.get(sourceFile)!; } const treeRecorder = tree.beginUpdate(relative(basePath, sourceFile.fileName)); const recorder: UpdateRecorder = { @@ -111,7 +107,9 @@ function runMissingInjectableMigration( treeRecorder.remove(node.getStart(), node.getWidth()); treeRecorder.insertRight(node.getStart(), newText); }, - commitUpdate() { tree.commitUpdate(treeRecorder); } + commitUpdate() { + tree.commitUpdate(treeRecorder); + } }; updateRecorders.set(sourceFile, recorder); return recorder; diff --git a/packages/core/schematics/migrations/missing-injectable/transform.ts b/packages/core/schematics/migrations/missing-injectable/transform.ts index d0412974e0032..f49f34b120843 100644 --- a/packages/core/schematics/migrations/missing-injectable/transform.ts +++ b/packages/core/schematics/migrations/missing-injectable/transform.ts @@ -11,16 +11,21 @@ import {DynamicValue, ResolvedValue} from '@angular/compiler-cli/src/ngtsc/parti import {TypeScriptReflectionHost} from '@angular/compiler-cli/src/ngtsc/reflection'; import * as ts from 'typescript'; +import {ImportManager} from '../../utils/import_manager'; import {getAngularDecorators} from '../../utils/ng_decorators'; import {ResolvedDirective, ResolvedNgModule} from './definition_collector'; -import {ImportManager} from './import_manager'; import {ProviderLiteral, ProvidersEvaluator} from './providers_evaluator'; import {UpdateRecorder} from './update_recorder'; - -/** Name of decorators which imply that a given class does not need to be migrated. */ -const NO_MIGRATE_DECORATORS = ['Injectable', 'Directive', 'Component', 'Pipe']; +/** + * Name of decorators which imply that a given class does not need to be migrated. + * - `@Injectable`, `@Directive`, `@Component` and `@Pipe` instruct the compiler + * to generate a factory definition. + * - `@NgModule` instructs the compiler to generate a provider definition that holds + * the factory function. + */ +const NO_MIGRATE_DECORATORS = ['Injectable', 'Directive', 'Component', 'Pipe', 'NgModule']; export interface AnalysisFailure { node: ts.Node; @@ -45,7 +50,9 @@ export class MissingInjectableTransform { new TypeScriptReflectionHost(typeChecker), typeChecker, /* dependencyTracker */ null); } - recordChanges() { this.importManager.recordChanges(); } + recordChanges() { + this.importManager.recordChanges(); + } /** * Migrates all specified NgModule's by walking through referenced providers @@ -75,10 +82,9 @@ export class MissingInjectableTransform { this._migrateLiteralProviders(literals); if (!Array.isArray(resolvedValue)) { - return [{ - node: module.providersExpr, - message: 'Providers of module are not statically analyzable.' - }]; + return [ + {node: module.providersExpr, message: 'Providers of module are not statically analyzable.'} + ]; } return this._visitProviderResolvedValue(resolvedValue, module); @@ -193,8 +199,9 @@ export class MissingInjectableTransform { const sourceFile = node.getSourceFile(); const newObjectLiteral = ts.updateObjectLiteral( - node, node.properties.concat( - ts.createPropertyAssignment('useValue', ts.createIdentifier('undefined')))); + node, + node.properties.concat( + ts.createPropertyAssignment('useValue', ts.createIdentifier('undefined')))); this.getUpdateRecorder(sourceFile) .updateObjectLiteral( @@ -216,11 +223,12 @@ export class MissingInjectableTransform { // decorate the class. This is because the class is instantiated through the // specified "deps" and the class does not need a factory definition. if (value.has('provide') && value.has('useClass') && value.get('deps') == null) { - return this._visitProviderResolvedValue(value.get('useClass') !, module); + return this._visitProviderResolvedValue(value.get('useClass')!, module); } } else if (Array.isArray(value)) { - return value.reduce((res, v) => res.concat(this._visitProviderResolvedValue(v, module)), [ - ] as AnalysisFailure[]); + return value.reduce( + (res, v) => res.concat(this._visitProviderResolvedValue(v, module)), + [] as AnalysisFailure[]); } else if (value instanceof DynamicValue) { return [{node: value.node, message: `Provider is not statically analyzable.`}]; } diff --git a/packages/core/schematics/migrations/missing-injectable/update_recorder.ts b/packages/core/schematics/migrations/missing-injectable/update_recorder.ts index 80fc670f1505a..0e2ad3ee0db50 100644 --- a/packages/core/schematics/migrations/missing-injectable/update_recorder.ts +++ b/packages/core/schematics/migrations/missing-injectable/update_recorder.ts @@ -7,15 +7,14 @@ */ import * as ts from 'typescript'; +import {ImportManagerUpdateRecorder} from '../../utils/import_manager'; /** * Update recorder interface that is used to transform source files in a non-colliding * way. Also this indirection makes it possible to re-use logic for both TSLint rules * and CLI devkit schematic updates. */ -export interface UpdateRecorder { - addNewImport(start: number, importText: string): void; - updateExistingImport(namedBindings: ts.NamedImports, newNamedBindings: string): void; +export interface UpdateRecorder extends ImportManagerUpdateRecorder { addClassDecorator(node: ts.ClassDeclaration, text: string, className: string): void; replaceDecorator(node: ts.Decorator, newText: string, className: string): void; updateObjectLiteral(node: ts.ObjectLiteralExpression, newText: string): void; diff --git a/packages/core/schematics/migrations/module-with-providers/collector.ts b/packages/core/schematics/migrations/module-with-providers/collector.ts index ee30d5e192ced..ebad0c3587a6c 100644 --- a/packages/core/schematics/migrations/module-with-providers/collector.ts +++ b/packages/core/schematics/migrations/module-with-providers/collector.ts @@ -8,7 +8,8 @@ import * as ts from 'typescript'; -import {NgDecorator, getAngularDecorators} from '../../utils/ng_decorators'; +import {getAngularDecorators, NgDecorator} from '../../utils/ng_decorators'; + import {isModuleWithProvidersNotGeneric} from './util'; export interface ResolvedNgModule { diff --git a/packages/core/schematics/migrations/module-with-providers/index.ts b/packages/core/schematics/migrations/module-with-providers/index.ts index d36beb8aa8626..85f14c45a80ec 100644 --- a/packages/core/schematics/migrations/module-with-providers/index.ts +++ b/packages/core/schematics/migrations/module-with-providers/index.ts @@ -7,18 +7,16 @@ */ import {Rule, SchematicContext, SchematicsException, Tree, UpdateRecorder} from '@angular-devkit/schematics'; -import {dirname, relative} from 'path'; +import {relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; -import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; -import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; import {Collector} from './collector'; import {AnalysisFailure, ModuleWithProvidersTransform} from './transform'; - /** * Runs the ModuleWithProviders migration for all TypeScript projects in the current CLI workspace. */ @@ -47,11 +45,8 @@ export default function(): Rule { } function runModuleWithProvidersMigration(tree: Tree, tsconfigPath: string, basePath: string) { - const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath); + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); const failures: string[] = []; - - const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); const collector = new Collector(typeChecker); const sourceFiles = program.getSourceFiles().filter( @@ -86,7 +81,7 @@ function runModuleWithProvidersMigration(tree: Tree, tsconfigPath: string, baseP /** Gets the update recorder for the specified source file. */ function getUpdateRecorder(sourceFile: ts.SourceFile): UpdateRecorder { if (updateRecorders.has(sourceFile)) { - return updateRecorders.get(sourceFile) !; + return updateRecorders.get(sourceFile)!; } const recorder = tree.beginUpdate(relative(basePath, sourceFile.fileName)); updateRecorders.set(sourceFile, recorder); diff --git a/packages/core/schematics/migrations/module-with-providers/transform.ts b/packages/core/schematics/migrations/module-with-providers/transform.ts index e11d17e82088a..c4ac37e8be43a 100644 --- a/packages/core/schematics/migrations/module-with-providers/transform.ts +++ b/packages/core/schematics/migrations/module-with-providers/transform.ts @@ -35,7 +35,7 @@ export class ModuleWithProvidersTransform { /** Migrates a given NgModule by walking through the referenced providers and static methods. */ migrateModule(module: ResolvedNgModule): AnalysisFailure[] { return module.staticMethodsWithoutType.map(this._migrateStaticNgModuleMethod.bind(this)) - .filter(v => v) as AnalysisFailure[]; + .filter(v => v) as AnalysisFailure[]; } /** Migrates a ModuleWithProviders type definition that has no explicit generic type */ @@ -98,8 +98,10 @@ export class ModuleWithProvidersTransform { return ngModule && (value.size === 1 || (providers && value.size === 2)); } - /** Determine the generic type of a suspected ModuleWithProviders return type and add it - * explicitly */ + /** + * Determine the generic type of a suspected ModuleWithProviders return type and add it + * explicitly + */ private _migrateStaticNgModuleMethod(node: ts.MethodDeclaration): AnalysisFailure|null { const returnStatement = node.body && node.body.statements.find(n => ts.isReturnStatement(n)) as ts.ReturnStatement | undefined; @@ -131,7 +133,7 @@ export class ModuleWithProvidersTransform { */ private _getTypeOfResolvedValue(value: ResolvedValue): string|undefined { if (value instanceof Map && this.isModuleWithProvidersType(value)) { - const mapValue = value.get('ngModule') !; + const mapValue = value.get('ngModule')!; if (mapValue instanceof Reference && ts.isClassDeclaration(mapValue.node) && mapValue.node.name) { return mapValue.node.name.text; diff --git a/packages/core/schematics/migrations/move-document/index.ts b/packages/core/schematics/migrations/move-document/index.ts index 2fb42f0753e45..dafaf061ede76 100644 --- a/packages/core/schematics/migrations/move-document/index.ts +++ b/packages/core/schematics/migrations/move-document/index.ts @@ -7,18 +7,16 @@ */ import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {dirname, relative} from 'path'; +import {relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; -import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; -import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; import {COMMON_IMPORT, DOCUMENT_TOKEN_NAME, DocumentImportVisitor, ResolvedDocumentImport} from './document_import_visitor'; import {addToImport, createImport, removeFromImport} from './move-import'; - /** Entry point for the V8 move-document migration. */ export default function(): Rule { return (tree: Tree) => { @@ -42,10 +40,7 @@ export default function(): Rule { * new import source. */ function runMoveDocumentMigration(tree: Tree, tsconfigPath: string, basePath: string) { - const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath); - - const program = ts.createProgram(parsed.fileNames, parsed.options, host); + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); const typeChecker = program.getTypeChecker(); const visitor = new DocumentImportVisitor(typeChecker); const sourceFiles = program.getSourceFiles().filter( diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/helpers.ts b/packages/core/schematics/migrations/renderer-to-renderer2/helpers.ts index b41a5c2cee95b..d7cff149c3cae 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/helpers.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/helpers.ts @@ -69,7 +69,7 @@ function createAnyTypeHelper(): ts.TypeAliasDeclaration { /** Creates a function parameter that is typed as `any`. */ function getAnyTypedParameter( - parameterName: string | ts.Identifier, isRequired = true): ts.ParameterDeclaration { + parameterName: string|ts.Identifier, isRequired = true): ts.ParameterDeclaration { // Declare the parameter as `any` so we don't have to add extra logic to ensure that the // generated code will pass type checking. Use our custom `any` type so people have an incentive // to clean it up afterwards and to avoid potentially introducing lint warnings in G3. @@ -153,10 +153,11 @@ function getCreationHelper( // `if (parent) { renderer.appendChild(parent, node) }`. const guardedAppendChildCall = ts.createIf( - parent, ts.createBlock( - [ts.createExpressionStatement(ts.createCall( - ts.createPropertyAccess(renderer, 'appendChild'), [], [parent, node]))], - true)); + parent, + ts.createBlock( + [ts.createExpressionStatement( + ts.createCall(ts.createPropertyAccess(renderer, 'appendChild'), [], [parent, node]))], + true)); return ts.createFunctionDeclaration( [], [], undefined, functionName, [], @@ -258,10 +259,11 @@ function getDetachViewHelper(): ts.FunctionDeclaration { // const node = rootNodes[i]; const nodeVariableStatement = ts.createVariableStatement( - undefined, ts.createVariableDeclarationList( - [ts.createVariableDeclaration( - node, undefined, ts.createElementAccess(rootNodes, incrementor))], - ts.NodeFlags.Const)); + undefined, + ts.createVariableDeclarationList( + [ts.createVariableDeclaration( + node, undefined, ts.createElementAccess(rootNodes, incrementor))], + ts.NodeFlags.Const)); // renderer.removeChild(renderer.parentNode(node), node); const removeCall = ts.createCall( ts.createPropertyAccess(renderer, 'removeChild'), [], diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts index 7144db130eb63..eb8780317f094 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts @@ -6,15 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {dirname, relative} from 'path'; +import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics'; +import {relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; -import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; -import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; -import {HelperFunction, getHelper} from './helpers'; +import {getHelper, HelperFunction} from './helpers'; import {migrateExpression, replaceImport} from './migration'; import {findCoreImport, findRendererReferences} from './util'; @@ -42,8 +41,7 @@ export default function(): Rule { } function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, basePath: string) { - const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath, fileName => { + const {program} = createMigrationProgram(tree, tsconfigPath, basePath, fileName => { // In case the module augmentation file has been requested, we return a source file that // augments "@angular/core" to include a named export called "Renderer". This ensures that // we can rely on the type checker for this migration in v9 where "Renderer" has been removed. @@ -56,10 +54,7 @@ function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, baseP `; } return null; - }); - - const program = - ts.createProgram(parsed.fileNames.concat(MODULE_AUGMENTATION_FILENAME), parsed.options, host); + }, [MODULE_AUGMENTATION_FILENAME]); const typeChecker = program.getTypeChecker(); const printer = ts.createPrinter(); const sourceFiles = program.getSourceFiles().filter( diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts b/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts index 88f29fd3e38f4..f82ab0809f49b 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/migration.ts @@ -12,7 +12,7 @@ import {HelperFunction} from './helpers'; import {findImportSpecifier} from './util'; /** A call expression that is based on a property access. */ -type PropertyAccessCallExpression = ts.CallExpression & {expression: ts.PropertyAccessExpression}; +type PropertyAccessCallExpression = ts.CallExpression&{expression: ts.PropertyAccessExpression}; /** Replaces an import inside an import statement with a different one. */ export function replaceImport(node: ts.NamedImports, oldImport: string, newImport: string) { @@ -42,7 +42,7 @@ export function replaceImport(node: ts.NamedImports, oldImport: string, newImpor * Returns null if the expression should be dropped. */ export function migrateExpression(node: ts.CallExpression, typeChecker: ts.TypeChecker): - {node: ts.Node | null, requiredHelpers?: HelperFunction[]} { + {node: ts.Node|null, requiredHelpers?: HelperFunction[]} { if (isPropertyAccessCallExpression(node)) { switch (node.expression.name.getText()) { case 'setElementProperty': @@ -152,7 +152,7 @@ function migrateSetElementClass(node: PropertyAccessCallExpression): ts.Node { // Clone so we don't mutate by accident. Note that we assume that // the user's code is providing all three required arguments. const outputMethodArgs = node.arguments.slice(); - const isAddArgument = outputMethodArgs.pop() !; + const isAddArgument = outputMethodArgs.pop()!; const createRendererCall = (isAdd: boolean) => { const innerExpression = node.expression.expression; const topExpression = @@ -263,6 +263,6 @@ function migrateAnimateCall() { */ function switchToHelperCall( node: PropertyAccessCallExpression, helper: HelperFunction, - args: ts.Expression[] | ts.NodeArray<ts.Expression>): ts.Node { + args: ts.Expression[]|ts.NodeArray<ts.Expression>): ts.Node { return ts.createCall(ts.createIdentifier(helper), [], [node.expression.expression, ...args]); } diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/util.ts b/packages/core/schematics/migrations/renderer-to-renderer2/util.ts index e2dc21df8aa35..993dd82aa3b08 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/util.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/util.ts @@ -88,8 +88,7 @@ export function findImportSpecifier( /** Checks whether a node is referring to an import spcifier. */ function isReferenceToImport( - typeChecker: ts.TypeChecker, node: ts.Node, - importSpecifier: ts.ImportSpecifier | null): boolean { + typeChecker: ts.TypeChecker, node: ts.Node, importSpecifier: ts.ImportSpecifier|null): boolean { if (importSpecifier) { const nodeSymbol = typeChecker.getTypeAtLocation(node).getSymbol(); const importSymbol = typeChecker.getTypeAtLocation(importSpecifier).getSymbol(); @@ -102,7 +101,7 @@ function isReferenceToImport( /** Finds the identifier referring to the `Renderer` inside a `forwardRef` call expression. */ function findRendererIdentifierInForwardRef( typeChecker: ts.TypeChecker, node: ts.CallExpression, - rendererImport: ts.ImportSpecifier | null): ts.Identifier|null { + rendererImport: ts.ImportSpecifier|null): ts.Identifier|null { const firstArg = node.arguments[0]; if (ts.isArrowFunction(firstArg)) { diff --git a/packages/core/schematics/migrations/static-queries/angular/directive_inputs.ts b/packages/core/schematics/migrations/static-queries/angular/directive_inputs.ts index fa511a964ae20..457ee8818f433 100644 --- a/packages/core/schematics/migrations/static-queries/angular/directive_inputs.ts +++ b/packages/core/schematics/migrations/static-queries/angular/directive_inputs.ts @@ -24,7 +24,7 @@ export function getInputNamesOfClass( } const inputDecorator = - getAngularDecorators(typeChecker, m.decorators !).find(d => d.name === 'Input'); + getAngularDecorators(typeChecker, m.decorators!).find(d => d.name === 'Input'); if (inputDecorator && hasPropertyNameText(m.name)) { resolvedInputSetters.push(m.name.text); diff --git a/packages/core/schematics/migrations/static-queries/index.ts b/packages/core/schematics/migrations/static-queries/index.ts index 9200776c437e5..1a4c5ac7e62b8 100644 --- a/packages/core/schematics/migrations/static-queries/index.ts +++ b/packages/core/schematics/migrations/static-queries/index.ts @@ -8,14 +8,13 @@ import {logging} from '@angular-devkit/core'; import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {dirname, relative} from 'path'; +import {relative} from 'path'; import {from} from 'rxjs'; import * as ts from 'typescript'; import {NgComponentTemplateVisitor} from '../../utils/ng_component_template'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; -import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; -import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; import {NgQueryResolveVisitor} from './angular/ng_query_visitor'; import {QueryTemplateStrategy} from './strategies/template_strategy/template_strategy'; @@ -107,49 +106,46 @@ async function runMigration(tree: Tree, context: SchematicContext) { */ function analyzeProject( tree: Tree, tsconfigPath: string, basePath: string, analyzedFiles: Set<string>, - logger: logging.LoggerApi): - AnalyzedProject|null { - const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath); - const program = ts.createProgram(parsed.fileNames, parsed.options, host); - const syntacticDiagnostics = program.getSyntacticDiagnostics(); - - // Syntactic TypeScript errors can throw off the query analysis and therefore we want - // to notify the developer that we couldn't analyze parts of the project. Developers - // can just re-run the migration after fixing these failures. - if (syntacticDiagnostics.length) { - logger.warn( - `\nTypeScript project "${tsconfigPath}" has syntactical errors which could cause ` + - `an incomplete migration. Please fix the following failures and rerun the migration:`); - logger.error(ts.formatDiagnostics(syntacticDiagnostics, host)); - logger.info( - 'Migration can be rerun with: "ng update @angular/core --from 7 --to 8 --migrate-only"\n'); - } + logger: logging.LoggerApi): AnalyzedProject|null { + const {program, host} = createMigrationProgram(tree, tsconfigPath, basePath); + const syntacticDiagnostics = program.getSyntacticDiagnostics(); + + // Syntactic TypeScript errors can throw off the query analysis and therefore we want + // to notify the developer that we couldn't analyze parts of the project. Developers + // can just re-run the migration after fixing these failures. + if (syntacticDiagnostics.length) { + logger.warn( + `\nTypeScript project "${tsconfigPath}" has syntactical errors which could cause ` + + `an incomplete migration. Please fix the following failures and rerun the migration:`); + logger.error(ts.formatDiagnostics(syntacticDiagnostics, host)); + logger.info( + 'Migration can be rerun with: "ng update @angular/core --from 7 --to 8 --migrate-only"\n'); + } - const typeChecker = program.getTypeChecker(); - const sourceFiles = program.getSourceFiles().filter( - f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); - const queryVisitor = new NgQueryResolveVisitor(typeChecker); - - // Analyze all project source-files and collect all queries that - // need to be migrated. - sourceFiles.forEach(sourceFile => { - const relativePath = relative(basePath, sourceFile.fileName); - - // Only look for queries within the current source files if the - // file has not been analyzed before. - if (!analyzedFiles.has(relativePath)) { - analyzedFiles.add(relativePath); - queryVisitor.visitNode(sourceFile); - } - }); - - if (queryVisitor.resolvedQueries.size === 0) { - return null; - } + const typeChecker = program.getTypeChecker(); + const sourceFiles = program.getSourceFiles().filter( + f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); + const queryVisitor = new NgQueryResolveVisitor(typeChecker); - return {program, host, tsconfigPath, typeChecker, basePath, queryVisitor, sourceFiles}; + // Analyze all project source-files and collect all queries that + // need to be migrated. + sourceFiles.forEach(sourceFile => { + const relativePath = relative(basePath, sourceFile.fileName); + + // Only look for queries within the current source files if the + // file has not been analyzed before. + if (!analyzedFiles.has(relativePath)) { + analyzedFiles.add(relativePath); + queryVisitor.visitNode(sourceFile); } + }); + + if (queryVisitor.resolvedQueries.size === 0) { + return null; + } + + return {program, host, tsconfigPath, typeChecker, basePath, queryVisitor, sourceFiles}; +} /** * Runs the static query migration for the given project. The schematic analyzes all @@ -179,7 +175,7 @@ async function runStaticQueryMigration( // is necessary in order to be able to check component templates for static query usage. resolvedTemplates.forEach(template => { if (classMetadata.has(template.container)) { - classMetadata.get(template.container) !.template = template; + classMetadata.get(template.container)!.template = template; } }); } diff --git a/packages/core/schematics/migrations/static-queries/strategies/template_strategy/template_strategy.ts b/packages/core/schematics/migrations/static-queries/strategies/template_strategy/template_strategy.ts index c614961b67332..1414809880b06 100644 --- a/packages/core/schematics/migrations/static-queries/strategies/template_strategy/template_strategy.ts +++ b/packages/core/schematics/migrations/static-queries/strategies/template_strategy/template_strategy.ts @@ -7,7 +7,7 @@ */ import {AotCompiler, CompileDirectiveMetadata, CompileMetadataResolver, CompileNgModuleMetadata, CompileStylesheetMetadata, ElementAst, EmbeddedTemplateAst, NgAnalyzedModules, QueryMatch, StaticSymbol, TemplateAst} from '@angular/compiler'; -import {Diagnostic, createProgram, readConfiguration} from '@angular/compiler-cli'; +import {createProgram, Diagnostic, readConfiguration} from '@angular/compiler-cli'; import {resolve} from 'path'; import * as ts from 'typescript'; @@ -46,17 +46,17 @@ export class QueryTemplateStrategy implements TimingStrategy { // expose the logic that is necessary to analyze the determined modules. We work around // this by just accessing the necessary private properties using the bracket notation. this.compiler = (aotProgram as any)['compiler']; - this.metadataResolver = this.compiler !['_metadataResolver']; + this.metadataResolver = this.compiler!['_metadataResolver']; // Modify the "DirectiveNormalizer" to not normalize any referenced external stylesheets. // This is necessary because in CLI projects preprocessor files are commonly referenced // and we don't want to parse them in order to extract relative style references. This // breaks the analysis of the project because we instantiate a standalone AOT compiler // program which does not contain the custom logic by the Angular CLI Webpack compiler plugin. - const directiveNormalizer = this.metadataResolver !['_directiveNormalizer']; + const directiveNormalizer = this.metadataResolver!['_directiveNormalizer']; directiveNormalizer['_normalizeStylesheet'] = function(metadata: CompileStylesheetMetadata) { return new CompileStylesheetMetadata( - {styles: metadata.styles, styleUrls: [], moduleUrl: metadata.moduleUrl !}); + {styles: metadata.styles, styleUrls: [], moduleUrl: metadata.moduleUrl!}); }; // Retrieves the analyzed modules of the current program. This data can be @@ -75,7 +75,7 @@ export class QueryTemplateStrategy implements TimingStrategy { /** Analyzes a given directive by determining the timing of all matched view queries. */ private _analyzeDirective(symbol: StaticSymbol, analyzedModules: NgAnalyzedModules) { - const metadata = this.metadataResolver !.getDirectiveMetadata(symbol); + const metadata = this.metadataResolver!.getDirectiveMetadata(symbol); const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(symbol); if (!metadata.isComponent || !ngModule) { @@ -168,7 +168,7 @@ export class QueryTemplateStrategy implements TimingStrategy { const queryKey = this._getViewQueryUniqueKey(filePath, classDecl.name.text, queryName); if (this.analyzedQueries.has(queryKey)) { - return this.analyzedQueries.get(queryKey) !; + return this.analyzedQueries.get(queryKey)!; } return null; } @@ -176,7 +176,7 @@ export class QueryTemplateStrategy implements TimingStrategy { private _parseTemplate(component: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata): TemplateAst[] { return this - .compiler !['_parseTemplate'](component, ngModule, ngModule.transitiveModule.directives) + .compiler!['_parseTemplate'](component, ngModule, ngModule.transitiveModule.directives) .template; } @@ -201,11 +201,11 @@ function findStaticQueryIds( nodes.forEach((node) => { const staticQueryIds = new Set<number>(); const dynamicQueryIds = new Set<number>(); - let queryMatches: QueryMatch[] = undefined !; + let queryMatches: QueryMatch[] = undefined!; if (node instanceof ElementAst) { findStaticQueryIds(node.children, result); node.children.forEach((child) => { - const childData = result.get(child) !; + const childData = result.get(child)!; childData.staticQueryIds.forEach(queryId => staticQueryIds.add(queryId)); childData.dynamicQueryIds.forEach(queryId => dynamicQueryIds.add(queryId)); }); @@ -213,7 +213,7 @@ function findStaticQueryIds( } else if (node instanceof EmbeddedTemplateAst) { findStaticQueryIds(node.children, result); node.children.forEach((child) => { - const childData = result.get(child) !; + const childData = result.get(child)!; childData.staticQueryIds.forEach(queryId => dynamicQueryIds.add(queryId)); childData.dynamicQueryIds.forEach(queryId => dynamicQueryIds.add(queryId)); }); diff --git a/packages/core/schematics/migrations/static-queries/strategies/timing-strategy.ts b/packages/core/schematics/migrations/static-queries/strategies/timing-strategy.ts index b530f60055d1b..0c35f6c6e45ee 100644 --- a/packages/core/schematics/migrations/static-queries/strategies/timing-strategy.ts +++ b/packages/core/schematics/migrations/static-queries/strategies/timing-strategy.ts @@ -16,5 +16,6 @@ export interface TimingStrategy { } export type TimingResult = { - timing: QueryTiming | null; message?: string; + timing: QueryTiming|null; + message?: string; }; diff --git a/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/declaration_usage_visitor.ts b/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/declaration_usage_visitor.ts index f42d647005abe..269f901f2c5bb 100644 --- a/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/declaration_usage_visitor.ts +++ b/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/declaration_usage_visitor.ts @@ -167,7 +167,7 @@ export class DeclarationUsageVisitor { d.body && !this.visitedJumpExprNodes.has(d)) .forEach(d => { this.visitedJumpExprNodes.add(d); - this.nodeQueue.push(d.body !); + this.nodeQueue.push(d.body!); }); } @@ -212,7 +212,7 @@ export class DeclarationUsageVisitor { this.ambiguousNodeQueue = []; while (this.nodeQueue.length) { - const node = this.nodeQueue.shift() !; + const node = this.nodeQueue.shift()!; if (ts.isIdentifier(node) && this.isReferringToSymbol(node)) { return ResolvedUsage.SYNCHRONOUS; @@ -306,7 +306,7 @@ export class DeclarationUsageVisitor { } } - jumpExp.arguments !.forEach((node: ts.Node) => { + jumpExp.arguments!.forEach((node: ts.Node) => { node = this._resolveDeclarationOfNode(node); if (ts.isVariableDeclaration(node) && node.initializer) { @@ -325,7 +325,7 @@ export class DeclarationUsageVisitor { */ private _resolveNodeFromContext(node: ts.Node): ts.Node { if (this.context.has(node)) { - return this.context.get(node) !; + return this.context.get(node)!; } return node; } diff --git a/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/super_class_context.ts b/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/super_class_context.ts index b48589e8a112d..d8bfafeedc428 100644 --- a/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/super_class_context.ts +++ b/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/super_class_context.ts @@ -35,7 +35,7 @@ export function updateSuperClassAbstractMembersContext( const baseClassImpl = baseClass.members.find( baseClassMethod => !!baseClassMethod.name && getPropertyNameText(baseClassMethod.name) === - getPropertyNameText(superClassMember.name !)); + getPropertyNameText(superClassMember.name!)); if (!baseClassImpl || !isFunctionLikeDeclaration(baseClassImpl) || !baseClassImpl.body) { return; diff --git a/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/template_usage_visitor.ts b/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/template_usage_visitor.ts index 3cfc79c2d2974..ec7c9b27d30e3 100644 --- a/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/template_usage_visitor.ts +++ b/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/template_usage_visitor.ts @@ -17,7 +17,9 @@ export class TemplateUsageVisitor extends NullVisitor { private hasQueryTemplateReference = false; private expressionAstVisitor = new ExpressionAstVisitor(this.queryPropertyName); - constructor(public queryPropertyName: string) { super(); } + constructor(public queryPropertyName: string) { + super(); + } /** Checks whether the given query is statically accessed within the specified HTML nodes. */ isQueryUsedStatically(htmlNodes: Node[]): boolean { @@ -59,7 +61,9 @@ export class TemplateUsageVisitor extends NullVisitor { attribute.value.visit(this.expressionAstVisitor, attribute.sourceSpan); } - visitBoundText(text: BoundText) { text.value.visit(this.expressionAstVisitor, text.sourceSpan); } + visitBoundText(text: BoundText) { + text.value.visit(this.expressionAstVisitor, text.sourceSpan); + } visitBoundEvent(node: BoundEvent) { node.handler.visit(this.expressionAstVisitor, node.handlerSpan); @@ -73,7 +77,9 @@ export class TemplateUsageVisitor extends NullVisitor { class ExpressionAstVisitor extends RecursiveAstVisitor { hasQueryPropertyRead = false; - constructor(private queryPropertyName: string) { super(); } + constructor(private queryPropertyName: string) { + super(); + } visitPropertyRead(node: PropertyRead, span: ParseSourceSpan): any { // The receiver of the property read needs to be "implicit" as queries are accessed diff --git a/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/usage_strategy.ts b/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/usage_strategy.ts index 81de639b5501d..6085105e9f61f 100644 --- a/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/usage_strategy.ts +++ b/packages/core/schematics/migrations/static-queries/strategies/usage_strategy/usage_strategy.ts @@ -70,7 +70,7 @@ export class QueryUsageStrategy implements TimingStrategy { classDecl: ts.ClassDeclaration, query: NgQueryDefinition, knownInputNames: string[], functionCtx: FunctionContext = new Map(), visitInheritedClasses = true): ResolvedUsage { const usageVisitor = - new DeclarationUsageVisitor(query.property !, this.typeChecker, functionCtx); + new DeclarationUsageVisitor(query.property!, this.typeChecker, functionCtx); const classMetadata = this.classMetadata.get(classDecl); let usage: ResolvedUsage = ResolvedUsage.ASYNCHRONOUS; @@ -99,10 +99,10 @@ export class QueryUsageStrategy implements TimingStrategy { // In case there is a component template for the current class, we check if the // template statically accesses the current query. In case that's true, the query // can be marked as static. - if (classMetadata.template && hasPropertyNameText(query.property !.name)) { + if (classMetadata.template && hasPropertyNameText(query.property!.name)) { const template = classMetadata.template; const parsedHtml = parseHtmlGracefully(template.content, template.filePath); - const htmlVisitor = new TemplateUsageVisitor(query.property !.name.text); + const htmlVisitor = new TemplateUsageVisitor(query.property!.name.text); if (parsedHtml && htmlVisitor.isQueryUsedStatically(parsedHtml)) { return ResolvedUsage.SYNCHRONOUS; @@ -179,5 +179,5 @@ function filterQueryClassMemberNodes( } return false; }) - .map(member => member.body !); + .map(member => member.body!); } diff --git a/packages/core/schematics/migrations/static-queries/transform.ts b/packages/core/schematics/migrations/static-queries/transform.ts index f60875799aade..379e64bba89f4 100644 --- a/packages/core/schematics/migrations/static-queries/transform.ts +++ b/packages/core/schematics/migrations/static-queries/transform.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {getPropertyNameText} from '../../utils/typescript/property_name'; import {NgQueryDefinition, QueryTiming} from './angular/query-definition'; -export type TransformedQueryResult = null | { +export type TransformedQueryResult = null|{ /** Transformed call expression. */ node: ts.CallExpression; /** Failure message which is set when the query could not be transformed successfully. */ @@ -25,7 +25,7 @@ const TODO_CHECK_COMMENT = 'TODO: check static flag'; * determined timing. The updated decorator call expression node will be returned. */ export function getTransformedQueryCallExpr( - query: NgQueryDefinition, timing: QueryTiming | null, + query: NgQueryDefinition, timing: QueryTiming|null, createTodo: boolean): TransformedQueryResult { const queryExpr = query.decorator.node.expression; const queryArguments = queryExpr.arguments; @@ -81,7 +81,7 @@ export function getTransformedQueryCallExpr( failureMessage, node: ts.updateCall( queryExpr, queryExpr.expression, queryExpr.typeArguments, - [queryArguments[0], newOptionsNode !]) + [queryArguments[0], newOptionsNode!]) }; } @@ -94,8 +94,7 @@ export function getTransformedQueryCallExpr( return { failureMessage: null, node: ts.updateCall( - queryExpr, queryExpr.expression, queryExpr.typeArguments, - [queryArguments[0], optionsNode]) + queryExpr, queryExpr.expression, queryExpr.typeArguments, [queryArguments[0], optionsNode]) }; } diff --git a/packages/core/schematics/migrations/template-var-assignment/index.ts b/packages/core/schematics/migrations/template-var-assignment/index.ts index 464841c158f9d..b7eeede2428f2 100644 --- a/packages/core/schematics/migrations/template-var-assignment/index.ts +++ b/packages/core/schematics/migrations/template-var-assignment/index.ts @@ -8,13 +8,11 @@ import {logging, normalize} from '@angular-devkit/core'; import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {dirname, relative} from 'path'; -import * as ts from 'typescript'; +import {relative} from 'path'; import {NgComponentTemplateVisitor} from '../../utils/ng_component_template'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; -import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; -import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; import {analyzeResolvedTemplate} from './analyze_template'; @@ -47,9 +45,7 @@ export default function(): Rule { */ function runTemplateVariableAssignmentCheck( tree: Tree, tsconfigPath: string, basePath: string, logger: Logger) { - const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath); - const program = ts.createProgram(parsed.fileNames, parsed.options, host); + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); const typeChecker = program.getTypeChecker(); const templateVisitor = new NgComponentTemplateVisitor(typeChecker); const sourceFiles = program.getSourceFiles().filter( diff --git a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/BUILD.bazel b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/BUILD.bazel index 3bfb1119043cf..040810b57e7c2 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/BUILD.bazel +++ b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/BUILD.bazel @@ -7,9 +7,12 @@ ts_library( visibility = [ "//packages/core/schematics:__pkg__", "//packages/core/schematics/migrations/google3:__pkg__", + "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3:__pkg__", "//packages/core/schematics/test:__pkg__", ], deps = [ + "//packages/compiler-cli/src/ngtsc/partial_evaluator", + "//packages/compiler-cli/src/ngtsc/reflection", "//packages/core/schematics/utils", "@npm//@angular-devkit/schematics", "@npm//@types/node", diff --git a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3/BUILD.bazel b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3/BUILD.bazel new file mode 100644 index 0000000000000..8950f5665253e --- /dev/null +++ b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3/BUILD.bazel @@ -0,0 +1,13 @@ +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "google3", + srcs = glob(["**/*.ts"]), + tsconfig = "//packages/core/schematics:tsconfig.json", + visibility = ["//packages/core/schematics/migrations/google3:__pkg__"], + deps = [ + "//packages/core/schematics/migrations/undecorated-classes-with-decorated-fields", + "@npm//tslint", + "@npm//typescript", + ], +) diff --git a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3/tslint_update_recorder.ts b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3/tslint_update_recorder.ts new file mode 100644 index 0000000000000..311bdb15e869f --- /dev/null +++ b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/google3/tslint_update_recorder.ts @@ -0,0 +1,50 @@ +/** + * @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 {Replacement, RuleFailure} from 'tslint'; +import * as ts from 'typescript'; + +import {UpdateRecorder} from '../update_recorder'; + +export class TslintUpdateRecorder implements UpdateRecorder { + failures: RuleFailure[] = []; + + constructor(private ruleName: string, private sourceFile: ts.SourceFile) {} + + /** Adds the specified decorator to the given class declaration. */ + addClassDecorator(node: ts.ClassDeclaration, decoratorText: string) { + // Adding a decorator should be the last replacement. Replacements/rule failures + // are handled in reverse and in case a decorator and import are inserted at + // the start of the file, the class decorator should come after the import. + this.failures.unshift(new RuleFailure( + this.sourceFile, node.getStart(), 0, + `Class needs to be decorated with ` + + `"${decoratorText}" because it uses Angular features.`, + this.ruleName, Replacement.appendText(node.getStart(), `${decoratorText}\n`))); + } + + /** Adds the specified import to the source file at the given position */ + addNewImport(start: number, importText: string) { + this.failures.push(new RuleFailure( + this.sourceFile, start, 0, `Source file needs to have import: "${importText}"`, + this.ruleName, Replacement.appendText(start, importText))); + } + + /** Updates existing named imports to the given new named imports. */ + updateExistingImport(namedBindings: ts.NamedImports, newNamedBindings: string): void { + const fix = [ + Replacement.deleteText(namedBindings.getStart(), namedBindings.getWidth()), + Replacement.appendText(namedBindings.getStart(), newNamedBindings), + ]; + this.failures.push(new RuleFailure( + this.sourceFile, namedBindings.getStart(), namedBindings.getEnd(), + `Import needs to be updated to import symbols: "${newNamedBindings}"`, this.ruleName, fix)); + } + + commitUpdate() {} +} diff --git a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/index.ts b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/index.ts index 87dafe4f60a54..7b9698b1dbe92 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/index.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/index.ts @@ -6,21 +6,22 @@ * found in the LICENSE file at https://angular.io/license */ -import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; -import {dirname, relative} from 'path'; +import {Rule, SchematicsException, Tree,} from '@angular-devkit/schematics'; +import {relative} from 'path'; import * as ts from 'typescript'; + import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; -import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; -import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; -import {FALLBACK_DECORATOR, addImport, getNamedImports, getUndecoratedClassesWithDecoratedFields, hasNamedImport} from './utils'; +import {createMigrationProgram} from '../../utils/typescript/compiler_host'; +import {UndecoratedClassesWithDecoratedFieldsTransform} from './transform'; +import {UpdateRecorder} from './update_recorder'; /** * Migration that adds an Angular decorator to classes that have Angular field decorators. * https://hackmd.io/vuQfavzfRG6KUCtU7oK_EA */ export default function(): Rule { - return (tree: Tree, context: SchematicContext) => { + return (tree: Tree) => { const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); const basePath = process.cwd(); const allPaths = [...buildPaths, ...testPaths]; @@ -37,43 +38,53 @@ export default function(): Rule { } function runUndecoratedClassesMigration(tree: Tree, tsconfigPath: string, basePath: string) { - const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = createMigrationCompilerHost(tree, parsed.options, basePath); - const program = ts.createProgram(parsed.fileNames, parsed.options, host); + const {program} = createMigrationProgram(tree, tsconfigPath, basePath); const typeChecker = program.getTypeChecker(); - const printer = ts.createPrinter(); const sourceFiles = program.getSourceFiles().filter( file => !file.isDeclarationFile && !program.isSourceFileFromExternalLibrary(file)); + const updateRecorders = new Map<ts.SourceFile, UpdateRecorder>(); + const transform = + new UndecoratedClassesWithDecoratedFieldsTransform(typeChecker, getUpdateRecorder); - sourceFiles.forEach(sourceFile => { - const classes = getUndecoratedClassesWithDecoratedFields(sourceFile, typeChecker); - - if (classes.length === 0) { - return; - } + // Migrate all source files in the project. + transform.migrate(sourceFiles); - const update = tree.beginUpdate(relative(basePath, sourceFile.fileName)); + // Record the changes collected in the import manager. + transform.recordChanges(); - classes.forEach((current, index) => { - // If it's the first class that we're processing in this file, add `Directive` to the imports. - if (index === 0 && !hasNamedImport(current.importDeclaration, FALLBACK_DECORATOR)) { - const namedImports = getNamedImports(current.importDeclaration); + // Walk through each update recorder and commit the update. We need to commit the + // updates in batches per source file as there can be only one recorder per source + // file in order to avoid shifted character offsets. + updateRecorders.forEach(recorder => recorder.commitUpdate()); - if (namedImports) { - update.remove(namedImports.getStart(), namedImports.getWidth()); - update.insertRight( - namedImports.getStart(), - printer.printNode( - ts.EmitHint.Unspecified, addImport(namedImports, FALLBACK_DECORATOR), - sourceFile)); - } + /** Gets the update recorder for the specified source file. */ + function getUpdateRecorder(sourceFile: ts.SourceFile): UpdateRecorder { + if (updateRecorders.has(sourceFile)) { + return updateRecorders.get(sourceFile)!; + } + const treeRecorder = tree.beginUpdate(relative(basePath, sourceFile.fileName)); + const recorder: UpdateRecorder = { + addClassDecorator(node: ts.ClassDeclaration, text: string) { + // New imports should be inserted at the left while decorators should be inserted + // at the right in order to ensure that imports are inserted before the decorator + // if the start position of import and decorator is the source file start. + treeRecorder.insertRight(node.getStart(), `${text}\n`); + }, + addNewImport(start: number, importText: string) { + // New imports should be inserted at the left while decorators should be inserted + // at the right in order to ensure that imports are inserted before the decorator + // if the start position of import and decorator is the source file start. + treeRecorder.insertLeft(start, importText); + }, + updateExistingImport(namedBindings: ts.NamedImports, newNamedBindings: string) { + treeRecorder.remove(namedBindings.getStart(), namedBindings.getWidth()); + treeRecorder.insertRight(namedBindings.getStart(), newNamedBindings); + }, + commitUpdate() { + tree.commitUpdate(treeRecorder); } - - // We don't need to go through the AST to insert the decorator, because the change - // is pretty basic. Also this has a better chance of preserving the user's formatting. - update.insertLeft(current.classDeclaration.getStart(), `@${FALLBACK_DECORATOR}()\n`); - }); - - tree.commitUpdate(update); - }); + }; + updateRecorders.set(sourceFile, recorder); + return recorder; + } } diff --git a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/transform.ts b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/transform.ts new file mode 100644 index 0000000000000..012ff0c43a0d9 --- /dev/null +++ b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/transform.ts @@ -0,0 +1,159 @@ +/** + * @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 {PartialEvaluator} from '@angular/compiler-cli/src/ngtsc/partial_evaluator'; +import {reflectObjectLiteral, TypeScriptReflectionHost} from '@angular/compiler-cli/src/ngtsc/reflection'; +import * as ts from 'typescript'; + +import {ImportManager} from '../../utils/import_manager'; +import {getAngularDecorators, NgDecorator} from '../../utils/ng_decorators'; +import {findBaseClassDeclarations} from '../../utils/typescript/find_base_classes'; +import {unwrapExpression} from '../../utils/typescript/functions'; + +import {UpdateRecorder} from './update_recorder'; + + +/** Analyzed class declaration. */ +interface AnalyzedClass { + /** Whether the class is decorated with @Directive or @Component. */ + isDirectiveOrComponent: boolean; + /** Whether the class is an abstract directive. */ + isAbstractDirective: boolean; + /** Whether the class uses any Angular features. */ + usesAngularFeatures: boolean; +} + +export class UndecoratedClassesWithDecoratedFieldsTransform { + private printer = ts.createPrinter(); + private importManager = new ImportManager(this.getUpdateRecorder, this.printer); + private reflectionHost = new TypeScriptReflectionHost(this.typeChecker); + private partialEvaluator = new PartialEvaluator(this.reflectionHost, this.typeChecker, null); + + constructor( + private typeChecker: ts.TypeChecker, + private getUpdateRecorder: (sf: ts.SourceFile) => UpdateRecorder) {} + + /** + * Migrates the specified source files. The transform adds the abstract `@Directive` + * decorator to classes that have Angular field decorators but are not decorated. + * https://hackmd.io/vuQfavzfRG6KUCtU7oK_EA + */ + migrate(sourceFiles: ts.SourceFile[]) { + this._findUndecoratedAbstractDirectives(sourceFiles).forEach(node => { + const sourceFile = node.getSourceFile(); + const recorder = this.getUpdateRecorder(sourceFile); + const directiveExpr = + this.importManager.addImportToSourceFile(sourceFile, 'Directive', '@angular/core'); + const decoratorExpr = ts.createDecorator(ts.createCall(directiveExpr, undefined, undefined)); + recorder.addClassDecorator( + node, this.printer.printNode(ts.EmitHint.Unspecified, decoratorExpr, sourceFile)); + }); + } + + /** Records all changes that were made in the import manager. */ + recordChanges() { + this.importManager.recordChanges(); + } + + /** Finds undecorated abstract directives in the specified source files. */ + private _findUndecoratedAbstractDirectives(sourceFiles: ts.SourceFile[]) { + const result = new Set<ts.ClassDeclaration>(); + const undecoratedClasses = new Set<ts.ClassDeclaration>(); + const nonAbstractDirectives = new WeakSet<ts.ClassDeclaration>(); + const abstractDirectives = new WeakSet<ts.ClassDeclaration>(); + + const visitNode = (node: ts.Node) => { + node.forEachChild(visitNode); + if (!ts.isClassDeclaration(node)) { + return; + } + const {isDirectiveOrComponent, isAbstractDirective, usesAngularFeatures} = + this._analyzeClassDeclaration(node); + if (isDirectiveOrComponent) { + if (isAbstractDirective) { + abstractDirectives.add(node); + } else { + nonAbstractDirectives.add(node); + } + } else if (usesAngularFeatures) { + abstractDirectives.add(node); + result.add(node); + } else { + undecoratedClasses.add(node); + } + }; + + sourceFiles.forEach(sourceFile => sourceFile.forEachChild(visitNode)); + + // We collected all undecorated class declarations which inherit from abstract directives. + // For such abstract directives, the derived classes also need to be migrated. + undecoratedClasses.forEach(node => { + for (const {node: baseClass} of findBaseClassDeclarations(node, this.typeChecker)) { + // If the undecorated class inherits from a non-abstract directive, skip the current + // class. We do this because undecorated classes which inherit metadata from non-abstract + // directives are handle in the `undecorated-classes-with-di` migration that copies + // inherited metadata into an explicit decorator. + if (nonAbstractDirectives.has(baseClass)) { + break; + } else if (abstractDirectives.has(baseClass)) { + result.add(node); + break; + } + } + }); + + return result; + } + + /** + * Analyzes the given class declaration by determining whether the class + * is a directive, is an abstract directive, or uses Angular features. + */ + private _analyzeClassDeclaration(node: ts.ClassDeclaration): AnalyzedClass { + const ngDecorators = node.decorators && getAngularDecorators(this.typeChecker, node.decorators); + const usesAngularFeatures = this._hasAngularDecoratedClassMember(node); + if (ngDecorators === undefined || ngDecorators.length === 0) { + return {isDirectiveOrComponent: false, isAbstractDirective: false, usesAngularFeatures}; + } + const directiveDecorator = ngDecorators.find(({name}) => name === 'Directive'); + const componentDecorator = ngDecorators.find(({name}) => name === 'Component'); + const isAbstractDirective = + directiveDecorator !== undefined && this._isAbstractDirective(directiveDecorator); + return { + isDirectiveOrComponent: !!directiveDecorator || !!componentDecorator, + isAbstractDirective, + usesAngularFeatures, + }; + } + + /** + * Checks whether the given decorator resolves to an abstract directive. An directive is + * considered "abstract" if there is no selector specified. + */ + private _isAbstractDirective({node}: NgDecorator): boolean { + const metadataArgs = node.expression.arguments; + if (metadataArgs.length === 0) { + return true; + } + const metadataExpr = unwrapExpression(metadataArgs[0]); + if (!ts.isObjectLiteralExpression(metadataExpr)) { + return false; + } + const metadata = reflectObjectLiteral(metadataExpr); + if (!metadata.has('selector')) { + return false; + } + const selector = this.partialEvaluator.evaluate(metadata.get('selector')!); + return selector == null; + } + + private _hasAngularDecoratedClassMember(node: ts.ClassDeclaration): boolean { + return node.members.some( + m => m.decorators && getAngularDecorators(this.typeChecker, m.decorators).length !== 0); + } +} diff --git a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/update_recorder.ts b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/update_recorder.ts new file mode 100644 index 0000000000000..7568256c35362 --- /dev/null +++ b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/update_recorder.ts @@ -0,0 +1,19 @@ +/** + * @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 * as ts from 'typescript'; +import {ImportManagerUpdateRecorder} from '../../utils/import_manager'; + +/** + * Update recorder interface that is used to transform source files + * in a non-colliding way. + */ +export interface UpdateRecorder extends ImportManagerUpdateRecorder { + addClassDecorator(node: ts.ClassDeclaration, text: string): void; + commitUpdate(): void; +} diff --git a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/utils.ts b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/utils.ts deleted file mode 100644 index 746f8676d68c5..0000000000000 --- a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/utils.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * @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 * as ts from 'typescript'; -import {getAngularDecorators} from '../../utils/ng_decorators'; - -/** Name of the decorator that should be added to undecorated classes. */ -export const FALLBACK_DECORATOR = 'Directive'; - -/** Finds all of the undecorated classes that have decorated fields within a file. */ -export function getUndecoratedClassesWithDecoratedFields( - sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { - const classes: UndecoratedClassWithDecoratedFields[] = []; - - sourceFile.forEachChild(function walk(node: ts.Node) { - if (ts.isClassDeclaration(node) && - (!node.decorators || !getAngularDecorators(typeChecker, node.decorators).length)) { - for (const member of node.members) { - const angularDecorators = - member.decorators && getAngularDecorators(typeChecker, member.decorators); - - if (angularDecorators && angularDecorators.length) { - classes.push( - {classDeclaration: node, importDeclaration: angularDecorators[0].importNode}); - return; - } - } - } - - node.forEachChild(walk); - }); - - return classes; -} - -/** Checks whether an import declaration has an import with a certain name. */ -export function hasNamedImport(declaration: ts.ImportDeclaration, symbolName: string): boolean { - const namedImports = getNamedImports(declaration); - - if (namedImports) { - return namedImports.elements.some(element => { - const {name, propertyName} = element; - return propertyName ? propertyName.text === symbolName : name.text === symbolName; - }); - } - - return false; -} - -/** Extracts the NamedImports node from an import declaration. */ -export function getNamedImports(declaration: ts.ImportDeclaration): ts.NamedImports|null { - const namedBindings = declaration.importClause && declaration.importClause.namedBindings; - return (namedBindings && ts.isNamedImports(namedBindings)) ? namedBindings : null; -} - -/** Adds a new import to a NamedImports node. */ -export function addImport(declaration: ts.NamedImports, symbolName: string) { - return ts.updateNamedImports(declaration, [ - ...declaration.elements, ts.createImportSpecifier(undefined, ts.createIdentifier(symbolName)) - ]); -} - -interface UndecoratedClassWithDecoratedFields { - classDeclaration: ts.ClassDeclaration; - importDeclaration: ts.ImportDeclaration; -} diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/convert_directive_metadata.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/convert_directive_metadata.ts index aef0570b178af..faa6ca4bf7053 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/convert_directive_metadata.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/convert_directive_metadata.ts @@ -78,7 +78,7 @@ export function convertDirectiveMetadataToExpression( /** * Gets a valid property name from the given text. If the text cannot be used * as unquoted identifier, the name will be wrapped in a string literal. -*/ + */ function getPropertyName(name: string): string|ts.StringLiteral { // Matches the most common identifiers that do not need quotes. Constructing a // regular expression that matches the ECMAScript specification in order to determine diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/decorator_rewriter.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/decorator_rewriter.ts index f2b2c4cf3244d..1767ccd3451b4 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/decorator_rewriter.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/decorator_rewriter.ts @@ -10,12 +10,13 @@ import {AotCompiler} from '@angular/compiler'; import {PartialEvaluator} from '@angular/compiler-cli/src/ngtsc/partial_evaluator'; import * as ts from 'typescript'; +import {ImportManager} from '../../../utils/import_manager'; import {NgDecorator} from '../../../utils/ng_decorators'; import {unwrapExpression} from '../../../utils/typescript/functions'; -import {ImportManager} from '../import_manager'; import {ImportRewriteTransformerFactory, UnresolvedIdentifierError} from './import_rewrite_visitor'; + /** * Class that can be used to copy decorators to a new location. The rewriter ensures that * identifiers and imports are rewritten to work in the new file location. Fields in a @@ -121,7 +122,7 @@ export class DecoratorRewriter { |null { try { return ts - .transform(prop, [ctx => this.importRewriterFactory.create(ctx, this.newSourceFile !)]) + .transform(prop, [ctx => this.importRewriterFactory.create(ctx, this.newSourceFile!)]) .transformed[0]; } catch (e) { // If the error is for an unresolved identifier, we want to return "null" because diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/import_rewrite_visitor.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/import_rewrite_visitor.ts index 417687615885c..1cbc50aed6f1e 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/import_rewrite_visitor.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/decorator_rewrite/import_rewrite_visitor.ts @@ -10,12 +10,13 @@ import {AotCompilerHost} from '@angular/compiler'; import {dirname, resolve} from 'path'; import * as ts from 'typescript'; -import {Import, getImportOfIdentifier} from '../../../utils/typescript/imports'; +import {ImportManager} from '../../../utils/import_manager'; +import {getImportOfIdentifier, Import} from '../../../utils/typescript/imports'; import {getValueSymbolOfDeclaration} from '../../../utils/typescript/symbol'; -import {ImportManager} from '../import_manager'; import {getPosixPath} from './path_format'; -import {ResolvedExport, getExportSymbolsOfFile} from './source_file_exports'; +import {getExportSymbolsOfFile, ResolvedExport} from './source_file_exports'; + /** * Factory that creates a TypeScript transformer which ensures that @@ -115,7 +116,7 @@ export class ImportRewriteTransformerFactory { */ private _getSourceFileExports(sourceFile: ts.SourceFile): ResolvedExport[] { if (this.sourceFileExports.has(sourceFile)) { - return this.sourceFileExports.get(sourceFile) !; + return this.sourceFileExports.get(sourceFile)!; } const sourceFileExports = getExportSymbolsOfFile(sourceFile, this.typeChecker); diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/import_manager.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/import_manager.ts deleted file mode 100644 index 8bc46eca1f9ff..0000000000000 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/import_manager.ts +++ /dev/null @@ -1,254 +0,0 @@ -/** - * @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 {dirname, resolve} from 'path'; -import * as ts from 'typescript'; -import {UpdateRecorder} from './update_recorder'; - -/** - * Import manager that can be used to add TypeScript imports to given source - * files. The manager ensures that multiple transformations are applied properly - * without shifted offsets and that similar existing import declarations are re-used. - */ -export class ImportManager { - /** Map of import declarations that need to be updated to include the given symbols. */ - private updatedImports = - new Map<ts.ImportDeclaration, {propertyName?: ts.Identifier, importName: ts.Identifier}[]>(); - /** Map of source-files and their previously used identifier names. */ - private usedIdentifierNames = new Map<ts.SourceFile, string[]>(); - /** - * Array of previously resolved symbol imports. Cache can be re-used to return - * the same identifier without checking the source-file again. - */ - private importCache: { - sourceFile: ts.SourceFile, - symbolName: string|null, - moduleName: string, - identifier: ts.Identifier - }[] = []; - - constructor( - private getUpdateRecorder: (sf: ts.SourceFile) => UpdateRecorder, - private printer: ts.Printer) {} - - /** - * Adds an import to the given source-file and returns the TypeScript - * identifier that can be used to access the newly imported symbol. - */ - addImportToSourceFile( - sourceFile: ts.SourceFile, symbolName: string|null, moduleName: string, - typeImport = false): ts.Expression { - const sourceDir = dirname(sourceFile.fileName); - let importStartIndex = 0; - let existingImport: ts.ImportDeclaration|null = null; - - // In case the given import has been already generated previously, we just return - // the previous generated identifier in order to avoid duplicate generated imports. - const cachedImport = this.importCache.find( - c => c.sourceFile === sourceFile && c.symbolName === symbolName && - c.moduleName === moduleName); - if (cachedImport) { - return cachedImport.identifier; - } - - // Walk through all source-file top-level statements and search for import declarations - // that already match the specified "moduleName" and can be updated to import the - // given symbol. If no matching import can be found, the last import in the source-file - // will be used as starting point for a new import that will be generated. - for (let i = sourceFile.statements.length - 1; i >= 0; i--) { - const statement = sourceFile.statements[i]; - - if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || - !statement.importClause) { - continue; - } - - if (importStartIndex === 0) { - importStartIndex = this._getEndPositionOfNode(statement); - } - - const moduleSpecifier = statement.moduleSpecifier.text; - - if (moduleSpecifier.startsWith('.') && - resolve(sourceDir, moduleSpecifier) !== resolve(sourceDir, moduleName) || - moduleSpecifier !== moduleName) { - continue; - } - - if (statement.importClause.namedBindings) { - const namedBindings = statement.importClause.namedBindings; - - // In case a "Type" symbol is imported, we can't use namespace imports - // because these only export symbols available at runtime (no types) - if (ts.isNamespaceImport(namedBindings) && !typeImport) { - return ts.createPropertyAccess( - ts.createIdentifier(namedBindings.name.text), - ts.createIdentifier(symbolName || 'default')); - } else if (ts.isNamedImports(namedBindings) && symbolName) { - const existingElement = namedBindings.elements.find( - e => - e.propertyName ? e.propertyName.text === symbolName : e.name.text === symbolName); - - if (existingElement) { - return ts.createIdentifier(existingElement.name.text); - } - - // In case the symbol could not be found in an existing import, we - // keep track of the import declaration as it can be updated to include - // the specified symbol name without having to create a new import. - existingImport = statement; - } - } else if (statement.importClause.name && !symbolName) { - return ts.createIdentifier(statement.importClause.name.text); - } - } - - if (existingImport) { - const propertyIdentifier = ts.createIdentifier(symbolName !); - const generatedUniqueIdentifier = this._getUniqueIdentifier(sourceFile, symbolName !); - const needsGeneratedUniqueName = generatedUniqueIdentifier.text !== symbolName; - const importName = needsGeneratedUniqueName ? generatedUniqueIdentifier : propertyIdentifier; - - // Since it can happen that multiple classes need to be imported within the - // specified source file and we want to add the identifiers to the existing - // import declaration, we need to keep track of the updated import declarations. - // We can't directly update the import declaration for each identifier as this - // would throw off the recorder offsets. We need to keep track of the new identifiers - // for the import and perform the import transformation as batches per source-file. - this.updatedImports.set( - existingImport, (this.updatedImports.get(existingImport) || []).concat({ - propertyName: needsGeneratedUniqueName ? propertyIdentifier : undefined, - importName: importName, - })); - - // Keep track of all updated imports so that we don't generate duplicate - // similar imports as these can't be statically analyzed in the source-file yet. - this.importCache.push({sourceFile, moduleName, symbolName, identifier: importName}); - - return importName; - } - - let identifier: ts.Identifier|null = null; - let newImport: ts.ImportDeclaration|null = null; - - if (symbolName) { - const propertyIdentifier = ts.createIdentifier(symbolName); - const generatedUniqueIdentifier = this._getUniqueIdentifier(sourceFile, symbolName); - const needsGeneratedUniqueName = generatedUniqueIdentifier.text !== symbolName; - identifier = needsGeneratedUniqueName ? generatedUniqueIdentifier : propertyIdentifier; - - newImport = ts.createImportDeclaration( - undefined, undefined, - ts.createImportClause( - undefined, - ts.createNamedImports([ts.createImportSpecifier( - needsGeneratedUniqueName ? propertyIdentifier : undefined, identifier)])), - ts.createStringLiteral(moduleName)); - } else { - identifier = this._getUniqueIdentifier(sourceFile, 'defaultExport'); - newImport = ts.createImportDeclaration( - undefined, undefined, ts.createImportClause(identifier, undefined), - ts.createStringLiteral(moduleName)); - } - - const newImportText = this.printer.printNode(ts.EmitHint.Unspecified, newImport, sourceFile); - // If the import is generated at the start of the source file, we want to add - // a new-line after the import. Otherwise if the import is generated after an - // existing import, we need to prepend a new-line so that the import is not on - // the same line as the existing import anchor. - this.getUpdateRecorder(sourceFile) - .addNewImport( - importStartIndex, importStartIndex === 0 ? `${newImportText}\n` : `\n${newImportText}`); - - // Keep track of all generated imports so that we don't generate duplicate - // similar imports as these can't be statically analyzed in the source-file yet. - this.importCache.push({sourceFile, symbolName, moduleName, identifier}); - - return identifier; - } - - /** - * Stores the collected import changes within the appropriate update recorders. The - * updated imports can only be updated *once* per source-file because previous updates - * could otherwise shift the source-file offsets. - */ - recordChanges() { - this.updatedImports.forEach((expressions, importDecl) => { - const sourceFile = importDecl.getSourceFile(); - const recorder = this.getUpdateRecorder(sourceFile); - const namedBindings = importDecl.importClause !.namedBindings as ts.NamedImports; - const newNamedBindings = ts.updateNamedImports( - namedBindings, - namedBindings.elements.concat(expressions.map( - ({propertyName, importName}) => ts.createImportSpecifier(propertyName, importName)))); - - const newNamedBindingsText = - this.printer.printNode(ts.EmitHint.Unspecified, newNamedBindings, sourceFile); - recorder.updateExistingImport(namedBindings, newNamedBindingsText); - }); - } - - /** Gets an unique identifier with a base name for the given source file. */ - private _getUniqueIdentifier(sourceFile: ts.SourceFile, baseName: string): ts.Identifier { - if (this.isUniqueIdentifierName(sourceFile, baseName)) { - this._recordUsedIdentifier(sourceFile, baseName); - return ts.createIdentifier(baseName); - } - - let name = null; - let counter = 1; - do { - name = `${baseName}_${counter++}`; - } while (!this.isUniqueIdentifierName(sourceFile, name)); - - this._recordUsedIdentifier(sourceFile, name !); - return ts.createIdentifier(name !); - } - - /** - * Checks whether the specified identifier name is used within the given - * source file. - */ - private isUniqueIdentifierName(sourceFile: ts.SourceFile, name: string) { - if (this.usedIdentifierNames.has(sourceFile) && - this.usedIdentifierNames.get(sourceFile) !.indexOf(name) !== -1) { - return false; - } - - // Walk through the source file and search for an identifier matching - // the given name. In that case, it's not guaranteed that this name - // is unique in the given declaration scope and we just return false. - const nodeQueue: ts.Node[] = [sourceFile]; - while (nodeQueue.length) { - const node = nodeQueue.shift() !; - if (ts.isIdentifier(node) && node.text === name) { - return false; - } - nodeQueue.push(...node.getChildren()); - } - return true; - } - - private _recordUsedIdentifier(sourceFile: ts.SourceFile, identifierName: string) { - this.usedIdentifierNames.set( - sourceFile, (this.usedIdentifierNames.get(sourceFile) || []).concat(identifierName)); - } - - /** - * Determines the full end of a given node. By default the end position of a node is - * before all trailing comments. This could mean that generated imports shift comments. - */ - private _getEndPositionOfNode(node: ts.Node) { - const nodeEndPos = node.getEnd(); - const commentRanges = ts.getTrailingCommentRanges(node.getSourceFile().text, nodeEndPos); - if (!commentRanges || !commentRanges.length) { - return nodeEndPos; - } - return commentRanges[commentRanges.length - 1] !.end; - } -} diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/index.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/index.ts index ceef1fbc4d119..6ad2f725d9399 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/index.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/index.ts @@ -123,7 +123,7 @@ function runUndecoratedClassesMigration( /** Gets the update recorder for the specified source file. */ function getUpdateRecorder(sourceFile: ts.SourceFile): UpdateRecorder { if (updateRecorders.has(sourceFile)) { - return updateRecorders.get(sourceFile) !; + return updateRecorders.get(sourceFile)!; } const treeRecorder = tree.beginUpdate(relative(basePath, sourceFile.fileName)); const recorder: UpdateRecorder = { @@ -146,7 +146,9 @@ function runUndecoratedClassesMigration( treeRecorder.remove(namedBindings.getStart(), namedBindings.getWidth()); treeRecorder.insertRight(namedBindings.getStart(), newNamedBindings); }, - commitUpdate() { tree.commitUpdate(treeRecorder); } + commitUpdate() { + tree.commitUpdate(treeRecorder); + } }; updateRecorders.set(sourceFile, recorder); return recorder; diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/ng_declaration_collector.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/ng_declaration_collector.ts index f0c770cbda18b..e6d569c28bed9 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/ng_declaration_collector.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/ng_declaration_collector.ts @@ -10,7 +10,7 @@ import {Reference} from '@angular/compiler-cli/src/ngtsc/imports'; import {PartialEvaluator, ResolvedValue} from '@angular/compiler-cli/src/ngtsc/partial_evaluator'; import * as ts from 'typescript'; -import {NgDecorator, getAngularDecorators} from '../../utils/ng_decorators'; +import {getAngularDecorators, NgDecorator} from '../../utils/ng_decorators'; import {getPropertyNameText} from '../../utils/typescript/property_name'; diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/transform.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/transform.ts index 32baa7d1b9e60..f6857adde714f 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/transform.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/transform.ts @@ -11,14 +11,14 @@ import {PartialEvaluator} from '@angular/compiler-cli/src/ngtsc/partial_evaluato import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core'; import * as ts from 'typescript'; +import {ImportManager} from '../../utils/import_manager'; import {getAngularDecorators} from '../../utils/ng_decorators'; import {hasExplicitConstructor} from '../../utils/typescript/class_declaration'; +import {findBaseClassDeclarations} from '../../utils/typescript/find_base_classes'; import {getImportOfIdentifier} from '../../utils/typescript/imports'; -import {UnexpectedMetadataValueError, convertDirectiveMetadataToExpression} from './decorator_rewrite/convert_directive_metadata'; +import {convertDirectiveMetadataToExpression, UnexpectedMetadataValueError} from './decorator_rewrite/convert_directive_metadata'; import {DecoratorRewriter} from './decorator_rewrite/decorator_rewriter'; -import {findBaseClassDeclarations} from './find_base_classes'; -import {ImportManager} from './import_manager'; import {hasDirectiveDecorator, hasInjectableDecorator} from './ng_declaration_collector'; import {UpdateRecorder} from './update_recorder'; @@ -316,7 +316,9 @@ export class UndecoratedClassesTransform { } /** Records all changes that were made in the import manager. */ - recordChanges() { this.importManager.recordChanges(); } + recordChanges() { + this.importManager.recordChanges(); + } /** * Constructs a TypeScript decorator node from the specified declaration metadata. Returns diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/update_recorder.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/update_recorder.ts index baa341ecce2f8..ed5ee2b2a942d 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/update_recorder.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/update_recorder.ts @@ -8,15 +8,14 @@ */ import * as ts from 'typescript'; +import {ImportManagerUpdateRecorder} from '../../utils/import_manager'; /** * Update recorder interface that is used to transform source files in a non-colliding * way. Also this indirection makes it possible to re-use transformation logic with * different replacement tools (e.g. TSLint or CLI devkit). */ -export interface UpdateRecorder { - addNewImport(start: number, importText: string): void; - updateExistingImport(namedBindings: ts.NamedImports, newNamedBindings: string): void; +export interface UpdateRecorder extends ImportManagerUpdateRecorder { addClassDecorator(node: ts.ClassDeclaration, text: string): void; addClassComment(node: ts.ClassDeclaration, text: string): void; commitUpdate(): void; diff --git a/packages/core/schematics/test/all-migrations.spec.ts b/packages/core/schematics/test/all-migrations.spec.ts new file mode 100644 index 0000000000000..5d2544105a02a --- /dev/null +++ b/packages/core/schematics/test/all-migrations.spec.ts @@ -0,0 +1,96 @@ +/** + * @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 {getSystemPath, normalize, virtualFs} from '@angular-devkit/core'; +import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing'; +import {HostTree} from '@angular-devkit/schematics'; +import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing'; +import * as shx from 'shelljs'; + +describe('all migrations', () => { + let runner: SchematicTestRunner; + let host: TempScopedNodeJsSyncHost; + let tree: UnitTestTree; + let tmpDirPath: string; + let previousWorkingDir: string; + + const migrationCollectionPath = require.resolve('../migrations.json'); + const allMigrationSchematics = Object.keys(require(migrationCollectionPath).schematics); + + beforeEach(() => { + runner = new SchematicTestRunner('test', migrationCollectionPath); + host = new TempScopedNodeJsSyncHost(); + tree = new UnitTestTree(new HostTree(host)); + + writeFile('/node_modules/@angular/core/index.d.ts', `export const MODULE: any;`); + writeFile('/angular.json', JSON.stringify({ + projects: {t: {architect: {build: {options: {tsConfig: './tsconfig.json'}}}}} + })); + writeFile('/tsconfig.json', `{}`); + + + previousWorkingDir = shx.pwd(); + tmpDirPath = getSystemPath(host.root); + + // Switch into the temporary directory path. This allows us to run + // the schematic against our custom unit test tree. + shx.cd(tmpDirPath); + }); + + afterEach(() => { + shx.cd(previousWorkingDir); + shx.rm('-r', tmpDirPath); + }); + + function writeFile(filePath: string, contents: string) { + host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); + } + + async function runMigration(migrationName: string) { + await runner.runSchematicAsync(migrationName, undefined, tree).toPromise(); + } + + if (!allMigrationSchematics.length) { + throw Error('No migration schematics found.'); + } + + allMigrationSchematics.forEach(name => { + describe(name, () => createTests(name)); + }); + + function createTests(migrationName: string) { + // Regression test for: https://github.com/angular/angular/issues/36346. + it('should not throw if non-existent symbols are imported with rootDirs', async () => { + writeFile(`/tsconfig.json`, JSON.stringify({ + compilerOptions: { + rootDirs: [ + './generated', + ] + } + })); + writeFile('/index.ts', ` + import {Renderer} from '@angular/core'; + + const variableDecl: Renderer = null; + + export class Test { + constructor(renderer: Renderer) {} + } + `); + + let error: any = null; + try { + await runMigration(migrationName); + } catch (e) { + error = e; + } + + expect(error).toBe(null); + }); + } +}); diff --git a/packages/core/schematics/test/dynamic_queries_migration_spec.ts b/packages/core/schematics/test/dynamic_queries_migration_spec.ts index 19b23c951ed51..69b31fb5309ef 100644 --- a/packages/core/schematics/test/dynamic_queries_migration_spec.ts +++ b/packages/core/schematics/test/dynamic_queries_migration_spec.ts @@ -47,7 +47,7 @@ describe('dynamic queries migration', () => { }); it('should remove the options object from a dynamic ViewChild query that only has one property', - async() => { + async () => { writeFile('/index.ts', ` import { Directive, ViewChild } from '@angular/core'; @@ -62,7 +62,7 @@ describe('dynamic queries migration', () => { }); it('should remove the options object from a dynamic ContentChild query that only has one property', - async() => { + async () => { writeFile('/index.ts', ` import { Directive, ContentChild } from '@angular/core'; @@ -77,7 +77,7 @@ describe('dynamic queries migration', () => { }); it('should only remove the `static` flag from a ViewChild query if it has more than one property', - async() => { + async () => { writeFile('/index.ts', ` import { Directive, ViewChild, ElementRef } from '@angular/core'; @@ -93,7 +93,7 @@ describe('dynamic queries migration', () => { }); it('should only remove the `static` flag from a ContentChild query if it has more than one property', - async() => { + async () => { writeFile('/index.ts', ` import { Directive, ContentChild, ElementRef } from '@angular/core'; @@ -108,7 +108,7 @@ describe('dynamic queries migration', () => { .toContain(`@ContentChild('child', { read: ElementRef }) child: ElementRef;`); }); - it('should not change static ViewChild queries', async() => { + it('should not change static ViewChild queries', async () => { writeFile('/index.ts', ` import { Directive, ViewChild, ElementRef } from '@angular/core'; @@ -123,7 +123,7 @@ describe('dynamic queries migration', () => { .toContain(`@ViewChild('child', { read: ElementRef, static: true }) child: ElementRef;`); }); - it('should not change static ContentChild queries', async() => { + it('should not change static ContentChild queries', async () => { writeFile('/index.ts', ` import { Directive, ContentChild, ElementRef } from '@angular/core'; @@ -138,7 +138,7 @@ describe('dynamic queries migration', () => { .toContain(`@ContentChild('child', { static: true, read: ElementRef }) child: ElementRef;`); }); - it('should migrate dynamic queries on a setter', async() => { + it('should migrate dynamic queries on a setter', async () => { writeFile('/index.ts', ` import { Directive, ContentChild, ViewChild } from '@angular/core'; diff --git a/packages/core/schematics/test/google3/dynamic_queries_spec.ts b/packages/core/schematics/test/google3/dynamic_queries_spec.ts index 51f5a2ec8000b..16ec13d368cb3 100644 --- a/packages/core/schematics/test/google3/dynamic_queries_spec.ts +++ b/packages/core/schematics/test/google3/dynamic_queries_spec.ts @@ -17,7 +17,7 @@ describe('Google3 dynamic queries TSLint rule', () => { let tmpDir: string; beforeEach(() => { - tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test'); + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); shx.mkdir('-p', tmpDir); writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}})); @@ -31,7 +31,7 @@ describe('Google3 dynamic queries TSLint rule', () => { const config = Configuration.parseConfigFile({rules: {'dynamic-queries': true}}); program.getRootFileNames().forEach(fileName => { - linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config); + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); }); return linter; @@ -41,7 +41,9 @@ describe('Google3 dynamic queries TSLint rule', () => { writeFileSync(join(tmpDir, fileName), content); } - function getFile(fileName: string) { return readFileSync(join(tmpDir, fileName), 'utf8'); } + function getFile(fileName: string) { + return readFileSync(join(tmpDir, fileName), 'utf8'); + } it('should flag dynamic queries', () => { writeFile('/index.ts', ` @@ -172,5 +174,4 @@ describe('Google3 dynamic queries TSLint rule', () => { expect(content).toContain(`@ContentChild('child') set child(c: any) {}`); expect(content).toContain(`@ViewChild('otherChild') set otherChild(c: any) {}`); }); - }); diff --git a/packages/core/schematics/test/google3/explicit_query_timing_rule_spec.ts b/packages/core/schematics/test/google3/explicit_query_timing_rule_spec.ts index 3705176b85f89..895973c728836 100644 --- a/packages/core/schematics/test/google3/explicit_query_timing_rule_spec.ts +++ b/packages/core/schematics/test/google3/explicit_query_timing_rule_spec.ts @@ -12,7 +12,6 @@ import * as shx from 'shelljs'; import {Configuration, Linter} from 'tslint'; describe('Google3 explicitQueryTiming TSLint rule', () => { - /** * Path to the static-query schematic rules directory. The path needs to be resolved through * the Bazel runfiles, because on Windows runfiles are not symlinked into the working directory. @@ -23,7 +22,7 @@ describe('Google3 explicitQueryTiming TSLint rule', () => { let tmpDir: string; beforeEach(() => { - tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test'); + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); shx.mkdir('-p', tmpDir); writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}})); @@ -41,7 +40,7 @@ describe('Google3 explicitQueryTiming TSLint rule', () => { const config = Configuration.parseConfigFile({rules: {'explicit-query-timing': true}}); program.getRootFileNames().forEach(fileName => { - linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config); + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); }); return linter; diff --git a/packages/core/schematics/test/google3/missing_injectable_rule_spec.ts b/packages/core/schematics/test/google3/missing_injectable_rule_spec.ts index 87cf98a077f75..5a11ec8ad6431 100644 --- a/packages/core/schematics/test/google3/missing_injectable_rule_spec.ts +++ b/packages/core/schematics/test/google3/missing_injectable_rule_spec.ts @@ -18,7 +18,7 @@ describe('Google3 missing injectable tslint rule', () => { let tmpDir: string; beforeEach(() => { - tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test'); + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); shx.mkdir('-p', tmpDir); writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}})); @@ -32,7 +32,7 @@ describe('Google3 missing injectable tslint rule', () => { const config = Configuration.parseConfigFile({rules: {'no-missing-injectable': true}}); program.getRootFileNames().forEach(fileName => { - linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config); + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); }); return linter; @@ -42,7 +42,9 @@ describe('Google3 missing injectable tslint rule', () => { writeFileSync(join(tmpDir, fileName), content); } - function getFile(fileName: string) { return readFileSync(join(tmpDir, fileName), 'utf8'); } + function getFile(fileName: string) { + return readFileSync(join(tmpDir, fileName), 'utf8'); + } describe('NgModule', () => createTests('NgModule', 'providers')); describe('Directive', () => createTests('Directive', 'providers')); @@ -77,7 +79,7 @@ describe('Google3 missing injectable tslint rule', () => { }); function createTests( - type: 'NgModule' | 'Directive' | 'Component', propName: 'providers' | 'viewProviders') { + type: 'NgModule'|'Directive'|'Component', propName: 'providers'|'viewProviders') { it('should create proper failures for missing injectable providers', () => { writeFile('index.ts', ` import { ${type} } from '@angular/core'; @@ -266,6 +268,4 @@ describe('Google3 missing injectable tslint rule', () => { .toMatch(/import { Inject, Injectable } from '@angular\/core';/); }); } - - }); diff --git a/packages/core/schematics/test/google3/no_template_variable_assignment_rule_spec.ts b/packages/core/schematics/test/google3/no_template_variable_assignment_rule_spec.ts index 66ce27f7ccbab..08ffd83ebf015 100644 --- a/packages/core/schematics/test/google3/no_template_variable_assignment_rule_spec.ts +++ b/packages/core/schematics/test/google3/no_template_variable_assignment_rule_spec.ts @@ -18,7 +18,7 @@ describe('Google3 noTemplateVariableAssignment TSLint rule', () => { let tmpDir: string; beforeEach(() => { - tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test'); + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); shx.mkdir('-p', tmpDir); writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}})); @@ -34,7 +34,7 @@ describe('Google3 noTemplateVariableAssignment TSLint rule', () => { Configuration.parseConfigFile({rules: {'no-template-variable-assignment': true}}); program.getRootFileNames().forEach(fileName => { - linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config); + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); }); return linter; diff --git a/packages/core/schematics/test/google3/renderer_to_renderer2_spec.ts b/packages/core/schematics/test/google3/renderer_to_renderer2_spec.ts index f403e7a026782..ee5a452899b9f 100644 --- a/packages/core/schematics/test/google3/renderer_to_renderer2_spec.ts +++ b/packages/core/schematics/test/google3/renderer_to_renderer2_spec.ts @@ -18,7 +18,7 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => { let tmpDir: string; beforeEach(() => { - tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test'); + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); shx.mkdir('-p', tmpDir); // We need to declare the Angular symbols we're testing for, otherwise type checking won't work. @@ -46,7 +46,7 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => { const config = Configuration.parseConfigFile({rules: {'renderer-to-renderer2': true}}); program.getRootFileNames().forEach(fileName => { - linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config); + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); }); return linter; @@ -56,7 +56,9 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => { writeFileSync(join(tmpDir, fileName), content); } - function getFile(fileName: string) { return readFileSync(join(tmpDir, fileName), 'utf8'); } + function getFile(fileName: string) { + return readFileSync(join(tmpDir, fileName), 'utf8'); + } it('should flag Renderer imports and typed nodes', () => { writeFile('/index.ts', ` @@ -223,8 +225,8 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => { runTSLint(true); const content = getFile('index.ts'); - expect(content.match(/function __ngRendererCreateElementHelper\(/g) !.length).toBe(1); - expect(content.match(/function __ngRendererSetElementAttributeHelper\(/g) !.length).toBe(1); + expect(content.match(/function __ngRendererCreateElementHelper\(/g)!.length).toBe(1); + expect(content.match(/function __ngRendererSetElementAttributeHelper\(/g)!.length).toBe(1); }); it('should insert helpers after the user\'s code', () => { @@ -410,5 +412,4 @@ describe('Google3 Renderer to Renderer2 TSLint rule', () => { // Expect the `setInfo` method to only contain whitespace. expect(content).toMatch(/setInfo\(\) \{\s+\}/); }); - }); diff --git a/packages/core/schematics/test/google3/undecorated_classes_with_decorated_fields_spec.ts b/packages/core/schematics/test/google3/undecorated_classes_with_decorated_fields_spec.ts index 68718ca8e15af..a8e5764a5ef5d 100644 --- a/packages/core/schematics/test/google3/undecorated_classes_with_decorated_fields_spec.ts +++ b/packages/core/schematics/test/google3/undecorated_classes_with_decorated_fields_spec.ts @@ -18,7 +18,7 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () => let tmpDir: string; beforeEach(() => { - tmpDir = join(process.env['TEST_TMPDIR'] !, 'google3-test'); + tmpDir = join(process.env['TEST_TMPDIR']!, 'google3-test'); shx.mkdir('-p', tmpDir); writeFile('tsconfig.json', JSON.stringify({compilerOptions: {module: 'es2015'}})); }); @@ -33,7 +33,7 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () => }); program.getRootFileNames().forEach(fileName => { - linter.lint(fileName, program.getSourceFile(fileName) !.getFullText(), config); + linter.lint(fileName, program.getSourceFile(fileName)!.getFullText(), config); }); return linter; @@ -43,7 +43,9 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () => writeFileSync(join(tmpDir, fileName), content); } - function getFile(fileName: string) { return readFileSync(join(tmpDir, fileName), 'utf8'); } + function getFile(fileName: string) { + return readFileSync(join(tmpDir, fileName), 'utf8'); + } it('should flag undecorated classes with decorated fields', () => { writeFile('/index.ts', ` @@ -64,7 +66,7 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () => expect(failures.length).toBe(1); expect(failures[0]) - .toBe('Classes with decorated fields must have an Angular decorator as well.'); + .toBe('Class needs to be decorated with "@Directive()" because it uses Angular features.'); }); it(`should add an import for Directive if there isn't one already`, () => { @@ -97,6 +99,28 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () => expect(getFile('/index.ts')).toContain(`import { Directive, Input } from '@angular/core';`); }); + it('should not generate conflicting imports there is a different `Directive` symbol', + async () => { + writeFile('/index.ts', ` + import { HostBinding } from '@angular/core'; + + export class Directive { + // Simulates a scenario where a library defines a class named "Directive". + // We don't want to generate a conflicting import. + } + + export class MyLibrarySharedBaseClass { + @HostBinding('class.active') isActive: boolean; + } + `); + + runTSLint(true); + const fileContent = getFile('/index.ts'); + expect(fileContent) + .toContain(`import { HostBinding, Directive as Directive_1 } from '@angular/core';`); + expect(fileContent).toMatch(/@Directive_1\(\)\s+export class MyLibrarySharedBaseClass/); + }); + it('should add @Directive to undecorated classes that have @Input', () => { writeFile('/index.ts', ` import { Input } from '@angular/core'; @@ -229,4 +253,35 @@ describe('Google3 undecorated classes with decorated fields TSLint rule', () => expect(getFile('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); + it('should add @Directive to undecorated derived classes of a migrated class', async () => { + writeFile('/index.ts', ` + import { Input, Directive, NgModule } from '@angular/core'; + + export class Base { + @Input() isActive: boolean; + } + + export class DerivedA extends Base {} + export class DerivedB extends DerivedA {} + export class DerivedC extends DerivedB {} + + @Directive({selector: 'my-comp'}) + export class MyComp extends DerivedC {} + + export class MyCompWrapped extends MyComp {} + + @NgModule({declarations: [MyComp, MyCompWrapped]}) + export class AppModule {} + `); + + runTSLint(true); + const fileContent = getFile('/index.ts'); + expect(fileContent).toContain(`import { Input, Directive, NgModule } from '@angular/core';`); + expect(fileContent).toMatch(/@Directive\(\)\s+export class Base/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedA/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedB/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedC/); + expect(fileContent).toMatch(/}\s+@Directive\(\{selector: 'my-comp'}\)\s+export class MyComp/); + expect(fileContent).toMatch(/}\s+export class MyCompWrapped/); + }); }); diff --git a/packages/core/schematics/test/line_mappings_spec.ts b/packages/core/schematics/test/line_mappings_spec.ts index a9aa66fb5bb53..17565e11d88d2 100644 --- a/packages/core/schematics/test/line_mappings_spec.ts +++ b/packages/core/schematics/test/line_mappings_spec.ts @@ -9,7 +9,6 @@ import {computeLineStartsMap, getLineAndCharacterFromPosition} from '../utils/line_mappings'; describe('line mappings', () => { - it('should properly compute line starts', () => { expect(computeLineStartsMap(` diff --git a/packages/core/schematics/test/missing_injectable_migration_spec.ts b/packages/core/schematics/test/missing_injectable_migration_spec.ts index 762989f4a8204..28e6787710842 100644 --- a/packages/core/schematics/test/missing_injectable_migration_spec.ts +++ b/packages/core/schematics/test/missing_injectable_migration_spec.ts @@ -76,7 +76,7 @@ describe('Missing injectable migration', () => { it('should migrate all providers defined in "viewProviders" and "providers" in the ' + 'same component', - async() => { + async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -102,8 +102,8 @@ describe('Missing injectable migration', () => { }); function createTests( - type: 'NgModule' | 'Directive' | 'Component', propName: 'providers' | 'viewProviders') { - it(`should migrate type provider in ${type}`, async() => { + type: 'NgModule'|'Directive'|'Component', propName: 'providers'|'viewProviders') { + it(`should migrate type provider in ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -121,7 +121,7 @@ describe('Missing injectable migration', () => { .toContain(`{ ${type}, Injectable } from '@angular/core`); }); - it(`should migrate object literal provider in ${type} to explicit value provider`, async() => { + it(`should migrate object literal provider in ${type} to explicit value provider`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -140,7 +140,7 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).toContain(`{${type}} from '@angular/core`); }); - it(`should migrate object literal provider with forwardRef in ${type}`, async() => { + it(`should migrate object literal provider with forwardRef in ${type}`, async () => { writeFile('/index.ts', ` import {${type}, forwardRef} from '@angular/core'; @@ -158,7 +158,7 @@ describe('Missing injectable migration', () => { .toContain(`{ ${type}, forwardRef, Injectable } from '@angular/core`); }); - it(`should not migrate object literal provider with "useValue" in ${type}`, async() => { + it(`should not migrate object literal provider with "useValue" in ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -174,7 +174,7 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).not.toContain('@Injectable'); }); - it(`should not migrate provider with "useClass" and "deps" in ${type}`, async() => { + it(`should not migrate provider with "useClass" and "deps" in ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -190,7 +190,7 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).not.toContain('@Injectable'); }); - it(`should not migrate object literal provider with "useFactory" in ${type}`, async() => { + it(`should not migrate object literal provider with "useFactory" in ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -206,7 +206,7 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).not.toContain('@Injectable'); }); - it(`should not migrate object literal provider with "useExisting" in ${type}`, async() => { + it(`should not migrate object literal provider with "useExisting" in ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -226,7 +226,7 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).not.toContain('@Injectable'); }); - it(`should migrate object literal provider with "useClass" in ${type}`, async() => { + it(`should migrate object literal provider with "useClass" in ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -248,7 +248,7 @@ describe('Missing injectable migration', () => { it(`should not migrate references for providers with "useExisting" in ${type}, but migrate ` + `existing token if declared in other ${type}`, - async() => { + async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -281,7 +281,7 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).toMatch(/MyService {}\s+export class MyToken/); }); - it('should not migrate provider which is already decorated with @Injectable', async() => { + it('should not migrate provider which is already decorated with @Injectable', async () => { writeFile('/index.ts', ` import {Injectable, ${type}} from '@angular/core'; @@ -299,7 +299,7 @@ describe('Missing injectable migration', () => { .toMatch(/@angular\/core';\s+@Injectable\(\)\s+export class MyService/); }); - it('should not migrate provider which is already decorated with @Directive', async() => { + it('should not migrate provider which is already decorated with @Directive', async () => { writeFile('/index.ts', ` import {Directive, ${type}} from '@angular/core'; @@ -316,7 +316,7 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).not.toContain('@Injectable'); }); - it('should not migrate provider which is already decorated with @Component', async() => { + it('should not migrate provider which is already decorated with @Component', async () => { writeFile('/index.ts', ` import {Component, ${type}} from '@angular/core'; @@ -333,7 +333,7 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).not.toContain('@Injectable'); }); - it('should not migrate provider which is already decorated with @Pipe', async() => { + it('should not migrate provider which is already decorated with @Pipe', async () => { writeFile('/index.ts', ` import {Pipe, ${type}} from '@angular/core'; @@ -350,7 +350,25 @@ describe('Missing injectable migration', () => { expect(tree.readContent('/index.ts')).not.toContain('@Injectable'); }); - it(`should migrate multiple providers in same ${type}`, async() => { + it('should not migrate provider which is already decorated with @NgModule', async () => { + const importedSymbols = type !== 'NgModule' ? ['NgModule', type] : ['NgModule']; + writeFile('/index.ts', ` + import {${importedSymbols.join(', ')}} from '@angular/core'; + + @NgModule() + export class MyOtherModule {} + + @${type}({${propName}: [MyOtherModule]}) + export class TestClass {} + `); + + await runMigration(); + + expect(warnOutput.length).toBe(0); + expect(tree.readContent('/index.ts')).not.toContain('@Injectable'); + }); + + it(`should migrate multiple providers in same ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -370,7 +388,7 @@ describe('Missing injectable migration', () => { .toContain(`{ ${type}, Injectable } from '@angular/core`); }); - it(`should migrate multiple mixed providers in same ${type}`, async() => { + it(`should migrate multiple mixed providers in same ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -399,7 +417,7 @@ describe('Missing injectable migration', () => { .toContain(`{ provide: ServiceB, useValue: undefined },`); }); - it(`should migrate multiple nested providers in same ${type}`, async() => { + it(`should migrate multiple nested providers in same ${type}`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -433,7 +451,7 @@ describe('Missing injectable migration', () => { .toContain(`{ provide: ServiceD, useValue: undefined },`); }); - it('should migrate providers referenced through identifier', async() => { + it('should migrate providers referenced through identifier', async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -467,7 +485,7 @@ describe('Missing injectable migration', () => { .toContain(`{ provide: ServiceC, useValue: undefined },`); }); - it('should migrate providers created through static analyzable function call', async() => { + it('should migrate providers created through static analyzable function call', async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -497,7 +515,7 @@ describe('Missing injectable migration', () => { .toContain(`ServiceB, { provide: ServiceC, useValue: undefined }),`); }); - it('should migrate providers which are computed through spread operator', async() => { + it('should migrate providers which are computed through spread operator', async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -525,7 +543,7 @@ describe('Missing injectable migration', () => { .toContain(`ServiceB, { provide: ServiceC, useValue: undefined }];`); }); - it(`should migrate provider once if referenced in multiple ${type} definitions`, async() => { + it(`should migrate provider once if referenced in multiple ${type} definitions`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -559,7 +577,7 @@ describe('Missing injectable migration', () => { it(`should only migrate empty object provider literal once if referenced multiple times ` + `in ${type} definitions`, - async() => { + async () => { writeFile('/provider.ts', ` export class MyService {} @@ -592,7 +610,7 @@ describe('Missing injectable migration', () => { .toContain(`const PROVIDER = { provide: MyService, useValue: undefined };`); }); - it('should create new import for @Injectable when migrating provider', async() => { + it('should create new import for @Injectable when migrating provider', async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; import {MyService, MySecondService} from './service'; @@ -617,7 +635,7 @@ describe('Missing injectable migration', () => { }); it('should re-use existing namespace import for importing @Injectable when migrating provider', - async() => { + async () => { writeFile('/index.ts', ` import * as core from '@angular/core'; @@ -643,7 +661,7 @@ describe('Missing injectable migration', () => { .toMatch(/@core.Injectable\(\)\s+export class MyService/); }); - it('should warn if a referenced individual provider could not be resolved', async() => { + it('should warn if a referenced individual provider could not be resolved', async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -659,7 +677,7 @@ describe('Missing injectable migration', () => { expect(warnOutput[0]).toContain(`4:${providerSourceTextColumn}:`); }); - it(`should warn if ${propName} value could not be resolved`, async() => { + it(`should warn if ${propName} value could not be resolved`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -675,7 +693,7 @@ describe('Missing injectable migration', () => { expect(warnOutput[0]).toContain(`4:${propValueSourceTextColumn}:`); }); - it(`should not throw if an empty @${type} is analyzed`, async() => { + it(`should not throw if an empty @${type} is analyzed`, async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; @@ -693,7 +711,7 @@ describe('Missing injectable migration', () => { }); it('should create new import for injectable after full end of last import statement', - async() => { + async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; import {MyService} from './service'; @@ -718,7 +736,7 @@ describe('Missing injectable migration', () => { .toMatch(/'b'; \/\/ some comment\s+import { Injectable } from "@angular\/core";/); }); - it('should create new import at source file start with trailing new-line', async() => { + it('should create new import at source file start with trailing new-line', async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; import {MyService} from './service'; @@ -739,7 +757,7 @@ describe('Missing injectable migration', () => { /^import { Injectable } from "@angular\/core";\s+\/\* @license \*\/\s+@Injectable\(\)\s+export class MyService/); }); - it('should remove @Inject decorator for providers which are migrated', async() => { + it('should remove @Inject decorator for providers which are migrated', async () => { writeFile('/index.ts', ` import {${type}} from '@angular/core'; import {MyService} from './service'; @@ -766,7 +784,7 @@ describe('Missing injectable migration', () => { .toMatch(/import { Inject, Injectable } from '@angular\/core';/); }); - it('should not migrate provider classes in library type definitions', async() => { + it('should not migrate provider classes in library type definitions', async () => { writeFile('/node_modules/my-lib/index.d.ts', ` export declare class MyService {} `); diff --git a/packages/core/schematics/test/module_with_providers_migration_spec.ts b/packages/core/schematics/test/module_with_providers_migration_spec.ts index 089753a633d15..13a999e76d90e 100644 --- a/packages/core/schematics/test/module_with_providers_migration_spec.ts +++ b/packages/core/schematics/test/module_with_providers_migration_spec.ts @@ -46,7 +46,7 @@ describe('ModuleWithProviders migration', () => { shx.rm('-r', tmpDirPath); }); - it('should add generic type for function return', async() => { + it('should add generic type for function return', async () => { writeFile('/index.ts', ` import {NgModule, ModuleWithProviders} from '@angular/core'; @@ -69,7 +69,7 @@ describe('ModuleWithProviders migration', () => { expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`); }); - it('should add generic type for function return; external file', async() => { + it('should add generic type for function return; external file', async () => { writeFile('/module.ts', ` import {NgModule} from '@angular/core'; @@ -96,7 +96,7 @@ describe('ModuleWithProviders migration', () => { expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`); }); - it('should add generic type for function return without explicit type', async() => { + it('should add generic type for function return without explicit type', async () => { writeFile('/index.ts', ` import {NgModule} from '@angular/core'; @@ -119,7 +119,7 @@ describe('ModuleWithProviders migration', () => { expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`); }); - it('should add generic type for const variable', async() => { + it('should add generic type for const variable', async () => { writeFile('/index.ts', ` import {ModuleWithProviders, NgModule} from '@angular/core'; @@ -140,7 +140,7 @@ describe('ModuleWithProviders migration', () => { expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`); }); - it('should add generic type for const variable without explicit type', async() => { + it('should add generic type for const variable without explicit type', async () => { writeFile('/index.ts', ` import {NgModule} from '@angular/core'; @@ -161,7 +161,7 @@ describe('ModuleWithProviders migration', () => { expect(tree.readContent('/index.ts')).toContain(`ModuleWithProviders<BaseModule>`); }); - it('should not add generic type for const variable with invalid base object', async() => { + it('should not add generic type for const variable with invalid base object', async () => { writeFile('/index.ts', ` import {NgModule} from '@angular/core'; @@ -182,7 +182,7 @@ describe('ModuleWithProviders migration', () => { expect(tree.readContent('/index.ts')).not.toContain(`ModuleWithProviders<BaseModule>`); }); - it('should add generic type for const variables and functions with incomplete type', async() => { + it('should add generic type for const variables and functions with incomplete type', async () => { writeFile('/index.ts', ` import {ModuleWithProviders, NgModule} from '@angular/core'; @@ -214,7 +214,7 @@ describe('ModuleWithProviders migration', () => { expect(tree.readContent('/index.ts')).not.toContain(`ModuleWithProviders `); }); - it('should not add generic type for const variables without initialization', async() => { + it('should not add generic type for const variables without initialization', async () => { writeFile('/index.ts', ` import {ModuleWithProviders} from '@angular/core'; diff --git a/packages/core/schematics/test/move_document_migration_spec.ts b/packages/core/schematics/test/move_document_migration_spec.ts index 46aefc9e016d2..d0f252c4fc5af 100644 --- a/packages/core/schematics/test/move_document_migration_spec.ts +++ b/packages/core/schematics/test/move_document_migration_spec.ts @@ -47,7 +47,7 @@ describe('move-document migration', () => { }); describe('move-document', () => { - it('should properly apply import replacement', async() => { + it('should properly apply import replacement', async () => { writeFile('/index.ts', ` import {DOCUMENT} from '@angular/platform-browser'; `); @@ -73,7 +73,7 @@ describe('move-document migration', () => { expect(content).not.toContain(`import {DOCUMENT} from '@angular/platform-browser';`); }); - it('should properly apply import replacement with existing import', async() => { + it('should properly apply import replacement with existing import', async () => { writeFile('/index.ts', ` import {DOCUMENT} from '@angular/platform-browser'; import {someImport} from '@angular/common'; @@ -96,7 +96,7 @@ describe('move-document migration', () => { expect(contentReverse).not.toContain(`import {DOCUMENT} from '@angular/platform-browser';`); }); - it('should properly apply import replacement with existing import w/ comments', async() => { + it('should properly apply import replacement with existing import w/ comments', async () => { writeFile('/index.ts', ` /** * this is a comment @@ -115,7 +115,7 @@ describe('move-document migration', () => { expect(content).toMatch(/.*this is a comment.*/); }); - it('should properly apply import replacement with existing and redundant imports', async() => { + it('should properly apply import replacement with existing and redundant imports', async () => { writeFile('/index.ts', ` import {DOCUMENT} from '@angular/platform-browser'; import {anotherImport} from '@angular/platform-browser-dynamic'; @@ -131,7 +131,7 @@ describe('move-document migration', () => { }); it('should properly apply import replacement with existing import and leave original import', - async() => { + async () => { writeFile('/index.ts', ` import {DOCUMENT, anotherImport} from '@angular/platform-browser'; import {someImport} from '@angular/common'; @@ -145,7 +145,7 @@ describe('move-document migration', () => { expect(content).toContain(`import { anotherImport } from '@angular/platform-browser';`); }); - it('should properly apply import replacement with existing import and alias', async() => { + it('should properly apply import replacement with existing import and alias', async () => { writeFile('/index.ts', ` import {DOCUMENT as doc, anotherImport} from '@angular/platform-browser'; import {someImport} from '@angular/common'; diff --git a/packages/core/schematics/test/project_tsconfig_paths_spec.ts b/packages/core/schematics/test/project_tsconfig_paths_spec.ts index 0ebf15276debc..b9cb58b5047c8 100644 --- a/packages/core/schematics/test/project_tsconfig_paths_spec.ts +++ b/packages/core/schematics/test/project_tsconfig_paths_spec.ts @@ -13,13 +13,14 @@ import {getProjectTsConfigPaths} from '../utils/project_tsconfig_paths'; describe('project tsconfig paths', () => { let testTree: UnitTestTree; - beforeEach(() => { testTree = new UnitTestTree(new HostTree()); }); + beforeEach(() => { + testTree = new UnitTestTree(new HostTree()); + }); it('should detect build tsconfig path inside of angular.json file', () => { testTree.create('/my-custom-config.json', ''); testTree.create('/angular.json', JSON.stringify({ - projects: - {my_name: {architect: {build: {options: {tsConfig: './my-custom-config.json'}}}}} + projects: {my_name: {architect: {build: {options: {tsConfig: './my-custom-config.json'}}}}} })); expect(getProjectTsConfigPaths(testTree).buildPaths).toEqual(['my-custom-config.json']); @@ -57,8 +58,7 @@ describe('project tsconfig paths', () => { it('should detect test tsconfig path inside of .angular.json file', () => { testTree.create('/my-test-config.json', ''); testTree.create('/.angular.json', JSON.stringify({ - projects: - {with_tests: {architect: {test: {options: {tsConfig: './my-test-config.json'}}}}} + projects: {with_tests: {architect: {test: {options: {tsConfig: './my-test-config.json'}}}}} })); expect(getProjectTsConfigPaths(testTree).testPaths).toEqual(['my-test-config.json']); diff --git a/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts b/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts index c30919ea5d9c8..c818804e89cf0 100644 --- a/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts +++ b/packages/core/schematics/test/renderer_to_renderer2_migration_spec.ts @@ -52,7 +52,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('import renaming', () => { - it('should change Renderer imports to Renderer2', async() => { + it('should change Renderer imports to Renderer2', async () => { writeFile('/index.ts', ` import { Renderer, Component } from '@angular/core'; @@ -67,7 +67,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain(`import { Component, Renderer2 } from '@angular/core';`); }); - it('should change aliased Renderer imports to Renderer2', async() => { + it('should change aliased Renderer imports to Renderer2', async () => { writeFile('/index.ts', ` import { Renderer as RenamedRenderer, Component } from '@angular/core'; @@ -82,7 +82,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain(`import { Component, Renderer2 as RenamedRenderer } from '@angular/core';`); }); - it('should not change Renderer imports if they are not from @angular/core', async() => { + it('should not change Renderer imports if they are not from @angular/core', async () => { writeFile('/index.ts', ` import { Component } from '@angular/core'; import { Renderer } from './my-renderer'; @@ -102,7 +102,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('type renaming', () => { - it('should change type of constructor parameter from Renderer to Renderer2', async() => { + it('should change type of constructor parameter from Renderer to Renderer2', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -117,7 +117,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain('constructor(element: ElementRef, renderer: Renderer2)'); }); - it('should change type of method parameter from Renderer to Renderer2', async() => { + it('should change type of method parameter from Renderer to Renderer2', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -134,7 +134,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain('disable(renderer: Renderer2, element: HTMLElement, isDisabled: boolean)'); }); - it('should change type of property declarations', async() => { + it('should change type of property declarations', async () => { writeFile('/index.ts', ` import { Renderer, Component } from '@angular/core'; @@ -148,7 +148,7 @@ describe('Renderer to Renderer2 migration', () => { expect(tree.readContent('/index.ts')).toContain('public renderer: Renderer2;'); }); - it('should change type of properties initialized via the constructor', async() => { + it('should change type of properties initialized via the constructor', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -163,7 +163,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain('constructor(element: ElementRef, private _renderer: Renderer2)'); }); - it('should change the type of something that was cast to Renderer', async() => { + it('should change the type of something that was cast to Renderer', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -184,7 +184,7 @@ describe('Renderer to Renderer2 migration', () => { expect(content).toContain(`renderer.setStyle(element.nativeElement, 'color', 'red');`); }); - it('should not rename types called Renderer that do not come from Angular', async() => { + it('should not rename types called Renderer that do not come from Angular', async () => { // Write a dummy renderer file so type checking picks it up. writeFile('/my-renderer.ts', `export abstract class Renderer {}`); @@ -203,7 +203,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain('constructor(element: ElementRef, renderer: Renderer)'); }); - it('should rename inside single-line forwardRef', async() => { + it('should rename inside single-line forwardRef', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef, forwardRef, Inject } from '@angular/core'; @@ -221,7 +221,7 @@ describe('Renderer to Renderer2 migration', () => { `constructor(@Inject(forwardRef(() => Renderer2)) private _renderer: Renderer2)`); }); - it('should rename inside multi-line forwardRef', async() => { + it('should rename inside multi-line forwardRef', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef, forwardRef, Inject } from '@angular/core'; @@ -238,11 +238,10 @@ describe('Renderer to Renderer2 migration', () => { expect(content).toContain( `constructor(@Inject(forwardRef(() => { return Renderer2; })) private _renderer: Renderer2) {}`); }); - }); describe('helper insertion', () => { - it('should only declare each helper once per file', async() => { + it('should only declare each helper once per file', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -260,11 +259,11 @@ describe('Renderer to Renderer2 migration', () => { const content = tree.readContent('/index.ts'); - expect(content.match(/function __ngRendererCreateElementHelper\(/g) !.length) + expect(content.match(/function __ngRendererCreateElementHelper\(/g)!.length) .toBe(1, 'Expected exactly one helper for createElement.'); }); - it('should insert helpers after the user\'s code', async() => { + it('should insert helpers after the user\'s code', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -287,7 +286,7 @@ describe('Renderer to Renderer2 migration', () => { expect(contentAfterSeparator).toContain('function __ngRendererCreateElementHelper('); }); - it('should be able to handle multiple helpers per file', async() => { + it('should be able to handle multiple helpers per file', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -314,15 +313,15 @@ describe('Renderer to Renderer2 migration', () => { const content = tree.readContent('/index.ts'); - expect(content.match(/function __ngRendererCreateTextHelper\(/g) !.length) + expect(content.match(/function __ngRendererCreateTextHelper\(/g)!.length) .toBe(1, 'Expected exactly one helper for createElement.'); - expect(content.match(/function __ngRendererCreateElementHelper\(/g) !.length) + expect(content.match(/function __ngRendererCreateElementHelper\(/g)!.length) .toBe(1, 'Expected exactly one helper for createText.'); - expect(content.match(/function __ngRendererCreateTemplateAnchorHelper\(/g) !.length) + expect(content.match(/function __ngRendererCreateTemplateAnchorHelper\(/g)!.length) .toBe(1, 'Expected exactly one helper for createTemplateAnchor.'); }); - it('should create the __ngRendererSplitNamespaceHelper', async() => { + it('should create the __ngRendererSplitNamespaceHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -347,7 +346,7 @@ describe('Renderer to Renderer2 migration', () => { `)); }); - it('should declare our custom any type', async() => { + it('should declare our custom any type', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -365,11 +364,10 @@ describe('Renderer to Renderer2 migration', () => { type AnyDuringRendererMigration = any; `)); }); - }); describe('setElementProperty migration', () => { - it('should migrate setElementProperty calls', async() => { + it('should migrate setElementProperty calls', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -390,7 +388,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('setText migration', () => { - it('should migrate setText calls', async() => { + it('should migrate setText calls', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -412,7 +410,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('listenGlobal migration', () => { - it('should migrate listenGlobal calls', async() => { + it('should migrate listenGlobal calls', async () => { writeFile('/index.ts', ` import { Renderer, Component } from '@angular/core'; @@ -433,7 +431,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('selectRootElement migration', () => { - it('should migrate selectRootElement calls', async() => { + it('should migrate selectRootElement calls', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -454,7 +452,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('setElementClass migration', () => { - it('should migrate calls with inline isAdd value', async() => { + it('should migrate calls with inline isAdd value', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -479,7 +477,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain(`this._renderer.removeClass(this._element.nativeElement, className);`); }); - it('should migrate calls with variable isAdd value', async() => { + it('should migrate calls with variable isAdd value', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -503,7 +501,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('setElementStyle migration', () => { - it('should migrate calls with two arguments to a removeStyle call', async() => { + it('should migrate calls with two arguments to a removeStyle call', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -522,7 +520,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain(`this._renderer.removeStyle(this._element.nativeElement, 'color');`); }); - it('should migrate calls with static third arguments to a setStyle call', async() => { + it('should migrate calls with static third arguments to a setStyle call', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -552,7 +550,7 @@ describe('Renderer to Renderer2 migration', () => { }); it('should migrate calls with null or undefined value for last argument to a removeStyle call', - async() => { + async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -575,7 +573,7 @@ describe('Renderer to Renderer2 migration', () => { `this._renderer.removeStyle(this._element.nativeElement, 'background-color');`); }); - it('should migrate calls with a variable third argument', async() => { + it('should migrate calls with a variable third argument', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -597,7 +595,7 @@ describe('Renderer to Renderer2 migration', () => { }); it('should migrate calls with a variable third argument whose value can be inferred', - async() => { + async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -625,7 +623,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('setElementAttribute migration', () => { - it('should migrate to calls to the __ngRendererSetElementAttributeHelper', async() => { + it('should migrate to calls to the __ngRendererSetElementAttributeHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -651,7 +649,7 @@ describe('Renderer to Renderer2 migration', () => { '__ngRendererSetElementAttributeHelper(this._renderer, this._element.nativeElement, name);'); }); - it('should declare the __ngRendererSetElementAttributeHelper', async() => { + it('should declare the __ngRendererSetElementAttributeHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -680,12 +678,11 @@ describe('Renderer to Renderer2 migration', () => { expect(content).toContain(stripWhitespace('function __ngRendererSplitNamespaceHelper(')); }); - }); describe('invokeElementMethod migration', () => { it('should migrate calls to a direct method call if the method name and arguments are static', - async() => { + async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -710,7 +707,7 @@ describe('Renderer to Renderer2 migration', () => { }); it('should migrate calls to a property access if the method name or arguments are dynamic', - async() => { + async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -738,7 +735,7 @@ describe('Renderer to Renderer2 migration', () => { `(this._element.nativeElement as any)['otherMethod'].apply(this._element.nativeElement, args);`); }); - it('should handle calls without an `args` array', async() => { + it('should handle calls without an `args` array', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -761,7 +758,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('setBindingDebugInfo migration', () => { - it('should drop calls to setBindingDebugInfo', async() => { + it('should drop calls to setBindingDebugInfo', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -783,7 +780,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('createViewRoot migration', () => { - it('should replace createViewRoot calls with a reference to the first argument', async() => { + it('should replace createViewRoot calls with a reference to the first argument', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -806,7 +803,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('createElement migration', () => { - it('should migrate to calls to the __ngRendererCreateElementHelper', async() => { + it('should migrate to calls to the __ngRendererCreateElementHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -833,7 +830,7 @@ describe('Renderer to Renderer2 migration', () => { 'return __ngRendererCreateElementHelper(this._renderer, this._element.nativeElement, nodeName);'); }); - it('should declare the __ngRendererCreateElementHelper', async() => { + it('should declare the __ngRendererCreateElementHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -862,11 +859,10 @@ describe('Renderer to Renderer2 migration', () => { expect(content).toContain(stripWhitespace('function __ngRendererSplitNamespaceHelper(')); }); - }); describe('createText migration', () => { - it('should migrate to calls to the __ngRendererCreateTextHelper', async() => { + it('should migrate to calls to the __ngRendererCreateTextHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -893,7 +889,7 @@ describe('Renderer to Renderer2 migration', () => { 'return __ngRendererCreateTextHelper(this._renderer, this._element.nativeElement, value);'); }); - it('should declare the __ngRendererCreateTextHelper', async() => { + it('should declare the __ngRendererCreateTextHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -917,11 +913,10 @@ describe('Renderer to Renderer2 migration', () => { } `)); }); - }); describe('createTemplateAnchor migration', () => { - it('should migrate to calls to the __ngRendererCreateTemplateAnchorHelper', async() => { + it('should migrate to calls to the __ngRendererCreateTemplateAnchorHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -947,7 +942,7 @@ describe('Renderer to Renderer2 migration', () => { 'return __ngRendererCreateTemplateAnchorHelper(this._renderer, this._element.nativeElement);'); }); - it('should declare the __ngRendererCreateTemplateAnchorHelper', async() => { + it('should declare the __ngRendererCreateTemplateAnchorHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -971,11 +966,10 @@ describe('Renderer to Renderer2 migration', () => { } `)); }); - }); describe('projectNodes migration', () => { - it('should migrate to calls to the __ngRendererProjectNodesHelper', async() => { + it('should migrate to calls to the __ngRendererProjectNodesHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -997,7 +991,7 @@ describe('Renderer to Renderer2 migration', () => { '__ngRendererProjectNodesHelper(this._renderer, this._element.nativeElement, nodesToProject);'); }); - it('should declare the __ngRendererProjectNodesHelper', async() => { + it('should declare the __ngRendererProjectNodesHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -1019,11 +1013,10 @@ describe('Renderer to Renderer2 migration', () => { } `)); }); - }); describe('animate migration', () => { - it('should migrate to calls to the __ngRendererAnimateHelper', async() => { + it('should migrate to calls to the __ngRendererAnimateHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -1043,7 +1036,7 @@ describe('Renderer to Renderer2 migration', () => { expect(tree.readContent('/index.ts')).toContain('__ngRendererAnimateHelper();'); }); - it('should declare the __ngRendererAnimateHelper', async() => { + it('should declare the __ngRendererAnimateHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -1063,11 +1056,10 @@ describe('Renderer to Renderer2 migration', () => { } `)); }); - }); describe('destroyView migration', () => { - it('should migrate to calls to the __ngRendererDestroyViewHelper', async() => { + it('should migrate to calls to the __ngRendererDestroyViewHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -1088,7 +1080,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain('__ngRendererDestroyViewHelper(this._renderer, allNodes);'); }); - it('should declare the __ngRendererDestroyViewHelper', async() => { + it('should declare the __ngRendererDestroyViewHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -1116,7 +1108,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('detachView migration', () => { - it('should migrate to calls to the __ngRendererDetachViewHelper', async() => { + it('should migrate to calls to the __ngRendererDetachViewHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component } from '@angular/core'; @@ -1137,7 +1129,7 @@ describe('Renderer to Renderer2 migration', () => { .toContain('__ngRendererDetachViewHelper(this._renderer, rootNodes);'); }); - it('should declare the __ngRendererDetachViewHelper', async() => { + it('should declare the __ngRendererDetachViewHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component } from '@angular/core'; @@ -1166,7 +1158,7 @@ describe('Renderer to Renderer2 migration', () => { }); describe('attachViewAfter migration', () => { - it('should migrate to calls to the __ngRendererAttachViewAfterHelper', async() => { + it('should migrate to calls to the __ngRendererAttachViewAfterHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component, ElementRef } from '@angular/core'; @@ -1188,7 +1180,7 @@ describe('Renderer to Renderer2 migration', () => { '__ngRendererAttachViewAfterHelper(this._renderer, this._element.nativeElement, rootNodes);'); }); - it('should declare the __ngRendererAttachViewAfterHelper', async() => { + it('should declare the __ngRendererAttachViewAfterHelper', async () => { writeFile('/index.ts', ` import { Renderer, Component } from '@angular/core'; @@ -1225,5 +1217,7 @@ describe('Renderer to Renderer2 migration', () => { return runner.runSchematicAsync('migration-v9-renderer-to-renderer2', {}, tree).toPromise(); } - function stripWhitespace(contents: string) { return contents.replace(/\s/g, ''); } + function stripWhitespace(contents: string) { + return contents.replace(/\s/g, ''); + } }); diff --git a/packages/core/schematics/test/static_queries_migration_template_spec.ts b/packages/core/schematics/test/static_queries_migration_template_spec.ts index 341bb27866d54..31882e577016e 100644 --- a/packages/core/schematics/test/static_queries_migration_template_spec.ts +++ b/packages/core/schematics/test/static_queries_migration_template_spec.ts @@ -61,7 +61,9 @@ describe('static-queries migration with template strategy', () => { shx.rm('-r', tmpDirPath); }); - function writeFakeAngular() { writeFile('/node_modules/@angular/core/index.d.ts', ``); } + function writeFakeAngular() { + writeFile('/node_modules/@angular/core/index.d.ts', ``); + } function writeFakeLibrary(selectorName = 'my-lib-selector') { writeFile('/node_modules/my-lib/index.d.ts', `export * from './public-api';`); @@ -105,8 +107,7 @@ describe('static-queries migration with template strategy', () => { } describe('ViewChild', () => { - - it('should detect queries selecting elements through template reference', async() => { + it('should detect queries selecting elements through template reference', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -135,7 +136,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myStaticButton', { static: true }) query2: any;`); }); - it('should detect queries selecting ng-template as static', async() => { + it('should detect queries selecting ng-template as static', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -158,7 +159,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myTmpl', { static: true }) query: any;`); }); - it('should detect queries selecting ng-template as static (BOM)', async() => { + it('should detect queries selecting ng-template as static (BOM)', async () => { writeFile('/index.ts', `\uFEFF import {Component, NgModule, ViewChild} from '@angular/core'; @@ -181,8 +182,9 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myTmpl', { static: true }) query: any;`); }); - it('should detect queries selecting component view providers through string token', async() => { - writeFile('/index.ts', ` + it('should detect queries selecting component view providers through string token', + async () => { + writeFile('/index.ts', ` import {Component, Directive, NgModule, ViewChild} from '@angular/core'; @Directive({ @@ -211,22 +213,22 @@ describe('static-queries migration with template strategy', () => { export class MyModule {} `); - writeFile(`/my-tmpl.html`, ` + writeFile(`/my-tmpl.html`, ` <span myDirective></span> <ng-template> <span myDirective2></span> </ng-template> `); - await runMigration(); + await runMigration(); - expect(tree.readContent('/index.ts')) - .toContain(`@ViewChild('my-token', { static: true }) query: any;`); - expect(tree.readContent('/index.ts')) - .toContain(`@ViewChild('my-token-2', { static: false }) query2: any;`); - }); + expect(tree.readContent('/index.ts')) + .toContain(`@ViewChild('my-token', { static: true }) query: any;`); + expect(tree.readContent('/index.ts')) + .toContain(`@ViewChild('my-token-2', { static: false }) query2: any;`); + }); - it('should detect queries selecting component view providers using class token', async() => { + it('should detect queries selecting component view providers using class token', async () => { writeFile('/index.ts', ` import {Component, Directive, NgModule, ViewChild} from '@angular/core'; @@ -270,7 +272,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild(MyService2, { static: false }) query2: any;`); }); - it('should detect queries selecting component', async() => { + it('should detect queries selecting component', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; import {HomeComponent, HomeComponent2} from './home-comp'; @@ -316,7 +318,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild(HomeComponent2, { static: false }) query2: any;`); }); - it('should detect queries selecting third-party component', async() => { + it('should detect queries selecting third-party component', async () => { writeFakeLibrary(); writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -341,9 +343,10 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild(MyLibComponent, { static: true }) query: any;`); }); - it('should detect queries selecting third-party component with multiple selectors', async() => { - writeFakeLibrary('a-selector, test-selector'); - writeFile('/index.ts', ` + it('should detect queries selecting third-party component with multiple selectors', + async () => { + writeFakeLibrary('a-selector, test-selector'); + writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; import {MyLibComponent} from 'my-lib'; @@ -356,20 +359,20 @@ describe('static-queries migration with template strategy', () => { export class MyModule {} `); - writeFile('/my-tmpl.html', ` + writeFile('/my-tmpl.html', ` <a-selector>Match 1</a-selector> <ng-template> <test-selector>Match 2</test-selector> </ng-template> `); - await runMigration(); + await runMigration(); - expect(tree.readContent('/index.ts')) - .toContain(`@ViewChild(MyLibComponent, { static: false }) query: any;`); - }); + expect(tree.readContent('/index.ts')) + .toContain(`@ViewChild(MyLibComponent, { static: false }) query: any;`); + }); - it('should detect queries within structural directive', async() => { + it('should detect queries within structural directive', async () => { writeFile('/index.ts', ` import {Component, Directive, NgModule, ViewChild} from '@angular/core'; @@ -399,7 +402,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myRef2', { static: false }) query2: any;`); }); - it('should detect inherited queries', async() => { + it('should detect inherited queries', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -424,7 +427,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myRef', { static: true }) query: any;`); }); - it('should detect queries declared on setter', async() => { + it('should detect queries declared on setter', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -448,7 +451,7 @@ describe('static-queries migration with template strategy', () => { .toMatch(/@ViewChild\('myRef', { static: true }\)\s+set query/); }); - it('should detect queries declared on getter', async() => { + it('should detect queries declared on getter', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -473,7 +476,7 @@ describe('static-queries migration with template strategy', () => { .toMatch(/@ViewChild\('myRef', { static: true }\)\s+get query/); }); - it('should add a todo if a query is not declared in any component', async() => { + it('should add a todo if a query is not declared in any component', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild, SomeToken} from '@angular/core'; @@ -493,7 +496,7 @@ describe('static-queries migration with template strategy', () => { /^⮑ {3}index.ts@5:11:.+could not be determined.+not declared in any component/); }); - it('should add a todo if a query is used multiple times with different timing', async() => { + it('should add a todo if a query is used multiple times with different timing', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -523,7 +526,7 @@ describe('static-queries migration with template strategy', () => { it('should be able to migrate an application with type checking failure which ' + 'does not affect analysis', - async() => { + async () => { // Fakes the `@angular/package` by creating a `ViewChild` decorator // function that requires developers to specify the "static" flag. writeFile('/node_modules/@angular/core/index.d.ts', ` @@ -565,7 +568,7 @@ describe('static-queries migration with template strategy', () => { it('should be able to migrate applications with template type checking failure ' + 'which does not affect analysis', - async() => { + async () => { writeFile('/index.ts', ` import {NgModule, Component, ViewChild} from '@angular/core'; @@ -596,7 +599,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myRef', { static: true }) query: any;`); }); - it('should notify user if project has syntax errors which can affect analysis', async() => { + it('should notify user if project has syntax errors which can affect analysis', async () => { writeFile('/index.ts', ` import {Component, ViewChild} from '@angular/core'; @@ -630,7 +633,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myRef', { static: true }) query: any;`); }); - it('should gracefully exit migration if queries could not be analyzed', async() => { + it('should gracefully exit migration if queries could not be analyzed', async () => { writeFile('/index.ts', ` import {Component, ViewChild} from '@angular/core'; @@ -650,7 +653,7 @@ describe('static-queries migration with template strategy', () => { expect(errorOutput[0]).toMatch(/Cannot determine the module for class MyComp/); }); - it('should gracefully exit migration if AOT compiler throws exception', async() => { + it('should gracefully exit migration if AOT compiler throws exception', async () => { writeFile('/my-component.ts', ` import {Component, ViewChild} from '@angular/core'; @@ -691,7 +694,7 @@ describe('static-queries migration with template strategy', () => { expect(errorOutput[0]).toMatch(/^TypeError: Cannot read property 'module' of undefined/); }); - it('should add a todo for content queries which are not detectable', async() => { + it('should add a todo for content queries which are not detectable', async () => { writeFile('/index.ts', ` import {Component, NgModule, ContentChild} from '@angular/core'; @@ -713,7 +716,7 @@ describe('static-queries migration with template strategy', () => { .toMatch(/^⮑ {3}index.ts@6:11: Content queries cannot be migrated automatically\./); }); - it('should add a todo if query options cannot be migrated inline', async() => { + it('should add a todo if query options cannot be migrated inline', async () => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -738,7 +741,7 @@ describe('static-queries migration with template strategy', () => { expect(warnOutput[0]).toMatch(/Please manually set the query timing to.*static: true/); }); - it('should not normalize stylesheets which are referenced in component', async() => { + it('should not normalize stylesheets which are referenced in component', async () => { writeFile('sub_dir/index.ts', ` import {Component, NgModule, ContentChild} from '@angular/core'; @@ -765,7 +768,7 @@ describe('static-queries migration with template strategy', () => { expect(console.error).toHaveBeenCalledTimes(0); }); - it('should always use the test migration strategy for test tsconfig files', async() => { + it('should always use the test migration strategy for test tsconfig files', async () => { writeFile('/src/tsconfig.spec.json', JSON.stringify({ compilerOptions: { experimentalDecorators: true, @@ -812,7 +815,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('test', { static: true }) query: any;`); }); - it('should not fall back to test strategy if selected strategy fails', async() => { + it('should not fall back to test strategy if selected strategy fails', async () => { writeFile('/src/tsconfig.spec.json', JSON.stringify({ compilerOptions: { experimentalDecorators: true, diff --git a/packages/core/schematics/test/static_queries_migration_usage_spec.ts b/packages/core/schematics/test/static_queries_migration_usage_spec.ts index 1ec96166486e7..e5738dea4c91d 100644 --- a/packages/core/schematics/test/static_queries_migration_usage_spec.ts +++ b/packages/core/schematics/test/static_queries_migration_usage_spec.ts @@ -63,7 +63,7 @@ describe('static-queries migration with usage strategy', () => { describe('ViewChild', () => { createQueryTests('ViewChild'); - it('should mark view queries used in "ngAfterContentInit" as static', async() => { + it('should mark view queries used in "ngAfterContentInit" as static', async () => { writeFile('/index.ts', ` import {Component, ViewChild} from '@angular/core'; @@ -83,7 +83,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@ViewChild('test', { static: true }) query: any;`); }); - it('should mark view queries used in "ngAfterContentChecked" as static', async() => { + it('should mark view queries used in "ngAfterContentChecked" as static', async () => { writeFile('/index.ts', ` import {Component, ViewChild} from '@angular/core'; @@ -107,7 +107,7 @@ describe('static-queries migration with usage strategy', () => { describe('ContentChild', () => { createQueryTests('ContentChild'); - it('should not mark content queries used in "ngAfterContentInit" as static', async() => { + it('should not mark content queries used in "ngAfterContentInit" as static', async () => { writeFile('/index.ts', ` import {Component, ContentChild} from '@angular/core'; @@ -127,7 +127,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@ContentChild('test', { static: false }) query: any;`); }); - it('should not mark content queries used in "ngAfterContentInit" as static (BOM)', async() => { + it('should not mark content queries used in "ngAfterContentInit" as static (BOM)', async () => { writeFile('/index.ts', `\uFEFF import {Component, ContentChild} from '@angular/core'; @@ -147,7 +147,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@ContentChild('test', { static: false }) query: any;`); }); - it('should not mark content queries used in "ngAfterContentChecked" as static', async() => { + it('should not mark content queries used in "ngAfterContentChecked" as static', async () => { writeFile('/index.ts', ` import {Component, ContentChild} from '@angular/core'; @@ -176,8 +176,8 @@ describe('static-queries migration with usage strategy', () => { return runner.runSchematicAsync('migration-v8-static-queries', {}, tree).toPromise(); } - function createQueryTests(queryType: 'ViewChild' | 'ContentChild') { - it('should mark queries as dynamic', async() => { + function createQueryTests(queryType: 'ViewChild'|'ContentChild') { + it('should mark queries as dynamic', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -200,7 +200,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('dynamic', { static: false }) dynamic: any`); }); - it('should mark queries used in "ngOnChanges" as static', async() => { + it('should mark queries used in "ngOnChanges" as static', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -220,7 +220,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should mark queries used in "ngOnInit" as static', async() => { + it('should mark queries used in "ngOnInit" as static', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -240,7 +240,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should mark queries used in "ngDoCheck" as static', async() => { + it('should mark queries used in "ngDoCheck" as static', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -260,7 +260,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should keep existing query options when updating timing', async() => { + it('should keep existing query options when updating timing', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -280,7 +280,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { /* test */ read: null, static: true }) query: any;`); }); - it('should add a todo for queries declared on setter', async() => { + it('should add a todo for queries declared on setter', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -300,7 +300,7 @@ describe('static-queries migration with usage strategy', () => { .toMatch(/index.ts@6:11: Queries defined on accessors cannot be analyzed.$/); }); - it('should add a todo for queries declared on getter', async() => { + it('should add a todo for queries declared on getter', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -321,7 +321,7 @@ describe('static-queries migration with usage strategy', () => { .toMatch(/index.ts@6:11: Queries defined on accessors cannot be analyzed.$/); }); - it('should not overwrite existing explicit query timing', async() => { + it('should not overwrite existing explicit query timing', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -337,7 +337,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', {static: /* untouched */ someVal}) query: any;`); }); - it('should detect queries used in deep method chain', async() => { + it('should detect queries used in deep method chain', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -372,7 +372,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should properly exit if recursive function is analyzed', async() => { + it('should properly exit if recursive function is analyzed', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -396,7 +396,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should detect queries used in newly instantiated classes', async() => { + it('should detect queries used in newly instantiated classes', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -435,7 +435,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query2: any;`); }); - it('should detect queries used in parenthesized new expressions', async() => { + it('should detect queries used in parenthesized new expressions', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -461,7 +461,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect queries in lifecycle hook with string literal name', async() => { + it('should detect queries in lifecycle hook with string literal name', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -481,7 +481,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect static queries within nested inheritance', async() => { + it('should detect static queries within nested inheritance', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -506,7 +506,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect static queries used within input setters', async() => { + it('should detect static queries used within input setters', async () => { writeFile('/index.ts', ` import {Component, Input, ${queryType}} from '@angular/core'; @@ -528,7 +528,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect inputs defined in metadata', async() => { + it('should detect inputs defined in metadata', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -554,7 +554,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect aliased inputs declared in metadata', async() => { + it('should detect aliased inputs declared in metadata', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -577,7 +577,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark query as static if query is used in non-input setter', async() => { + it('should not mark query as static if query is used in non-input setter', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -597,7 +597,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should detect input decorator on setter', async() => { + it('should detect input decorator on setter', async () => { writeFile('/index.ts', ` import {Input, Component, ${queryType}} from '@angular/core'; @@ -622,7 +622,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect setter inputs in derived classes', async() => { + it('should detect setter inputs in derived classes', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -647,7 +647,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should properly detect static query in external derived class', async() => { + it('should properly detect static query in external derived class', async () => { writeFile('/src/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -680,7 +680,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark queries used in promises as static', async() => { + it('should not mark queries used in promises as static', async () => { writeFile('/es2015.dom.d.ts', ` interface PromiseConstructor { resolve(): Promise; @@ -735,7 +735,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query2: any;`); }); - it('should handle function callbacks which statically access queries', async() => { + it('should handle function callbacks which statically access queries', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -764,7 +764,7 @@ describe('static-queries migration with usage strategy', () => { }); it('should handle class instantiations with specified callbacks that access queries', - async() => { + async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; import {External} from './external'; @@ -794,7 +794,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should handle nested functions with arguments from parent closure', async() => { + it('should handle nested functions with arguments from parent closure', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -823,7 +823,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark queries used in setTimeout as static', async() => { + it('should not mark queries used in setTimeout as static', async () => { writeFile('/lib.dom.d.ts', `declare function setTimeout(cb: Function);`); writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -859,7 +859,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) query3: any;`); }); - it('should not mark queries used in "addEventListener" as static', async() => { + it('should not mark queries used in "addEventListener" as static', async () => { writeFile('/lib.dom.d.ts', ` interface HTMLElement { addEventListener(cb: Function); @@ -888,7 +888,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should not mark queries used in "requestAnimationFrame" as static', async() => { + it('should not mark queries used in "requestAnimationFrame" as static', async () => { writeFile('/lib.dom.d.ts', `declare function requestAnimationFrame(cb: Function);`); writeFile('/index.ts', ` import {Component, ElementRef, ${queryType}} from '@angular/core'; @@ -913,8 +913,9 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should mark queries used in immediately-invoked function expression as static', async() => { - writeFile('/index.ts', ` + it('should mark queries used in immediately-invoked function expression as static', + async () => { + writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @Component({template: '<span #test></span>'}) @@ -934,15 +935,15 @@ describe('static-queries migration with usage strategy', () => { } `); - await runMigration(); + await runMigration(); - expect(tree.readContent('/index.ts')) - .toContain(`@${queryType}('test', { static: true }) query: any;`); - expect(tree.readContent('/index.ts')) - .toContain(`@${queryType}('test', { static: true }) query2: any;`); - }); + expect(tree.readContent('/index.ts')) + .toContain(`@${queryType}('test', { static: true }) query: any;`); + expect(tree.readContent('/index.ts')) + .toContain(`@${queryType}('test', { static: true }) query2: any;`); + }); - it('should detect static queries used in external function-like declaration', async() => { + it('should detect static queries used in external function-like declaration', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; import {externalFn} from './external'; @@ -971,7 +972,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect static queries used through getter property access', async() => { + it('should detect static queries used through getter property access', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -995,7 +996,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect static queries used through external getter access', async() => { + it('should detect static queries used through external getter access', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; import {External} from './external'; @@ -1033,8 +1034,9 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark queries as static if a value is assigned to accessor property', async() => { - writeFile('/index.ts', ` + it('should not mark queries as static if a value is assigned to accessor property', + async () => { + writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @Component({template: '<span #test></span>'}) @@ -1052,13 +1054,13 @@ describe('static-queries migration with usage strategy', () => { } `); - await runMigration(); + await runMigration(); - expect(tree.readContent('/index.ts')) - .toContain(`@${queryType}('test', { static: false }) query: any;`); - }); + expect(tree.readContent('/index.ts')) + .toContain(`@${queryType}('test', { static: false }) query: any;`); + }); - it('should mark queries as static if non-input setter uses query', async() => { + it('should mark queries as static if non-input setter uses query', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1083,7 +1085,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should check setter and getter when using compound assignment', async() => { + it('should check setter and getter when using compound assignment', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1111,7 +1113,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query2: any;`); }); - it('should check getters when using comparison operator in binary expression', async() => { + it('should check getters when using comparison operator in binary expression', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1136,7 +1138,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should check derived abstract class methods', async() => { + it('should check derived abstract class methods', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1172,7 +1174,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect queries accessed through deep abstract class method', async() => { + it('should detect queries accessed through deep abstract class method', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1204,7 +1206,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect queries accessed through abstract property getter', async() => { + it('should detect queries accessed through abstract property getter', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1230,7 +1232,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect queries accessed through abstract property setter', async() => { + it('should detect queries accessed through abstract property setter', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1257,8 +1259,9 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect query usage in abstract class methods accessing inherited query', async() => { - writeFile('/index.ts', ` + it('should detect query usage in abstract class methods accessing inherited query', + async () => { + writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; export abstract class RootBaseClass { @@ -1287,13 +1290,13 @@ describe('static-queries migration with usage strategy', () => { } `); - await runMigration(); + await runMigration(); - expect(tree.readContent('/index.ts')) - .toContain(`@${queryType}('test', { static: true }) query: any;`); - }); + expect(tree.readContent('/index.ts')) + .toContain(`@${queryType}('test', { static: true }) query: any;`); + }); - it('should detect query usage within component template', async() => { + it('should detect query usage within component template', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1314,8 +1317,9 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect query usage with nested property read within component template', async() => { - writeFile('/index.ts', ` + it('should detect query usage with nested property read within component template', + async () => { + writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @Component({templateUrl: 'my-template.html'}) @@ -1324,19 +1328,19 @@ describe('static-queries migration with usage strategy', () => { } `); - writeFile(`/my-template.html`, ` + writeFile(`/my-template.html`, ` <foo #test></foo> <comp [dir]="query.someProperty"></comp> `); - await runMigration(); + await runMigration(); - expect(tree.readContent('/index.ts')) - .toContain(`@${queryType}('test', { static: true }) query: any;`); - }); + expect(tree.readContent('/index.ts')) + .toContain(`@${queryType}('test', { static: true }) query: any;`); + }); it('should not mark query as static if template has template reference with same name', - async() => { + async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1360,7 +1364,7 @@ describe('static-queries migration with usage strategy', () => { }); it('should not mark query as static if template has property read with query name but different receiver', - async() => { + async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1385,7 +1389,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) someProp: any;`); }); - it('should ignore queries accessed within <ng-template> element', async() => { + it('should ignore queries accessed within <ng-template> element', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1409,7 +1413,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should detect inherited queries used in templates', async() => { + it('should detect inherited queries used in templates', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1433,7 +1437,7 @@ describe('static-queries migration with usage strategy', () => { }); it('should mark queries which could be accessed statically within third-party calls as ambiguous', - async() => { + async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; import {thirdPartySync} from 'my-lib'; @@ -1458,18 +1462,18 @@ describe('static-queries migration with usage strategy', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain( - `@${queryType}('test', /* TODO: check static flag */ { static: true }) query: any;`); + .toContain(`@${ + queryType}('test', /* TODO: check static flag */ { static: true }) query: any;`); expect(tree.readContent('/index.ts')) - .toContain( - `@${queryType}('test', /* TODO: check static flag */ { static: true }) query2: any;`); + .toContain(`@${ + queryType}('test', /* TODO: check static flag */ { static: true }) query2: any;`); expect(warnOutput.length).toBe(2); expect(warnOutput[0]).toContain('Query timing is ambiguous.'); expect(warnOutput[1]).toContain('Query timing is ambiguous.'); }); it('should mark queries which could be accessed statically within third-party new expressions as ambiguous', - async() => { + async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; import {ThirdParty} from 'my-lib'; @@ -1493,15 +1497,15 @@ describe('static-queries migration with usage strategy', () => { await runMigration(); expect(tree.readContent('/index.ts')) - .toContain( - `@${queryType}('test', /* TODO: check static flag */ { static: true }) query: any;`); + .toContain(`@${ + queryType}('test', /* TODO: check static flag */ { static: true }) query: any;`); expect(warnOutput.length).toBe(1); expect(warnOutput[0]) .toContain( 'Query timing is ambiguous. Please check if the query can be marked as dynamic'); }); - it('should properly handle multiple tsconfig files', async() => { + it('should properly handle multiple tsconfig files', async () => { writeFile('/src/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1526,7 +1530,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should support function call with default parameter value', async() => { + it('should support function call with default parameter value', async () => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; diff --git a/packages/core/schematics/test/template_var_assignment_migration_spec.ts b/packages/core/schematics/test/template_var_assignment_migration_spec.ts index a7d0f30e86c38..54a97867e384b 100644 --- a/packages/core/schematics/test/template_var_assignment_migration_spec.ts +++ b/packages/core/schematics/test/template_var_assignment_migration_spec.ts @@ -62,7 +62,7 @@ describe('template variable assignment migration', () => { return runner.runSchematicAsync('migration-v8-template-local-variables', {}, tree).toPromise(); } - it('should warn for two-way data binding variable assignment', async() => { + it('should warn for two-way data binding variable assignment', async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -78,7 +78,7 @@ describe('template variable assignment migration', () => { expect(warnOutput[0]).toMatch(/^⮑ {3}index.ts@5:69: Found assignment/); }); - it('should warn for two-way data binding assigning to "as" variable', async() => { + it('should warn for two-way data binding assigning to "as" variable', async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -100,7 +100,7 @@ describe('template variable assignment migration', () => { expect(warnOutput).toMatch(/^⮑ {3}tmpl.html@3:31: Found assignment/); }); - it('should warn for bound event assignments to "as" variable', async() => { + it('should warn for bound event assignments to "as" variable', async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -124,7 +124,7 @@ describe('template variable assignment migration', () => { expect(warnOutput[1]).toMatch(/^⮑ {3}sub_dir\/tmpl.html@4:25: Found assignment/); }); - it('should warn for bound event assignments to template "let" variables', async() => { + it('should warn for bound event assignments to template "let" variables', async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -148,7 +148,7 @@ describe('template variable assignment migration', () => { expect(warnOutput[1]).toMatch(/^⮑ {3}sub_dir\/tmpl.html@4:25: Found assignment/); }); - it('should not warn for bound event assignments to component property', async() => { + it('should not warn for bound event assignments to component property', async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -166,7 +166,7 @@ describe('template variable assignment migration', () => { }); it('should not warn for bound event assignments to template variable object property', - async() => { + async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -186,7 +186,7 @@ describe('template variable assignment migration', () => { }); it('should not warn for property writes with template variable name but different receiver', - async() => { + async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -211,7 +211,7 @@ describe('template variable assignment migration', () => { expect(warnOutput.length).toBe(0); }); - it('should warn for template variable assignments in expression conditional', async() => { + it('should warn for template variable assignments in expression conditional', async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -236,7 +236,7 @@ describe('template variable assignment migration', () => { }); it('should not warn for property writes with template variable name but different scope', - async() => { + async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -259,7 +259,7 @@ describe('template variable assignment migration', () => { }); - it('should not throw an error if a detected template fails parsing', async() => { + it('should not throw an error if a detected template fails parsing', async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; @@ -276,7 +276,7 @@ describe('template variable assignment migration', () => { expect(warnOutput.length).toBe(0); }); - it('should be able to report multiple templates within the same source file', async() => { + it('should be able to report multiple templates within the same source file', async () => { writeFile('/index.ts', ` import {Component} from '@angular/core'; diff --git a/packages/core/schematics/test/undecorated_classes_with_decorated_fields_migration_spec.ts b/packages/core/schematics/test/undecorated_classes_with_decorated_fields_migration_spec.ts index ed04c675d17dd..e82adc24627b0 100644 --- a/packages/core/schematics/test/undecorated_classes_with_decorated_fields_migration_spec.ts +++ b/packages/core/schematics/test/undecorated_classes_with_decorated_fields_migration_spec.ts @@ -42,7 +42,7 @@ describe('Undecorated classes with decorated fields migration', () => { shx.rm('-r', tmpDirPath); }); - it(`should add an import for Directive if there isn't one already`, async() => { + it(`should add an import for Directive if there isn't one already`, async () => { writeFile('/index.ts', ` import { Input } from '@angular/core'; @@ -56,7 +56,7 @@ describe('Undecorated classes with decorated fields migration', () => { .toContain(`import { Input, Directive } from '@angular/core';`); }); - it('should not change the imports if there is an import for Directive already', async() => { + it('should not change the imports if there is an import for Directive already', async () => { writeFile('/index.ts', ` import { Directive, Input } from '@angular/core'; @@ -74,7 +74,29 @@ describe('Undecorated classes with decorated fields migration', () => { .toContain(`import { Directive, Input } from '@angular/core';`); }); - it('should add @Directive to undecorated classes that have @Input', async() => { + it('should not generate conflicting imports there is a different `Directive` symbol', + async () => { + writeFile('/index.ts', ` + import { HostBinding } from '@angular/core'; + + export class Directive { + // Simulates a scenario where a library defines a class named "Directive". + // We don't want to generate a conflicting import. + } + + export class MyLibrarySharedBaseClass { + @HostBinding('class.active') isActive: boolean; + } + `); + + await runMigration(); + const fileContent = tree.readContent('/index.ts'); + expect(fileContent) + .toContain(`import { HostBinding, Directive as Directive_1 } from '@angular/core';`); + expect(fileContent).toMatch(/@Directive_1\(\)\s+export class MyLibrarySharedBaseClass/); + }); + + it('should add @Directive to undecorated classes that have @Input', async () => { writeFile('/index.ts', ` import { Input } from '@angular/core'; @@ -87,7 +109,7 @@ describe('Undecorated classes with decorated fields migration', () => { expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); - it('should not change decorated classes', async() => { + it('should not change decorated classes', async () => { writeFile('/index.ts', ` import { Input, Component, Output, EventEmitter } from '@angular/core'; @@ -109,7 +131,7 @@ describe('Undecorated classes with decorated fields migration', () => { expect(content).toContain(`@Directive()\nexport class Child extends Base {`); }); - it('should add @Directive to undecorated classes that have @Output', async() => { + it('should add @Directive to undecorated classes that have @Output', async () => { writeFile('/index.ts', ` import { Output, EventEmitter } from '@angular/core'; @@ -122,7 +144,7 @@ describe('Undecorated classes with decorated fields migration', () => { expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); - it('should add @Directive to undecorated classes that have a host binding', async() => { + it('should add @Directive to undecorated classes that have a host binding', async () => { writeFile('/index.ts', ` import { HostBinding } from '@angular/core'; @@ -138,7 +160,7 @@ describe('Undecorated classes with decorated fields migration', () => { expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); - it('should add @Directive to undecorated classes that have a host listener', async() => { + it('should add @Directive to undecorated classes that have a host listener', async () => { writeFile('/index.ts', ` import { HostListener } from '@angular/core'; @@ -154,7 +176,7 @@ describe('Undecorated classes with decorated fields migration', () => { expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); - it('should add @Directive to undecorated classes that have a ViewChild query', async() => { + it('should add @Directive to undecorated classes that have a ViewChild query', async () => { writeFile('/index.ts', ` import { ViewChild, ElementRef } from '@angular/core'; @@ -167,7 +189,7 @@ describe('Undecorated classes with decorated fields migration', () => { expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); - it('should add @Directive to undecorated classes that have a ViewChildren query', async() => { + it('should add @Directive to undecorated classes that have a ViewChildren query', async () => { writeFile('/index.ts', ` import { ViewChildren, ElementRef } from '@angular/core'; @@ -180,7 +202,7 @@ describe('Undecorated classes with decorated fields migration', () => { expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); - it('should add @Directive to undecorated classes that have a ContentChild query', async() => { + it('should add @Directive to undecorated classes that have a ContentChild query', async () => { writeFile('/index.ts', ` import { ContentChild, ElementRef } from '@angular/core'; @@ -193,7 +215,7 @@ describe('Undecorated classes with decorated fields migration', () => { expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); - it('should add @Directive to undecorated classes that have a ContentChildren query', async() => { + it('should add @Directive to undecorated classes that have a ContentChildren query', async () => { writeFile('/index.ts', ` import { ContentChildren, ElementRef } from '@angular/core'; @@ -206,6 +228,92 @@ describe('Undecorated classes with decorated fields migration', () => { expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`); }); + it('should add @Directive to undecorated derived classes of a migrated class', async () => { + writeFile('/index.ts', ` + import { Input, Directive, NgModule } from '@angular/core'; + + export class Base { + @Input() isActive: boolean; + } + + export class DerivedA extends Base {} + export class DerivedB extends DerivedA {} + export class DerivedC extends DerivedB {} + + @Directive({selector: 'my-comp'}) + export class MyComp extends DerivedC {} + + export class MyCompWrapped extends MyComp {} + + @NgModule({declarations: [MyComp, MyCompWrapped]}) + export class AppModule {} + `); + + await runMigration(); + const fileContent = tree.readContent('/index.ts'); + expect(fileContent).toContain(`import { Input, Directive, NgModule } from '@angular/core';`); + expect(fileContent).toMatch(/@Directive\(\)\s+export class Base/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedA/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedB/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedC/); + expect(fileContent).toMatch(/}\s+@Directive\(\{selector: 'my-comp'}\)\s+export class MyComp/); + expect(fileContent).toMatch(/}\s+export class MyCompWrapped/); + }); + + it('should add @Directive to derived undecorated classes of abstract directives', async () => { + writeFile('/index.ts', ` + import { Input, Directive, NgModule } from '@angular/core'; + + @Directive() + export class Base { + // ... + } + + export class DerivedA extends Base {} + export class DerivedB extends DerivedA {} + export class DerivedC extends DerivedB {} + + @Directive({selector: 'my-comp'}) + export class MyComp extends DerivedC {} + + export class MyCompWrapped extends MyComp {} + + @NgModule({declarations: [MyComp, MyCompWrapped]}) + export class AppModule {} + `); + + await runMigration(); + const fileContent = tree.readContent('/index.ts'); + expect(fileContent).toContain(`import { Input, Directive, NgModule } from '@angular/core';`); + expect(fileContent).toMatch(/core';\s+@Directive\(\)\s+export class Base/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedA/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedB/); + expect(fileContent).toMatch(/@Directive\(\)\s+export class DerivedC/); + expect(fileContent).toMatch(/}\s+@Directive\(\{selector: 'my-comp'}\)\s+export class MyComp/); + expect(fileContent).toMatch(/}\s+export class MyCompWrapped/); + }); + + it('should not throw if undecorated class extends from unresolved declaration', async () => { + writeFile('/lib.d.ts', ` + // Fakes the ES5 error default lib types. Since we are in a virtual tree, + // the default lib types from TypeScript are not available. + interface ErrorConstructor {} + declare var Error: ErrorConstructor; + `); + writeFile('/index.ts', ` + export class MyCustomErrorClass extends Error {} + `); + + let error: any = null; + try { + await runMigration(); + } catch (e) { + error = e; + } + + expect(error).toBe(null); + }); + function writeFile(filePath: string, contents: string) { host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); } diff --git a/packages/core/schematics/test/undecorated_classes_with_di_migration_spec.ts b/packages/core/schematics/test/undecorated_classes_with_di_migration_spec.ts index 997dca5b52112..c67771226167e 100644 --- a/packages/core/schematics/test/undecorated_classes_with_di_migration_spec.ts +++ b/packages/core/schematics/test/undecorated_classes_with_di_migration_spec.ts @@ -83,7 +83,7 @@ describe('Undecorated classes with DI migration', () => { `); } - it('should print a failure message base class is declared through type definition', async() => { + it('should print a failure message base class is declared through type definition', async () => { writeFile('/node_modules/my-lib/package.json', JSON.stringify({ version: '0.0.0', main: './index.js', @@ -121,7 +121,7 @@ describe('Undecorated classes with DI migration', () => { 'dependency injection. Please manually fix the following failures'); }); - it('should add @Directive() decorator to extended base class', async() => { + it('should add @Directive() decorator to extended base class', async () => { writeFile('/index.ts', ` import {Component, NgModule, NgZone} from '@angular/core'; @@ -149,8 +149,8 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')).toMatch(/@Directive\(\)\nexport class BaseClass2 {/); }); - it('not decorated base class multiple times if extended multiple times', async() => { - writeFile('/index.ts', dedent ` + it('not decorated base class multiple times if extended multiple times', async () => { + writeFile('/index.ts', dedent` import {Component, NgModule, NgZone} from '@angular/core'; export class BaseClass { @@ -169,7 +169,7 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Directive() export class BaseClass { @@ -177,7 +177,7 @@ describe('Undecorated classes with DI migration', () => { }`); }); - it('should add @Injectable() decorator to extended base class', async() => { + it('should add @Injectable() decorator to extended base class', async () => { writeFile('/index.ts', ` import {Injectable, NgModule, NgZone} from '@angular/core'; @@ -197,8 +197,8 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')).toMatch(/@Injectable\(\)\nexport class BaseClass {/); }); - it('should not decorate base class for decorated pipe', async() => { - writeFile('/index.ts', dedent ` + it('should not decorate base class for decorated pipe', async () => { + writeFile('/index.ts', dedent` import {Component, NgModule, Pipe, PipeTransform} from '@angular/core'; @Pipe({name: 'test'}) @@ -213,13 +213,13 @@ describe('Undecorated classes with DI migration', () => { expect(errorOutput.length).toBe(0); expect(warnOutput.length).toBe(0); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Pipe({name: 'test'}) export class MyPipe extends PipeTransform {}`); }); - it('should not decorate base class if no constructor is inherited', async() => { - writeFile('/index.ts', dedent ` + it('should not decorate base class if no constructor is inherited', async () => { + writeFile('/index.ts', dedent` import {Component, NgModule, Directive} from '@angular/core'; export class BaseClassWithoutCtor { @@ -238,7 +238,7 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` export class BaseClassWithoutCtor { someUnrelatedProp = true; @@ -252,8 +252,8 @@ describe('Undecorated classes with DI migration', () => { }); it('should not decorate base class if directive/component/provider defines a constructor', - async() => { - writeFile('/index.ts', dedent ` + async () => { + writeFile('/index.ts', dedent` import {Component, Injectable, NgModule, NgZone} from '@angular/core'; export class BaseClass { @@ -284,15 +284,15 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` export class BaseClass { constructor(zone: NgZone) {} }`); }); - it('should not decorate base class if it already has decorator', async() => { - writeFile('/index.ts', dedent ` + it('should not decorate base class if it already has decorator', async () => { + writeFile('/index.ts', dedent` import {Component, Directive, NgModule, NgZone} from '@angular/core'; @Directive({selector: 'base-class'}) @@ -312,13 +312,13 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Directive({selector: 'base-class'}) export class BaseClass {`); }); - it('should add a comment if the base class is declared through type definition', async() => { + it('should add a comment if the base class is declared through type definition', async () => { writeFile('/node_modules/my-lib/package.json', JSON.stringify({ version: '0.0.0', main: './index.js', @@ -332,7 +332,7 @@ describe('Undecorated classes with DI migration', () => { } `); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {Component, Injectable, NgModule} from '@angular/core'; import {SuperBaseClass} from 'my-lib'; @@ -365,42 +365,42 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Directive() export class BaseClass extends SuperBaseClass { // TODO: add explicit constructor }`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Injectable() export class BaseClass2 extends SuperBaseClass { // TODO: add explicit constructor }`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Directive() export class PassThroughClass extends BaseClass {}`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({template: ''}) export class MyComponent extends PassThroughClass {}`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({template: ''}) export class MyComponent3 extends SuperBaseClass { // TODO: add explicit constructor }`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Injectable() export class MyService extends BaseClass2 {}`); }); it('should not add a comment if the base class is declared through type definition but is' + 'decorated', - async() => { + async () => { writeFakeLibrary(); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {Component, NgModule} from '@angular/core'; import {BaseComponent} from 'my-lib'; @@ -413,14 +413,14 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({template: ''}) export class MyComponent extends BaseComponent {}`); }); - it('should not decorate base class in typings if it misses an explicit constructor', async() => { + it('should not decorate base class in typings if it misses an explicit constructor', async () => { writeFakeLibrary(); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {Component, NgModule} from '@angular/core'; import {BaseDirective} from 'my-lib'; @@ -433,16 +433,16 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({template: ''}) export class MyComponent extends BaseDirective {}`); expect(tree.readContent('/node_modules/my-lib/public-api.d.ts')).not.toContain('@Directive'); }); - it('should detect decorated classes by respecting summary files', async() => { + it('should detect decorated classes by respecting summary files', async () => { writeSummaryOnlyThirdPartyLibrary(); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {Component, NgModule} from '@angular/core'; import {BaseComponent} from 'my-lib'; @@ -457,12 +457,12 @@ describe('Undecorated classes with DI migration', () => { expect(warnOutput.length).toBe(0); expect(errorOutput.length).toBe(0); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({template: ''}) export class MyComponent extends BaseComponent {}`); }); - it('should decorate all undecorated directives of inheritance chain', async() => { + it('should decorate all undecorated directives of inheritance chain', async () => { writeFile('/index.ts', ` import {Component, NgModule, NgZone} from '@angular/core'; @@ -486,7 +486,7 @@ describe('Undecorated classes with DI migration', () => { .toMatch(/}\s+@Directive\(\)\nexport class BaseClass extends SuperBaseClass {/); }); - it('should decorate all undecorated providers of inheritance chain', async() => { + it('should decorate all undecorated providers of inheritance chain', async () => { writeFile('/index.ts', ` import {Injectable, NgModule, NgZone} from '@angular/core'; @@ -511,7 +511,7 @@ describe('Undecorated classes with DI migration', () => { }); it('should properly update import if @Directive can be accessed through existing namespace import', - async() => { + async () => { writeFile('/index.ts', ` import {Component, NgModule, NgZone} from '@angular/core'; import {BaseClass} from './base'; @@ -537,7 +537,7 @@ describe('Undecorated classes with DI migration', () => { }); it('should properly update existing import with aliased specifier if identifier is already used', - async() => { + async () => { writeFile('/index.ts', ` import {Component, NgModule, NgZone} from '@angular/core'; import {Directive} from './third_party_directive'; @@ -561,7 +561,7 @@ describe('Undecorated classes with DI migration', () => { }); it('should properly create new import with aliased specifier if identifier is already used', - async() => { + async () => { writeFile('/index.ts', ` import {Component, NgModule, NgZone} from '@angular/core'; import {BaseClass} from './base'; @@ -590,8 +590,9 @@ describe('Undecorated classes with DI migration', () => { .toContain(`{ Directive as Directive_1 } from "@angular/core";`); }); - it('should use existing aliased import of @Directive instead of creating new import', async() => { - writeFile('/index.ts', ` + it('should use existing aliased import of @Directive instead of creating new import', + async () => { + writeFile('/index.ts', ` import {Component, NgModule} from '@angular/core'; import {BaseClass} from './base'; @@ -602,7 +603,7 @@ describe('Undecorated classes with DI migration', () => { export class MyModule {} `); - writeFile('/base.ts', ` + writeFile('/base.ts', ` import {Directive as AliasedDir, NgZone} from '@angular/core'; export class BaseClass { @@ -610,15 +611,14 @@ describe('Undecorated classes with DI migration', () => { } `); - await runMigration(); - - expect(tree.readContent('/base.ts')).toMatch(/@AliasedDir\(\)\nexport class BaseClass {/); - }); + await runMigration(); - describe('decorator copying', async() => { + expect(tree.readContent('/base.ts')).toMatch(/@AliasedDir\(\)\nexport class BaseClass {/); + }); - it('should be able to copy the "templateUrl" field', async() => { - writeFile('/index.ts', dedent ` + describe('decorator copying', async () => { + it('should be able to copy the "templateUrl" field', async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -628,7 +628,7 @@ describe('Undecorated classes with DI migration', () => { export class MyModule {} `); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Directive, NgModule} from '@angular/core'; @Directive({ @@ -645,7 +645,7 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')) .toContain(`import { NgModule, Directive } from '@angular/core';`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Directive({ selector: 'my-dir', templateUrl: './my-dir.html' @@ -653,8 +653,8 @@ describe('Undecorated classes with DI migration', () => { export class MyDir extends BaseClass {}`); }); - it('should be able to copy the "styleUrls" field', async() => { - writeFile('/index.ts', dedent ` + it('should be able to copy the "styleUrls" field', async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -664,7 +664,7 @@ describe('Undecorated classes with DI migration', () => { export class MyModule {} `); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Directive, NgModule} from '@angular/core'; /** my comment */ @@ -680,7 +680,7 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` import {BaseClass} from './lib/base'; @Directive({ @@ -690,8 +690,8 @@ describe('Undecorated classes with DI migration', () => { export class MyDir extends BaseClass {}`); }); - it('should be able to copy @Pipe decorator', async() => { - writeFile('/index.ts', dedent ` + it('should be able to copy @Pipe decorator', async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BasePipe} from './lib/base'; @@ -701,7 +701,7 @@ describe('Undecorated classes with DI migration', () => { export class MyModule {} `); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Pipe, NgModule} from '@angular/core'; @Pipe({name: 'my-pipe-name'}) @@ -715,16 +715,16 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')) .toContain(`import { NgModule, Pipe } from '@angular/core';`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Pipe({ name: 'my-pipe-name' }) export class MyPipe extends BasePipe {}`); }); - it('should be able to copy decorator in same source file', async() => { + it('should be able to copy decorator in same source file', async () => { writeFile( '/node_modules/@angular/cdk/table/index.d.ts', `export declare const CDK_TABLE_TEMPLATE = '';`); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {NgModule, Component} from '@angular/core'; import {CDK_TABLE_TEMPLATE} from '@angular/cdk/table'; @@ -748,7 +748,7 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ selector: 'my-dir', template: CDK_TABLE_TEMPLATE, @@ -757,8 +757,8 @@ describe('Undecorated classes with DI migration', () => { export class MyDir extends BaseClass {}`); }); - it('should be able to create new imports for copied identifier references', async() => { - writeFile('/index.ts', dedent ` + it('should be able to create new imports for copied identifier references', async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -772,7 +772,7 @@ describe('Undecorated classes with DI migration', () => { '/node_modules/@angular/cdk/table/index.d.ts', `export declare const CDK_TABLE_TEMPLATE = '';`); writeFile('/styles.ts', `export const STYLE_THROUGH_VAR = 'external';`); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Component, NgModule} from '@angular/core'; import {CDK_TABLE_TEMPLATE as tableTmpl} from '@angular/cdk/table'; import {STYLE_THROUGH_VAR} from '../styles'; @@ -798,7 +798,7 @@ describe('Undecorated classes with DI migration', () => { .toContain(`import { STYLE_THROUGH_VAR } from "./styles";`); expect(tree.readContent('/index.ts')) .toContain(`import { BaseClass, LOCAL_STYLE } from './lib/base';`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ selector: 'my-dir', template: CDK_TABLE_TEMPLATE, @@ -807,8 +807,8 @@ describe('Undecorated classes with DI migration', () => { export class MyDir extends BaseClass {}`); }); - it('should copy decorator once if directive is referenced multiple times', async() => { - writeFile('/index.ts', dedent ` + it('should copy decorator once if directive is referenced multiple times', async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -818,7 +818,7 @@ describe('Undecorated classes with DI migration', () => { export class MyModule {} `); - writeFile('/second-module.ts', dedent ` + writeFile('/second-module.ts', dedent` import {NgModule, Directive} from '@angular/core'; import {MyComp} from './index'; @@ -829,7 +829,7 @@ describe('Undecorated classes with DI migration', () => { export class MySecondModule {} `); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Component, NgModule} from '@angular/core'; @Component({ @@ -844,7 +844,7 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` import {BaseClass} from './lib/base'; @Component({ @@ -854,8 +854,8 @@ describe('Undecorated classes with DI migration', () => { export class MyComp extends BaseClass {}`); }); - it('should create aliased imports to avoid collisions for referenced identifiers', async() => { - writeFile('/index.ts', dedent ` + it('should create aliased imports to avoid collisions for referenced identifiers', async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -869,7 +869,7 @@ describe('Undecorated classes with DI migration', () => { export class MyModule {} `); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Component, NgModule} from '@angular/core'; export const MY_TEMPLATE = ''; @@ -888,7 +888,7 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')) .toContain(`import { BaseClass, MY_TEMPLATE as MY_TEMPLATE_1 } from './lib/base';`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ selector: 'my-dir', template: MY_TEMPLATE_1 @@ -896,8 +896,8 @@ describe('Undecorated classes with DI migration', () => { export class MyComp extends BaseClass {}`); }); - it('should add comment for metadata fields which cannot be copied', async() => { - writeFile('/index.ts', dedent ` + it('should add comment for metadata fields which cannot be copied', async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -907,7 +907,7 @@ describe('Undecorated classes with DI migration', () => { export class MyModule {} `); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Component, NgModule, Document} from '@angular/core'; // this variable cannot be imported automatically. @@ -926,7 +926,7 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ selector: 'my-dir', template: '', @@ -940,8 +940,8 @@ describe('Undecorated classes with DI migration', () => { }); it('should add comment for metadata fields which are added through spread operator', - async() => { - writeFile('/index.ts', dedent ` + async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -951,7 +951,7 @@ describe('Undecorated classes with DI migration', () => { export class MyModule {} `); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Component, NgModule} from '@angular/core'; export const metadataThroughVar = { @@ -971,7 +971,7 @@ describe('Undecorated classes with DI migration', () => { await runMigration(); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ selector: 'my-dir', template: '', @@ -984,10 +984,10 @@ describe('Undecorated classes with DI migration', () => { export class MyComp extends BaseClass {}`); }); - it('should be able to copy fields specified through shorthand assignment', async() => { + it('should be able to copy fields specified through shorthand assignment', async () => { writeFile('/hello.css', ''); writeFile('/my-tmpl.html', ''); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -999,7 +999,7 @@ describe('Undecorated classes with DI migration', () => { writeFile('/lib/hello.css', ''); writeFile('/lib/my-tmpl.html', ''); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Component, NgModule} from '@angular/core'; export const host = {}; @@ -1022,7 +1022,7 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')) .toContain(`import { BaseClass, templateUrl, host } from './lib/base';`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ selector: 'my-dir', templateUrl, @@ -1036,10 +1036,10 @@ describe('Undecorated classes with DI migration', () => { export class MyComp extends BaseClass {}`); }); - it('should serialize metadata from base class without source code', async() => { + it('should serialize metadata from base class without source code', async () => { writeFakeLibrary(); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseComponent, BasePipe} from 'my-lib'; @@ -1061,7 +1061,7 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')) .toContain( `import { NgModule, ChangeDetectionStrategy, ViewEncapsulation, NG_VALIDATORS, Component, Pipe } from '@angular/core';`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ changeDetection: ChangeDetectionStrategy.Default, selector: "comp-selector", @@ -1077,7 +1077,7 @@ describe('Undecorated classes with DI migration', () => { } }) export class PassThrough extends BaseComponent {}`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ changeDetection: ChangeDetectionStrategy.Default, selector: "comp-selector", @@ -1093,7 +1093,7 @@ describe('Undecorated classes with DI migration', () => { } }) export class MyComp extends PassThrough {}`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Pipe({ pure: true, name: "external-pipe-name" @@ -1101,12 +1101,12 @@ describe('Undecorated classes with DI migration', () => { export class MyPipe extends BasePipe {}`); }); - it('should serialize metadata with external references from class without source code', async() => { + it('should serialize metadata with external references from class without source code', async () => { writeFakeLibrary({useImportedTemplate: true}); writeFile( '/node_modules/@angular/cdk/table/index.d.ts', `export declare const CDK_TABLE_TEMPLATE = 'Template of CDK Table.';`); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseComponent} from 'my-lib'; @@ -1121,7 +1121,7 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')) .toContain( `import { NgModule, ChangeDetectionStrategy, ViewEncapsulation, NG_VALIDATORS, Component } from '@angular/core';`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Component({ changeDetection: ChangeDetectionStrategy.Default, selector: "comp-selector", @@ -1140,10 +1140,10 @@ describe('Undecorated classes with DI migration', () => { }); it('should not throw if metadata from base class without source code is not serializable', - async() => { + async () => { writeFakeLibrary({insertInvalidReference: true}); - writeFile('/index.ts', dedent ` + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseComponent} from 'my-lib'; @@ -1159,8 +1159,8 @@ describe('Undecorated classes with DI migration', () => { expect(errorOutput[0]).toMatch(/Could not resolve non-existent/); }); - it('should not create imports for identifiers resolving to target source file', async() => { - writeFile('/index.ts', dedent ` + it('should not create imports for identifiers resolving to target source file', async () => { + writeFile('/index.ts', dedent` import {NgModule} from '@angular/core'; import {BaseClass} from './lib/base'; @@ -1175,7 +1175,7 @@ describe('Undecorated classes with DI migration', () => { export {LOCAL_NAME as PUBLIC_NAME}; `); - writeFile('/lib/base.ts', dedent ` + writeFile('/lib/base.ts', dedent` import {Directive, NgModule} from '@angular/core'; import {SHARED_TEMPLATE_URL, PUBLIC_NAME} from '..'; @@ -1194,7 +1194,7 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/index.ts')) .toContain(`import { NgModule, Directive } from '@angular/core';`); - expect(tree.readContent('/index.ts')).toContain(dedent ` + expect(tree.readContent('/index.ts')).toContain(dedent` @Directive({ selector: 'my-dir', template: SHARED_TEMPLATE_URL, @@ -1388,7 +1388,7 @@ describe('Undecorated classes with DI migration', () => { })); } - it('should not run for test tsconfig files', async() => { + it('should not run for test tsconfig files', async () => { writeFile('/src/tsconfig.spec.json', JSON.stringify({ compilerOptions: { lib: ['es2015'], @@ -1429,8 +1429,8 @@ describe('Undecorated classes with DI migration', () => { expect(errorOutput.length).toBe(0); }); - describe('diagnostics', async() => { - it('should gracefully exit migration if project fails with structural diagnostic', async() => { + describe('diagnostics', async () => { + it('should gracefully exit migration if project fails with structural diagnostic', async () => { writeFile('/index.ts', ` import {Component, NgModule} from '@angular/core'; @@ -1455,28 +1455,29 @@ describe('Undecorated classes with DI migration', () => { 'TypeScript program failures'); }); - it('should gracefully exit migration if project fails with syntactical diagnostic', async() => { - writeFile('/index.ts', ` + it('should gracefully exit migration if project fails with syntactical diagnostic', + async () => { + writeFile('/index.ts', ` import {Component, NgModule} /* missing "from" */ '@angular/core'; `); - await runMigration(); + await runMigration(); - expect(warnOutput.length).toBe(1); - expect(warnOutput[0]) - .toMatch(/project "tsconfig.json" has syntactical errors which could cause/); - expect(errorOutput.length).toBe(1); - expect(errorOutput[0]).toMatch(/error TS1005: 'from' expected/); - expect(infoOutput.join(' ')) - .toContain( - 'Some project targets could not be analyzed due to ' + - 'TypeScript program failures'); - }); + expect(warnOutput.length).toBe(1); + expect(warnOutput[0]) + .toMatch(/project "tsconfig.json" has syntactical errors which could cause/); + expect(errorOutput.length).toBe(1); + expect(errorOutput[0]).toMatch(/error TS1005: 'from' expected/); + expect(infoOutput.join(' ')) + .toContain( + 'Some project targets could not be analyzed due to ' + + 'TypeScript program failures'); + }); // Regression test for: https://github.com/angular/angular/issues/34985. it('should be able to migrate libraries with multiple source files and flat-module ' + 'options set', - async() => { + async () => { writeFile('/tsconfig.json', JSON.stringify({ compilerOptions: { lib: ['es2015'], @@ -1507,7 +1508,7 @@ describe('Undecorated classes with DI migration', () => { expect(tree.readContent('/test.ts')).toMatch(/@Injectable\(\)\nexport class BaseClass {/); }); - it('should not throw if resources could not be read', async() => { + it('should not throw if resources could not be read', async () => { writeFile('/index.ts', ` import {Component, NgModule} from '@angular/core'; @@ -1529,7 +1530,7 @@ describe('Undecorated classes with DI migration', () => { expect(errorOutput.length).toBe(0); }); - it('should not throw if tsconfig references non-existent source file', async() => { + it('should not throw if tsconfig references non-existent source file', async () => { writeFile('/tsconfig.json', JSON.stringify({ compilerOptions: { lib: ['es2015'], diff --git a/packages/core/schematics/migrations/missing-injectable/import_manager.ts b/packages/core/schematics/utils/import_manager.ts similarity index 93% rename from packages/core/schematics/migrations/missing-injectable/import_manager.ts rename to packages/core/schematics/utils/import_manager.ts index 8bc46eca1f9ff..07b7f945f9ae1 100644 --- a/packages/core/schematics/migrations/missing-injectable/import_manager.ts +++ b/packages/core/schematics/utils/import_manager.ts @@ -8,7 +8,12 @@ import {dirname, resolve} from 'path'; import * as ts from 'typescript'; -import {UpdateRecorder} from './update_recorder'; + +/** Update recorder for managing imports. */ +export interface ImportManagerUpdateRecorder { + addNewImport(start: number, importText: string): void; + updateExistingImport(namedBindings: ts.NamedImports, newNamedBindings: string): void; +} /** * Import manager that can be used to add TypeScript imports to given source @@ -33,7 +38,7 @@ export class ImportManager { }[] = []; constructor( - private getUpdateRecorder: (sf: ts.SourceFile) => UpdateRecorder, + private getUpdateRecorder: (sf: ts.SourceFile) => ImportManagerUpdateRecorder, private printer: ts.Printer) {} /** @@ -109,8 +114,8 @@ export class ImportManager { } if (existingImport) { - const propertyIdentifier = ts.createIdentifier(symbolName !); - const generatedUniqueIdentifier = this._getUniqueIdentifier(sourceFile, symbolName !); + const propertyIdentifier = ts.createIdentifier(symbolName!); + const generatedUniqueIdentifier = this._getUniqueIdentifier(sourceFile, symbolName!); const needsGeneratedUniqueName = generatedUniqueIdentifier.text !== symbolName; const importName = needsGeneratedUniqueName ? generatedUniqueIdentifier : propertyIdentifier; @@ -181,7 +186,7 @@ export class ImportManager { this.updatedImports.forEach((expressions, importDecl) => { const sourceFile = importDecl.getSourceFile(); const recorder = this.getUpdateRecorder(sourceFile); - const namedBindings = importDecl.importClause !.namedBindings as ts.NamedImports; + const namedBindings = importDecl.importClause!.namedBindings as ts.NamedImports; const newNamedBindings = ts.updateNamedImports( namedBindings, namedBindings.elements.concat(expressions.map( @@ -206,8 +211,8 @@ export class ImportManager { name = `${baseName}_${counter++}`; } while (!this.isUniqueIdentifierName(sourceFile, name)); - this._recordUsedIdentifier(sourceFile, name !); - return ts.createIdentifier(name !); + this._recordUsedIdentifier(sourceFile, name!); + return ts.createIdentifier(name!); } /** @@ -216,7 +221,7 @@ export class ImportManager { */ private isUniqueIdentifierName(sourceFile: ts.SourceFile, name: string) { if (this.usedIdentifierNames.has(sourceFile) && - this.usedIdentifierNames.get(sourceFile) !.indexOf(name) !== -1) { + this.usedIdentifierNames.get(sourceFile)!.indexOf(name) !== -1) { return false; } @@ -225,7 +230,7 @@ export class ImportManager { // is unique in the given declaration scope and we just return false. const nodeQueue: ts.Node[] = [sourceFile]; while (nodeQueue.length) { - const node = nodeQueue.shift() !; + const node = nodeQueue.shift()!; if (ts.isIdentifier(node) && node.text === name) { return false; } @@ -249,6 +254,6 @@ export class ImportManager { if (!commentRanges || !commentRanges.length) { return nodeEndPos; } - return commentRanges[commentRanges.length - 1] !.end; + return commentRanges[commentRanges.length - 1]!.end; } } diff --git a/packages/core/schematics/utils/ng_component_template.ts b/packages/core/schematics/utils/ng_component_template.ts index 5b20b964cbcf0..01b1dfcb5e69b 100644 --- a/packages/core/schematics/utils/ng_component_template.ts +++ b/packages/core/schematics/utils/ng_component_template.ts @@ -31,7 +31,9 @@ export interface ResolvedTemplate { * If the template is declared inline within a TypeScript source file, the line and * character are based on the full source file content. */ - getCharacterAndLineOfPosition: (pos: number) => { character: number, line: number }; + getCharacterAndLineOfPosition: (pos: number) => { + character: number, line: number + }; } /** @@ -103,8 +105,8 @@ export class NgComponentTemplateVisitor { content: property.initializer.text, inline: true, start: templateStartIdx, - getCharacterAndLineOfPosition: - pos => ts.getLineAndCharacterOfPosition(sourceFile, pos + templateStartIdx) + getCharacterAndLineOfPosition: pos => + ts.getLineAndCharacterOfPosition(sourceFile, pos + templateStartIdx) }); } if (propertyName === 'templateUrl' && ts.isStringLiteralLike(property.initializer)) { diff --git a/packages/core/schematics/utils/ng_decorators.ts b/packages/core/schematics/utils/ng_decorators.ts index 4e2fadea1c6ff..54f53c0b5cc74 100644 --- a/packages/core/schematics/utils/ng_decorators.ts +++ b/packages/core/schematics/utils/ng_decorators.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {getCallDecoratorImport} from './typescript/decorators'; -export type CallExpressionDecorator = ts.Decorator & { +export type CallExpressionDecorator = ts.Decorator&{ expression: ts.CallExpression; }; @@ -30,8 +30,8 @@ export function getAngularDecorators( .filter(({importData}) => importData && importData.importModule.startsWith('@angular/')) .map(({node, importData}) => ({ node: node as CallExpressionDecorator, - name: importData !.name, - moduleName: importData !.importModule, - importNode: importData !.node + name: importData!.name, + moduleName: importData!.importModule, + importNode: importData!.node })); } diff --git a/packages/core/schematics/utils/project_tsconfig_paths.ts b/packages/core/schematics/utils/project_tsconfig_paths.ts index 6690974fb0dd4..64153a36a5ef0 100644 --- a/packages/core/schematics/utils/project_tsconfig_paths.ts +++ b/packages/core/schematics/utils/project_tsconfig_paths.ts @@ -71,7 +71,7 @@ function getTargetTsconfigPath(project: WorkspaceProject, targetName: string): s */ function getWorkspaceConfigGracefully(tree: Tree): any { const path = defaultWorkspaceConfigPaths.find(filePath => tree.exists(filePath)); - const configBuffer = tree.read(path !); + const configBuffer = tree.read(path!); if (!path || !configBuffer) { return null; diff --git a/packages/core/schematics/utils/schematics_prompt.ts b/packages/core/schematics/utils/schematics_prompt.ts index 9fcfc92f489ef..f32d64d7af821 100644 --- a/packages/core/schematics/utils/schematics_prompt.ts +++ b/packages/core/schematics/utils/schematics_prompt.ts @@ -30,5 +30,5 @@ export function supportsPrompt(): boolean { * create prompts. */ export function getInquirer(): Inquirer { - return resolvedInquirerModule !; + return resolvedInquirerModule!; } diff --git a/packages/core/schematics/utils/typescript/compiler_host.ts b/packages/core/schematics/utils/typescript/compiler_host.ts index 5103890dfef37..2257b32006d5f 100644 --- a/packages/core/schematics/utils/typescript/compiler_host.ts +++ b/packages/core/schematics/utils/typescript/compiler_host.ts @@ -6,12 +6,39 @@ * found in the LICENSE file at https://angular.io/license */ import {Tree} from '@angular-devkit/schematics'; -import {relative} from 'path'; +import {dirname, relative, resolve} from 'path'; import * as ts from 'typescript'; +import {parseTsconfigFile} from './parse_tsconfig'; + +export type FakeReadFileFn = (fileName: string) => string|null; + +/** + * Creates a TypeScript program instance for a TypeScript project within + * the virtual file system tree. + * @param tree Virtual file system tree that contains the source files. + * @param tsconfigPath Virtual file system path that resolves to the TypeScript project. + * @param basePath Base path for the virtual file system tree. + * @param fakeFileRead Optional file reader function. Can be used to overwrite files in + * the TypeScript program, or to add in-memory files (e.g. to add global types). + * @param additionalFiles Additional file paths that should be added to the program. + */ +export function createMigrationProgram( + tree: Tree, tsconfigPath: string, basePath: string, fakeFileRead?: FakeReadFileFn, + additionalFiles?: string[]) { + // Resolve the tsconfig path to an absolute path. This is needed as TypeScript otherwise + // is not able to resolve root directories in the given tsconfig. More details can be found + // in the following issue: https://github.com/microsoft/TypeScript/issues/37731. + tsconfigPath = resolve(basePath, tsconfigPath); + const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); + const host = createMigrationCompilerHost(tree, parsed.options, basePath, fakeFileRead); + const program = + ts.createProgram(parsed.fileNames.concat(additionalFiles || []), parsed.options, host); + return {parsed, host, program}; +} export function createMigrationCompilerHost( tree: Tree, options: ts.CompilerOptions, basePath: string, - fakeRead?: (fileName: string) => string | null): ts.CompilerHost { + fakeRead?: FakeReadFileFn): ts.CompilerHost { const host = ts.createCompilerHost(options, true); // We need to overwrite the host "readFile" method, as we want the TypeScript diff --git a/packages/core/schematics/utils/typescript/decorators.ts b/packages/core/schematics/utils/typescript/decorators.ts index de1df266e892e..7722961300282 100644 --- a/packages/core/schematics/utils/typescript/decorators.ts +++ b/packages/core/schematics/utils/typescript/decorators.ts @@ -7,7 +7,8 @@ */ import * as ts from 'typescript'; -import {Import, getImportOfIdentifier} from './imports'; + +import {getImportOfIdentifier, Import} from './imports'; export function getCallDecoratorImport( typeChecker: ts.TypeChecker, decorator: ts.Decorator): Import|null { diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/find_base_classes.ts b/packages/core/schematics/utils/typescript/find_base_classes.ts similarity index 73% rename from packages/core/schematics/migrations/undecorated-classes-with-di/find_base_classes.ts rename to packages/core/schematics/utils/typescript/find_base_classes.ts index 01467b745d93c..f6a16c120a49d 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/find_base_classes.ts +++ b/packages/core/schematics/utils/typescript/find_base_classes.ts @@ -7,7 +7,7 @@ */ import * as ts from 'typescript'; -import {getBaseTypeIdentifiers} from '../../utils/typescript/class_declaration'; +import {getBaseTypeIdentifiers} from './class_declaration'; /** Gets all base class declarations of the specified class declaration. */ export function findBaseClassDeclarations(node: ts.ClassDeclaration, typeChecker: ts.TypeChecker) { @@ -20,7 +20,9 @@ export function findBaseClassDeclarations(node: ts.ClassDeclaration, typeChecker break; } const symbol = typeChecker.getTypeAtLocation(baseTypes[0]).getSymbol(); - if (!symbol || !ts.isClassDeclaration(symbol.valueDeclaration)) { + // Note: `ts.Symbol#valueDeclaration` can be undefined. TypeScript has an incorrect type + // for this: https://github.com/microsoft/TypeScript/issues/24706. + if (!symbol || !symbol.valueDeclaration || !ts.isClassDeclaration(symbol.valueDeclaration)) { break; } result.push({identifier: baseTypes[0], node: symbol.valueDeclaration}); diff --git a/packages/core/schematics/utils/typescript/functions.ts b/packages/core/schematics/utils/typescript/functions.ts index 3e32af41329f5..07c105604ef82 100644 --- a/packages/core/schematics/utils/typescript/functions.ts +++ b/packages/core/schematics/utils/typescript/functions.ts @@ -17,11 +17,11 @@ export function isFunctionLikeDeclaration(node: ts.Node): node is ts.FunctionLik /** * Unwraps a given expression TypeScript node. Expressions can be wrapped within multiple - * parentheses. e.g. "(((({exp}))))()". The function should return the TypeScript node - * referring to the inner expression. e.g "exp". + * parentheses or as expression. e.g. "(((({exp}))))()". The function should return the + * TypeScript node referring to the inner expression. e.g "exp". */ -export function unwrapExpression(node: ts.Expression | ts.ParenthesizedExpression): ts.Expression { - if (ts.isParenthesizedExpression(node)) { +export function unwrapExpression(node: ts.Expression|ts.ParenthesizedExpression): ts.Expression { + if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node)) { return unwrapExpression(node.expression); } else { return node; diff --git a/packages/core/schematics/utils/typescript/parse_tsconfig.ts b/packages/core/schematics/utils/typescript/parse_tsconfig.ts index 220448a0efa85..9cbed072fd4f6 100644 --- a/packages/core/schematics/utils/typescript/parse_tsconfig.ts +++ b/packages/core/schematics/utils/typescript/parse_tsconfig.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import * as path from 'path'; import * as ts from 'typescript'; export function parseTsconfigFile(tsconfigPath: string, basePath: string): ts.ParsedCommandLine { @@ -17,5 +18,12 @@ export function parseTsconfigFile(tsconfigPath: string, basePath: string): ts.Pa readFile: ts.sys.readFile, }; + // Throw if incorrect arguments are passed to this function. Passing relative base paths + // results in root directories not being resolved and in later type checking runtime errors. + // More details can be found here: https://github.com/microsoft/TypeScript/issues/37731. + if (!path.isAbsolute(basePath)) { + throw Error('Unexpected relative base path has been specified.'); + } + return ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, {}); } diff --git a/packages/core/src/application_init.ts b/packages/core/src/application_init.ts index b9852148c1889..69a77c8bc5681 100644 --- a/packages/core/src/application_init.ts +++ b/packages/core/src/application_init.ts @@ -34,9 +34,9 @@ export const APP_INITIALIZER = new InjectionToken<Array<() => void>>('Applicatio @Injectable() export class ApplicationInitStatus { // TODO(issue/24571): remove '!'. - private resolve !: Function; + private resolve!: Function; // TODO(issue/24571): remove '!'. - private reject !: Function; + private reject!: Function; private initialized = false; public readonly donePromise: Promise<any>; public readonly done = false; @@ -57,7 +57,7 @@ export class ApplicationInitStatus { const asyncInitPromises: Promise<any>[] = []; const complete = () => { - (this as{done: boolean}).done = true; + (this as {done: boolean}).done = true; this.resolve(); }; @@ -70,7 +70,13 @@ export class ApplicationInitStatus { } } - Promise.all(asyncInitPromises).then(() => { complete(); }).catch(e => { this.reject(e); }); + Promise.all(asyncInitPromises) + .then(() => { + complete(); + }) + .catch(e => { + this.reject(e); + }); if (asyncInitPromises.length === 0) { complete(); diff --git a/packages/core/src/application_module.ts b/packages/core/src/application_module.ts index f59eb86cd3fdd..de727bc81b798 100644 --- a/packages/core/src/application_module.ts +++ b/packages/core/src/application_module.ts @@ -9,7 +9,7 @@ import {APP_INITIALIZER, ApplicationInitStatus} from './application_init'; import {ApplicationRef} from './application_ref'; import {APP_ID_RANDOM_PROVIDER} from './application_tokens'; -import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection'; +import {defaultIterableDiffers, defaultKeyValueDiffers, IterableDiffers, KeyValueDiffers} from './change_detection/change_detection'; import {Console} from './console'; import {Injector, StaticProvider} from './di'; import {Inject, Optional, SkipSelf} from './di/metadata'; @@ -78,8 +78,7 @@ export const APPLICATION_MODULE_PROVIDERS: StaticProvider[] = [ { provide: ApplicationRef, useClass: ApplicationRef, - deps: - [NgZone, Console, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus] + deps: [NgZone, Console, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus] }, {provide: SCHEDULER, deps: [NgZone], useFactory: zoneSchedulerFactory}, { @@ -112,10 +111,12 @@ export function zoneSchedulerFactory(ngZone: NgZone): (fn: () => void) => void { let queue: (() => void)[] = []; ngZone.onStable.subscribe(() => { while (queue.length) { - queue.pop() !(); + queue.pop()!(); } }); - return function(fn: () => void) { queue.push(fn); }; + return function(fn: () => void) { + queue.push(fn); + }; } /** diff --git a/packages/core/src/application_ref.ts b/packages/core/src/application_ref.ts index 694f5763b3c50..1149dc0ffca45 100644 --- a/packages/core/src/application_ref.ts +++ b/packages/core/src/application_ref.ts @@ -8,7 +8,7 @@ import './util/ng_jit_mode'; -import {Observable, Observer, Subscription, merge} from 'rxjs'; +import {merge, Observable, Observer, Subscription} from 'rxjs'; import {share} from 'rxjs/operators'; import {ApplicationInitStatus} from './application_init'; @@ -80,7 +80,7 @@ export function compileNgModuleFactory__POST_R3__<M>( return Promise.resolve(moduleFactory); } - const compilerProviders = _mergeArrays(compilerOptions.map(o => o.providers !)); + const compilerProviders = _mergeArrays(compilerOptions.map(o => o.providers!)); // In case there are no compiler providers, we just return the module factory as // there won't be any resource loader. This can happen with Ivy, because AOT compiled @@ -157,9 +157,8 @@ export function createPlatform(injector: Injector): PlatformRef { * @publicApi */ export function createPlatformFactory( - parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef) | null, - name: string, providers: StaticProvider[] = []): (extraProviders?: StaticProvider[]) => - PlatformRef { + parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef)|null, name: string, + providers: StaticProvider[] = []): (extraProviders?: StaticProvider[]) => PlatformRef { const desc = `Platform: ${name}`; const marker = new InjectionToken(desc); return (extraProviders: StaticProvider[] = []) => { @@ -320,10 +319,12 @@ export class PlatformRef { throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?'); } moduleRef.onDestroy(() => remove(this._modules, moduleRef)); - ngZone !.runOutsideAngular( - () => ngZone !.onError.subscribe( - {next: (error: any) => { exceptionHandler.handleError(error); }})); - return _callAndReportToErrorHandler(exceptionHandler, ngZone !, () => { + ngZone!.runOutsideAngular(() => ngZone!.onError.subscribe({ + next: (error: any) => { + exceptionHandler.handleError(error); + } + })); + return _callAndReportToErrorHandler(exceptionHandler, ngZone!, () => { const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus); initStatus.runInitializers(); return initStatus.donePromise.then(() => { @@ -356,7 +357,8 @@ export class PlatformRef { * */ bootstrapModule<M>( - moduleType: Type<M>, compilerOptions: (CompilerOptions&BootstrapOptions)| + moduleType: Type<M>, + compilerOptions: (CompilerOptions&BootstrapOptions)| Array<CompilerOptions&BootstrapOptions> = []): Promise<NgModuleRef<M>> { const options = optionsReducer({}, compilerOptions); return compileNgModuleFactory(this.injector, options, moduleType) @@ -371,7 +373,10 @@ export class PlatformRef { moduleRef.instance.ngDoBootstrap(appRef); } else { throw new Error( - `The module ${stringify(moduleRef.instance.constructor)} was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. ` + + `The module ${ + stringify( + moduleRef.instance + .constructor)} was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. ` + `Please define one of these.`); } this._modules.push(moduleRef); @@ -380,13 +385,17 @@ export class PlatformRef { /** * Register a listener to be called when the platform is disposed. */ - onDestroy(callback: () => void): void { this._destroyListeners.push(callback); } + onDestroy(callback: () => void): void { + this._destroyListeners.push(callback); + } /** * Retrieve the platform {@link Injector}, which is the parent injector for * every Angular application on the page and provides singleton providers. */ - get injector(): Injector { return this._injector; } + get injector(): Injector { + return this._injector; + } /** * Destroy the Angular platform and all Angular applications on the page. @@ -400,11 +409,13 @@ export class PlatformRef { this._destroyed = true; } - get destroyed() { return this._destroyed; } + get destroyed() { + return this._destroyed; + } } function getNgZone( - ngZoneOption: NgZone | 'zone.js' | 'noop' | undefined, ngZoneEventCoalescing: boolean): NgZone { + ngZoneOption: NgZone|'zone.js'|'noop'|undefined, ngZoneEventCoalescing: boolean): NgZone { let ngZone: NgZone; if (ngZoneOption === 'noop') { @@ -438,7 +449,7 @@ function _callAndReportToErrorHandler( } } -function optionsReducer<T extends Object>(dst: any, objs: T | T[]): T { +function optionsReducer<T extends Object>(dst: any, objs: T|T[]): T { if (Array.isArray(objs)) { dst = objs.reduce(optionsReducer, dst); } else { @@ -566,7 +577,7 @@ export class ApplicationRef { * @see [Usage notes](#is-stable-examples) for examples and caveats when using this API. */ // TODO(issue/24571): remove '!'. - public readonly isStable !: Observable<boolean>; + public readonly isStable!: Observable<boolean>; /** @internal */ constructor( @@ -576,8 +587,13 @@ export class ApplicationRef { private _initStatus: ApplicationInitStatus) { this._enforceNoNewChanges = isDevMode(); - this._zone.onMicrotaskEmpty.subscribe( - {next: () => { this._zone.run(() => { this.tick(); }); }}); + this._zone.onMicrotaskEmpty.subscribe({ + next: () => { + this._zone.run(() => { + this.tick(); + }); + } + }); const isCurrentlyStable = new Observable<boolean>((observer: Observer<boolean>) => { this._stable = this._zone.isStable && !this._zone.hasPendingMacrotasks && @@ -612,7 +628,9 @@ export class ApplicationRef { NgZone.assertInAngularZone(); if (this._stable) { this._stable = false; - this._zone.runOutsideAngular(() => { observer.next(false); }); + this._zone.runOutsideAngular(() => { + observer.next(false); + }); } }); @@ -622,7 +640,7 @@ export class ApplicationRef { }; }); - (this as{isStable: Observable<boolean>}).isStable = + (this as {isStable: Observable<boolean>}).isStable = merge(isCurrentlyStable, isStable.pipe(share())); } @@ -653,7 +671,7 @@ export class ApplicationRef { componentFactory = componentOrFactory; } else { componentFactory = - this._componentFactoryResolver.resolveComponentFactory(componentOrFactory) !; + this._componentFactoryResolver.resolveComponentFactory(componentOrFactory)!; } this.componentTypes.push(componentFactory.componentType); @@ -663,7 +681,9 @@ export class ApplicationRef { const selectorOrNode = rootSelectorOrNode || componentFactory.selector; const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule); - compRef.onDestroy(() => { this._unloadComponent(compRef); }); + compRef.onDestroy(() => { + this._unloadComponent(compRef); + }); const testability = compRef.injector.get(Testability, null); if (testability) { compRef.injector.get(TestabilityRegistry) @@ -755,7 +775,9 @@ export class ApplicationRef { /** * Returns the number of attached views. */ - get viewCount() { return this._views.length; } + get viewCount() { + return this._views.length; + } } function remove<T>(list: T[], el: T): void { diff --git a/packages/core/src/change_detection/change_detection.ts b/packages/core/src/change_detection/change_detection.ts index 158bc238f9052..327beb1f6e875 100644 --- a/packages/core/src/change_detection/change_detection.ts +++ b/packages/core/src/change_detection/change_detection.ts @@ -11,26 +11,16 @@ import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ'; import {IterableDifferFactory, IterableDiffers} from './differs/iterable_differs'; import {KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs'; -export {WrappedValue, devModeEqual} from './change_detection_util'; +export {SimpleChange, SimpleChanges} from '../interface/simple_change'; +export {devModeEqual, WrappedValue} from './change_detection_util'; export {ChangeDetectorRef} from './change_detector_ref'; export {ChangeDetectionStrategy, ChangeDetectorStatus, isDefaultChangeDetectionStrategy} from './constants'; export {DefaultIterableDifferFactory} from './differs/default_iterable_differ'; export {DefaultIterableDiffer} from './differs/default_iterable_differ'; export {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ'; -export { - CollectionChangeRecord, - IterableChangeRecord, - IterableChanges, - IterableDiffer, - IterableDifferFactory, - IterableDiffers, - NgIterable, - TrackByFunction -} from -'./differs/iterable_differs'; +export {CollectionChangeRecord, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, NgIterable, TrackByFunction} from './differs/iterable_differs'; export {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs'; export {PipeTransform} from './pipe_transform'; -export {SimpleChange, SimpleChanges} from '../interface/simple_change'; diff --git a/packages/core/src/change_detection/change_detection_util.ts b/packages/core/src/change_detection/change_detection_util.ts index b98a3756e751a..3d0ad85a4ab54 100644 --- a/packages/core/src/change_detection/change_detection_util.ts +++ b/packages/core/src/change_detection/change_detection_util.ts @@ -49,19 +49,27 @@ export class WrappedValue { /** @deprecated from 5.3, use `unwrap()` instead - will switch to protected */ wrapped: any; - constructor(value: any) { this.wrapped = value; } + constructor(value: any) { + this.wrapped = value; + } /** Creates a wrapped value. */ - static wrap(value: any): WrappedValue { return new WrappedValue(value); } + static wrap(value: any): WrappedValue { + return new WrappedValue(value); + } /** * Returns the underlying value of a wrapped value. * Returns the given `value` when it is not wrapped. **/ - static unwrap(value: any): any { return WrappedValue.isWrapped(value) ? value.wrapped : value; } + static unwrap(value: any): any { + return WrappedValue.isWrapped(value) ? value.wrapped : value; + } /** Returns true if `value` is a wrapped value. */ - static isWrapped(value: any): value is WrappedValue { return value instanceof WrappedValue; } + static isWrapped(value: any): value is WrappedValue { + return value instanceof WrappedValue; + } } export function isListLikeIterable(obj: any): boolean { diff --git a/packages/core/src/change_detection/differs/default_iterable_differ.ts b/packages/core/src/change_detection/differs/default_iterable_differ.ts index dd6d22a06d5ae..ea402369ab55b 100644 --- a/packages/core/src/change_detection/differs/default_iterable_differ.ts +++ b/packages/core/src/change_detection/differs/default_iterable_differ.ts @@ -15,7 +15,9 @@ import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFac export class DefaultIterableDifferFactory implements IterableDifferFactory { constructor() {} - supports(obj: Object|null|undefined): boolean { return isListLikeIterable(obj); } + supports(obj: Object|null|undefined): boolean { + return isListLikeIterable(obj); + } create<V>(trackByFn?: TrackByFunction<V>): DefaultIterableDiffer<V> { return new DefaultIterableDiffer<V>(trackByFn); @@ -31,7 +33,7 @@ const trackByIdentity = (index: number, item: any) => item; export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> { public readonly length: number = 0; // TODO(issue/24571): remove '!'. - public readonly collection !: V[] | Iterable<V>| null; + public readonly collection!: V[]|Iterable<V>|null; // Keeps track of the used records at any point in time (during & across `_check()` calls) private _linkedRecords: _DuplicateMap<V>|null = null; // Keeps track of the removed records at any point in time during `_check()` calls. @@ -50,7 +52,9 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan private _identityChangesTail: IterableChangeRecord_<V>|null = null; private _trackByFn: TrackByFunction<V>; - constructor(trackByFn?: TrackByFunction<V>) { this._trackByFn = trackByFn || trackByIdentity; } + constructor(trackByFn?: TrackByFunction<V>) { + this._trackByFn = trackByFn || trackByIdentity; + } forEachItem(fn: (record: IterableChangeRecord_<V>) => void) { let record: IterableChangeRecord_<V>|null; @@ -71,9 +75,9 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan // Order: remove, add, move const record: IterableChangeRecord<V> = !nextRemove || nextIt && - nextIt.currentIndex ! < + nextIt.currentIndex! < getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ? - nextIt ! : + nextIt! : nextRemove; const adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets); const currentIndex = record.currentIndex; @@ -83,14 +87,14 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan addRemoveOffset--; nextRemove = nextRemove._nextRemoved; } else { - nextIt = nextIt !._next; + nextIt = nextIt!._next; if (record.previousIndex == null) { addRemoveOffset++; } else { // INVARIANT: currentIndex < previousIndex if (!moveOffsets) moveOffsets = []; const localMovePreviousIndex = adjPreviousIndex - addRemoveOffset; - const localCurrentIndex = currentIndex ! - addRemoveOffset; + const localCurrentIndex = currentIndex! - addRemoveOffset; if (localMovePreviousIndex != localCurrentIndex) { for (let i = 0; i < localMovePreviousIndex; i++) { const offset = i < moveOffsets.length ? moveOffsets[i] : (moveOffsets[i] = 0); @@ -171,7 +175,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan let item: V; let itemTrackBy: any; if (Array.isArray(collection)) { - (this as{length: number}).length = collection.length; + (this as {length: number}).length = collection.length; for (let index = 0; index < this.length; index++) { item = collection[index]; @@ -206,11 +210,11 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan record = record._next; index++; }); - (this as{length: number}).length = index; + (this as {length: number}).length = index; } this._truncate(record); - (this as{collection: V[] | Iterable<V>}).collection = collection; + (this as {collection: V[] | Iterable<V>}).collection = collection; return this.isDirty; } @@ -338,7 +342,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan let reinsertRecord: IterableChangeRecord_<V>|null = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null); if (reinsertRecord !== null) { - record = this._reinsertAfter(reinsertRecord, record._prev !, index); + record = this._reinsertAfter(reinsertRecord, record._prev!, index); } else if (record.currentIndex != index) { record.currentIndex = index; this._addToMoves(record, index); @@ -611,7 +615,7 @@ class _DuplicateItemRecordList<V> { // TODO(vicb): // assert(record.item == _head.item || // record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN); - this._tail !._nextDup = record; + this._tail!._nextDup = record; record._prevDup = this._tail; record._nextDup = null; this._tail = record; @@ -623,7 +627,7 @@ class _DuplicateItemRecordList<V> { get(trackById: any, atOrAfterIndex: number|null): IterableChangeRecord_<V>|null { let record: IterableChangeRecord_<V>|null; for (record = this._head; record !== null; record = record._nextDup) { - if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex !) && + if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex!) && looseIdentical(record.trackById, trackById)) { return record; } @@ -696,7 +700,7 @@ class _DuplicateMap<V> { */ remove(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> { const key = record.trackById; - const recordList: _DuplicateItemRecordList<V> = this.map.get(key) !; + const recordList: _DuplicateItemRecordList<V> = this.map.get(key)!; // Remove the list of duplicates when it gets empty if (recordList.remove(record)) { this.map.delete(key); @@ -704,13 +708,16 @@ class _DuplicateMap<V> { return record; } - get isEmpty(): boolean { return this.map.size === 0; } + get isEmpty(): boolean { + return this.map.size === 0; + } - clear() { this.map.clear(); } + clear() { + this.map.clear(); + } } -function getPreviousIndex( - item: any, addRemoveOffset: number, moveOffsets: number[] | null): number { +function getPreviousIndex(item: any, addRemoveOffset: number, moveOffsets: number[]|null): number { const previousIndex = item.previousIndex; if (previousIndex === null) return previousIndex; let moveOffset = 0; diff --git a/packages/core/src/change_detection/differs/default_keyvalue_differ.ts b/packages/core/src/change_detection/differs/default_keyvalue_differ.ts index b0d8f404b71e1..fce214ca4b30a 100644 --- a/packages/core/src/change_detection/differs/default_keyvalue_differ.ts +++ b/packages/core/src/change_detection/differs/default_keyvalue_differ.ts @@ -14,9 +14,13 @@ import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFac export class DefaultKeyValueDifferFactory<K, V> implements KeyValueDifferFactory { constructor() {} - supports(obj: any): boolean { return obj instanceof Map || isJsObject(obj); } + supports(obj: any): boolean { + return obj instanceof Map || isJsObject(obj); + } - create<K, V>(): KeyValueDiffer<K, V> { return new DefaultKeyValueDiffer<K, V>(); } + create<K, V>(): KeyValueDiffer<K, V> { + return new DefaultKeyValueDiffer<K, V>(); + } } export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyValueChanges<K, V> { @@ -175,7 +179,7 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal private _getOrCreateRecordForKey(key: K, value: V): KeyValueChangeRecord_<K, V> { if (this._records.has(key)) { - const record = this._records.get(key) !; + const record = this._records.get(key)!; this._maybeAddToChanges(record, value); const prev = record._prev; const next = record._next; @@ -236,7 +240,7 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal if (this._additionsHead === null) { this._additionsHead = this._additionsTail = record; } else { - this._additionsTail !._nextAdded = record; + this._additionsTail!._nextAdded = record; this._additionsTail = record; } } @@ -245,7 +249,7 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal if (this._changesHead === null) { this._changesHead = this._changesTail = record; } else { - this._changesTail !._nextChanged = record; + this._changesTail!._nextChanged = record; this._changesTail = record; } } diff --git a/packages/core/src/change_detection/differs/iterable_differs.ts b/packages/core/src/change_detection/differs/iterable_differs.ts index e9f6f660d9f6d..0029e61bd1e5e 100644 --- a/packages/core/src/change_detection/differs/iterable_differs.ts +++ b/packages/core/src/change_detection/differs/iterable_differs.ts @@ -18,7 +18,7 @@ import {DefaultIterableDifferFactory} from '../differs/default_iterable_differ'; * * @publicApi */ -export type NgIterable<T> = Array<T>| Iterable<T>; +export type NgIterable<T> = Array<T>|Iterable<T>; /** * A strategy for tracking changes over time to an iterable. Used by {@link NgForOf} to @@ -86,8 +86,10 @@ export interface IterableChanges<V> { /** Iterate over all removed items. */ forEachRemovedItem(fn: (record: IterableChangeRecord<V>) => void): void; - /** Iterate over all items which had their identity (as computed by the `TrackByFunction`) - * changed. */ + /** + * Iterate over all items which had their identity (as computed by the `TrackByFunction`) + * changed. + */ forEachIdentityChange(fn: (record: IterableChangeRecord<V>) => void): void; } @@ -124,7 +126,9 @@ export interface CollectionChangeRecord<V> extends IterableChangeRecord<V> {} * * @publicApi */ -export interface TrackByFunction<T> { (index: number, item: T): any; } +export interface TrackByFunction<T> { + (index: number, item: T): any; +} /** * Provides a factory for {@link IterableDiffer}. @@ -153,7 +157,9 @@ export class IterableDiffers { * @deprecated v4.0.0 - Should be private */ factories: IterableDifferFactory[]; - constructor(factories: IterableDifferFactory[]) { this.factories = factories; } + constructor(factories: IterableDifferFactory[]) { + this.factories = factories; + } static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers { if (parent != null) { @@ -206,8 +212,8 @@ export class IterableDiffers { if (factory != null) { return factory; } else { - throw new Error( - `Cannot find a differ supporting object '${iterable}' of type '${getTypeNameForDebugging(iterable)}'`); + throw new Error(`Cannot find a differ supporting object '${iterable}' of type '${ + getTypeNameForDebugging(iterable)}'`); } } } diff --git a/packages/core/src/change_detection/differs/keyvalue_differs.ts b/packages/core/src/change_detection/differs/keyvalue_differs.ts index 953d787d36654..246bd4d357c6d 100644 --- a/packages/core/src/change_detection/differs/keyvalue_differs.ts +++ b/packages/core/src/change_detection/differs/keyvalue_differs.ts @@ -129,7 +129,9 @@ export class KeyValueDiffers { */ factories: KeyValueDifferFactory[]; - constructor(factories: KeyValueDifferFactory[]) { this.factories = factories; } + constructor(factories: KeyValueDifferFactory[]) { + this.factories = factories; + } static create<S>(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers { if (parent) { diff --git a/packages/core/src/change_detection/pipe_transform.ts b/packages/core/src/change_detection/pipe_transform.ts index 3a6889e3a85a9..db5b0f4dc8f4e 100644 --- a/packages/core/src/change_detection/pipe_transform.ts +++ b/packages/core/src/change_detection/pipe_transform.ts @@ -30,4 +30,6 @@ * * @publicApi */ -export interface PipeTransform { transform(value: any, ...args: any[]): any; } +export interface PipeTransform { + transform(value: any, ...args: any[]): any; +} diff --git a/packages/core/src/codegen_private_exports.ts b/packages/core/src/codegen_private_exports.ts index 177d4560abb80..76bcb8614853c 100644 --- a/packages/core/src/codegen_private_exports.ts +++ b/packages/core/src/codegen_private_exports.ts @@ -8,4 +8,4 @@ export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver'; export {registerModuleFactory as ɵregisterModuleFactory} from './linker/ng_module_factory_registration'; -export {ArgumentType as ɵArgumentType, BindingFlags as ɵBindingFlags, DepFlags as ɵDepFlags, EMPTY_ARRAY as ɵEMPTY_ARRAY, EMPTY_MAP as ɵEMPTY_MAP, NodeFlags as ɵNodeFlags, QueryBindingType as ɵQueryBindingType, QueryValueType as ɵQueryValueType, ViewDefinition as ɵViewDefinition, ViewFlags as ɵViewFlags, anchorDef as ɵand, createComponentFactory as ɵccf, createNgModuleFactory as ɵcmf, createRendererType2 as ɵcrt, directiveDef as ɵdid, elementDef as ɵeld, getComponentViewDefinitionFactory as ɵgetComponentViewDefinitionFactory, inlineInterpolate as ɵinlineInterpolate, interpolate as ɵinterpolate, moduleDef as ɵmod, moduleProvideDef as ɵmpd, ngContentDef as ɵncd, nodeValue as ɵnov, pipeDef as ɵpid, providerDef as ɵprd, pureArrayDef as ɵpad, pureObjectDef as ɵpod, purePipeDef as ɵppd, queryDef as ɵqud, textDef as ɵted, unwrapValue as ɵunv, viewDef as ɵvid} from './view/index'; +export {anchorDef as ɵand, ArgumentType as ɵArgumentType, BindingFlags as ɵBindingFlags, createComponentFactory as ɵccf, createNgModuleFactory as ɵcmf, createRendererType2 as ɵcrt, DepFlags as ɵDepFlags, directiveDef as ɵdid, elementDef as ɵeld, EMPTY_ARRAY as ɵEMPTY_ARRAY, EMPTY_MAP as ɵEMPTY_MAP, getComponentViewDefinitionFactory as ɵgetComponentViewDefinitionFactory, inlineInterpolate as ɵinlineInterpolate, interpolate as ɵinterpolate, moduleDef as ɵmod, moduleProvideDef as ɵmpd, ngContentDef as ɵncd, NodeFlags as ɵNodeFlags, nodeValue as ɵnov, pipeDef as ɵpid, providerDef as ɵprd, pureArrayDef as ɵpad, pureObjectDef as ɵpod, purePipeDef as ɵppd, QueryBindingType as ɵQueryBindingType, queryDef as ɵqud, QueryValueType as ɵQueryValueType, textDef as ɵted, unwrapValue as ɵunv, viewDef as ɵvid, ViewDefinition as ɵViewDefinition, ViewFlags as ɵViewFlags} from './view/index'; diff --git a/packages/core/src/compiler/compiler_facade_interface.ts b/packages/core/src/compiler/compiler_facade_interface.ts index 79d06d41c1147..e563344f23f6a 100644 --- a/packages/core/src/compiler/compiler_facade_interface.ts +++ b/packages/core/src/compiler/compiler_facade_interface.ts @@ -22,7 +22,9 @@ * ``` */ -export interface ExportedCompilerFacade { ɵcompilerFacade: CompilerFacade; } +export interface ExportedCompilerFacade { + ɵcompilerFacade: CompilerFacade; +} export interface CompilerFacade { compilePipe(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3PipeMetadataFacade): @@ -44,13 +46,15 @@ export interface CompilerFacade { R3ResolvedDependencyType: typeof R3ResolvedDependencyType; R3FactoryTarget: typeof R3FactoryTarget; - ResourceLoader: {new (): ResourceLoader}; + ResourceLoader: {new(): ResourceLoader}; } -export interface CoreEnvironment { [name: string]: Function; } +export interface CoreEnvironment { + [name: string]: Function; +} export type ResourceLoader = { - get(url: string): Promise<string>| string; + get(url: string): Promise<string>|string; }; export type StringMap = { @@ -58,7 +62,7 @@ export type StringMap = { }; export type StringMapWithRename = { - [key: string]: string | [string, string]; + [key: string]: string|[string, string]; }; export type Provider = any; diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index 640d7369fb95b..e8d075ea93506 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -16,7 +16,7 @@ export {getDebugNodeR2 as ɵgetDebugNodeR2} from './debug/debug_node'; export {inject, setCurrentInjector as ɵsetCurrentInjector, ɵɵinject} from './di/injector_compatibility'; export {getInjectableDef as ɵgetInjectableDef, ɵɵInjectableDef, ɵɵInjectorDef} from './di/interface/defs'; export {INJECTOR_SCOPE as ɵINJECTOR_SCOPE} from './di/scope'; -export {CurrencyIndex as ɵCurrencyIndex, ExtraLocaleDataIndex as ɵExtraLocaleDataIndex, LocaleDataIndex as ɵLocaleDataIndex, findLocaleData as ɵfindLocaleData, getLocaleCurrencyCode as ɵgetLocaleCurrencyCode, getLocalePluralCase as ɵgetLocalePluralCase, registerLocaleData as ɵregisterLocaleData, unregisterAllLocaleData as ɵunregisterLocaleData} from './i18n/locale_data_api'; +export {CurrencyIndex as ɵCurrencyIndex, ExtraLocaleDataIndex as ɵExtraLocaleDataIndex, findLocaleData as ɵfindLocaleData, getLocaleCurrencyCode as ɵgetLocaleCurrencyCode, getLocalePluralCase as ɵgetLocalePluralCase, LocaleDataIndex as ɵLocaleDataIndex, registerLocaleData as ɵregisterLocaleData, unregisterAllLocaleData as ɵunregisterLocaleData} from './i18n/locale_data_api'; export {DEFAULT_LOCALE_ID as ɵDEFAULT_LOCALE_ID} from './i18n/localization'; export {ivyEnabled as ɵivyEnabled} from './ivy_switch'; export {ComponentFactory as ɵComponentFactory} from './linker/component_factory'; @@ -24,7 +24,7 @@ export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} fr export {clearResolutionOfComponentResourcesQueue as ɵclearResolutionOfComponentResourcesQueue, resolveComponentResources as ɵresolveComponentResources} from './metadata/resource_loading'; export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/reflection_capabilities'; export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn} from './reflection/types'; -export {BypassType as ɵBypassType, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue, allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, getSanitizationBypassType as ɵgetSanitizationBypassType, unwrapSafeValue as ɵunwrapSafeValue} from './sanitization/bypass'; +export {allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, BypassType as ɵBypassType, getSanitizationBypassType as ɵgetSanitizationBypassType, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue, unwrapSafeValue as ɵunwrapSafeValue} from './sanitization/bypass'; export {_sanitizeHtml as ɵ_sanitizeHtml} from './sanitization/html_sanitizer'; export {_sanitizeStyle as ɵ_sanitizeStyle} from './sanitization/style_sanitizer'; export {_sanitizeUrl as ɵ_sanitizeUrl} from './sanitization/url_sanitizer'; diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 24fdccfd2a182..0f1c8e77d58f5 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -7,7 +7,101 @@ */ // clang-format off +// we reexport these symbols just so that they are retained during the dead code elimination +// performed by rollup while it's creating fesm files. +// +// no code actually imports these symbols from the @angular/core entry point +export { + compileNgModuleFactory__POST_R3__ as ɵcompileNgModuleFactory__POST_R3__, + isBoundToModule__POST_R3__ as ɵisBoundToModule__POST_R3__ +} from './application_ref'; +export { + SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as ɵSWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__, +} from './change_detection/change_detector_ref'; +export { + getDebugNode__POST_R3__ as ɵgetDebugNode__POST_R3__, +} from './debug/debug_node'; export { + SWITCH_COMPILE_INJECTABLE__POST_R3__ as ɵSWITCH_COMPILE_INJECTABLE__POST_R3__, +} from './di/injectable'; +export {INJECTOR_IMPL__POST_R3__ as ɵINJECTOR_IMPL__POST_R3__} from './di/injector'; +export { + NG_INJ_DEF as ɵNG_INJ_DEF, + NG_PROV_DEF as ɵNG_PROV_DEF, +} from './di/interface/defs'; +export {createInjector as ɵcreateInjector} from './di/r3_injector'; +export { + SWITCH_IVY_ENABLED__POST_R3__ as ɵSWITCH_IVY_ENABLED__POST_R3__, +} from './ivy_switch'; +export { + Compiler_compileModuleAndAllComponentsAsync__POST_R3__ as ɵCompiler_compileModuleAndAllComponentsAsync__POST_R3__, + Compiler_compileModuleAndAllComponentsSync__POST_R3__ as ɵCompiler_compileModuleAndAllComponentsSync__POST_R3__, + Compiler_compileModuleAsync__POST_R3__ as ɵCompiler_compileModuleAsync__POST_R3__, + Compiler_compileModuleSync__POST_R3__ as ɵCompiler_compileModuleSync__POST_R3__, +} from './linker/compiler'; +export { + SWITCH_ELEMENT_REF_FACTORY__POST_R3__ as ɵSWITCH_ELEMENT_REF_FACTORY__POST_R3__, +} from './linker/element_ref'; +export { getModuleFactory__POST_R3__ as ɵgetModuleFactory__POST_R3__ } from './linker/ng_module_factory_loader'; +export { registerNgModuleType as ɵregisterNgModuleType } from './linker/ng_module_factory_registration'; +export { + SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as ɵSWITCH_TEMPLATE_REF_FACTORY__POST_R3__, +} from './linker/template_ref'; +export { + SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ as ɵSWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__, +} from './linker/view_container_ref'; +export { + SWITCH_COMPILE_COMPONENT__POST_R3__ as ɵSWITCH_COMPILE_COMPONENT__POST_R3__, + SWITCH_COMPILE_DIRECTIVE__POST_R3__ as ɵSWITCH_COMPILE_DIRECTIVE__POST_R3__, + SWITCH_COMPILE_PIPE__POST_R3__ as ɵSWITCH_COMPILE_PIPE__POST_R3__, +} from './metadata/directives'; +export { + NgModuleDef as ɵNgModuleDef, + NgModuleTransitiveScopes as ɵNgModuleTransitiveScopes, + ɵɵNgModuleDefWithMeta, +} from './metadata/ng_module'; +export { + SWITCH_COMPILE_NGMODULE__POST_R3__ as ɵSWITCH_COMPILE_NGMODULE__POST_R3__, +} from './metadata/ng_module'; +export { + SWITCH_RENDERER2_FACTORY__POST_R3__ as ɵSWITCH_RENDERER2_FACTORY__POST_R3__, +} from './render/api'; +export { + getLContext as ɵgetLContext +} from './render3/context_discovery'; +export { + NG_COMP_DEF as ɵNG_COMP_DEF, + NG_DIR_DEF as ɵNG_DIR_DEF, + NG_ELEMENT_ID as ɵNG_ELEMENT_ID, + NG_MOD_DEF as ɵNG_MOD_DEF, + NG_PIPE_DEF as ɵNG_PIPE_DEF, +} from './render3/fields'; +export { + AttributeMarker as ɵAttributeMarker, + ComponentDef as ɵComponentDef, + ComponentFactory as ɵRender3ComponentFactory, + ComponentRef as ɵRender3ComponentRef, + ComponentType as ɵComponentType, + CssSelectorList as ɵCssSelectorList, + detectChanges as ɵdetectChanges, + DirectiveDef as ɵDirectiveDef, + DirectiveType as ɵDirectiveType, + getDirectives as ɵgetDirectives, + getHostElement as ɵgetHostElement, + LifecycleHooksFeature as ɵLifecycleHooksFeature, + markDirty as ɵmarkDirty, + NgModuleFactory as ɵNgModuleFactory, + NgModuleRef as ɵRender3NgModuleRef, + NgModuleType as ɵNgModuleType, + NO_CHANGE as ɵNO_CHANGE, + PipeDef as ɵPipeDef, + renderComponent as ɵrenderComponent, + RenderFlags as ɵRenderFlags, + setClassMetadata as ɵsetClassMetadata, + setLocaleId as ɵsetLocaleId, + store as ɵstore, + whenRendered as ɵwhenRendered, + ɵɵadvance, ɵɵattribute, ɵɵattributeInterpolate1, ɵɵattributeInterpolate2, @@ -18,88 +112,72 @@ export { ɵɵattributeInterpolate7, ɵɵattributeInterpolate8, ɵɵattributeInterpolateV, + ɵɵclassMap, + ɵɵclassMapInterpolate1, + ɵɵclassMapInterpolate2, + ɵɵclassMapInterpolate3, + ɵɵclassMapInterpolate4, + ɵɵclassMapInterpolate5, + ɵɵclassMapInterpolate6, + ɵɵclassMapInterpolate7, + ɵɵclassMapInterpolate8, + ɵɵclassMapInterpolateV, + ɵɵclassProp, + ɵɵComponentDefWithMeta, + ɵɵcomponentHostSyntheticListener, + ɵɵcontainer, + ɵɵcontainerRefreshEnd, + ɵɵcontainerRefreshStart, + ɵɵcontentQuery, + ɵɵCopyDefinitionFeature, ɵɵdefineComponent, ɵɵdefineDirective, - ɵɵdefinePipe, ɵɵdefineNgModule, - detectChanges as ɵdetectChanges, - renderComponent as ɵrenderComponent, - AttributeMarker as ɵAttributeMarker, - ComponentType as ɵComponentType, - ComponentFactory as ɵRender3ComponentFactory, - ComponentRef as ɵRender3ComponentRef, - DirectiveType as ɵDirectiveType, - RenderFlags as ɵRenderFlags, + ɵɵdefinePipe, + ɵɵDirectiveDefWithMeta, ɵɵdirectiveInject, - ɵɵinjectAttribute, - ɵɵinjectPipeChangeDetectorRef, - ɵɵinvalidFactory, + ɵɵdisableBindings, + ɵɵelement, + ɵɵelementContainer, + ɵɵelementContainerEnd, + ɵɵelementContainerStart, + ɵɵelementEnd, + ɵɵelementStart, + ɵɵembeddedViewEnd, + ɵɵembeddedViewStart, + ɵɵenableBindings, + ɵɵFactoryDef, + ɵɵgetCurrentView, ɵɵgetFactoryOf, ɵɵgetInheritedFactory, - ɵɵsetComponentScope, - ɵɵsetNgModuleScope, - ɵɵtemplateRefExtractor, - ɵɵProvidersFeature, - ɵɵCopyDefinitionFeature, + ɵɵhostProperty, + ɵɵi18n, + ɵɵi18nApply, + ɵɵi18nAttributes, + ɵɵi18nEnd, + ɵɵi18nExp, + ɵɵi18nPostprocess, + ɵɵi18nStart, ɵɵInheritDefinitionFeature, - ɵɵNgOnChangesFeature, - LifecycleHooksFeature as ɵLifecycleHooksFeature, - NgModuleType as ɵNgModuleType, - NgModuleRef as ɵRender3NgModuleRef, - CssSelectorList as ɵCssSelectorList, - markDirty as ɵmarkDirty, - NgModuleFactory as ɵNgModuleFactory, - NO_CHANGE as ɵNO_CHANGE, - ɵɵcontainer, - ɵɵnextContext, - ɵɵelementStart, + ɵɵinjectAttribute, + ɵɵinjectPipeChangeDetectorRef, + ɵɵinvalidFactory, + ɵɵlistener, + ɵɵloadQuery, ɵɵnamespaceHTML, ɵɵnamespaceMathML, ɵɵnamespaceSVG, - ɵɵelement, - ɵɵlistener, - ɵɵtext, - ɵɵtextInterpolate, - ɵɵtextInterpolate1, - ɵɵtextInterpolate2, - ɵɵtextInterpolate3, - ɵɵtextInterpolate4, - ɵɵtextInterpolate5, - ɵɵtextInterpolate6, - ɵɵtextInterpolate7, - ɵɵtextInterpolate8, - ɵɵtextInterpolateV, - ɵɵembeddedViewStart, - ɵɵprojection, + ɵɵnextContext, + ɵɵNgOnChangesFeature, + ɵɵpipe, ɵɵpipeBind1, ɵɵpipeBind2, ɵɵpipeBind3, ɵɵpipeBind4, ɵɵpipeBindV, - ɵɵpureFunction0, - ɵɵpureFunction1, - ɵɵpureFunction2, - ɵɵpureFunction3, - ɵɵpureFunction4, - ɵɵpureFunction5, - ɵɵpureFunction6, - ɵɵpureFunction7, - ɵɵpureFunction8, - ɵɵpureFunctionV, - ɵɵgetCurrentView, - getDirectives as ɵgetDirectives, - getHostElement as ɵgetHostElement, - ɵɵrestoreView, - ɵɵcontainerRefreshStart, - ɵɵcontainerRefreshEnd, - ɵɵqueryRefresh, - ɵɵviewQuery, - ɵɵstaticViewQuery, - ɵɵstaticContentQuery, - ɵɵcontentQuery, - ɵɵloadQuery, - ɵɵelementEnd, - ɵɵhostProperty, + ɵɵPipeDefWithMeta, + ɵɵprojection, + ɵɵprojectionDef, ɵɵproperty, ɵɵpropertyInterpolate, ɵɵpropertyInterpolate1, @@ -111,15 +189,29 @@ export { ɵɵpropertyInterpolate7, ɵɵpropertyInterpolate8, ɵɵpropertyInterpolateV, - ɵɵupdateSyntheticHostBinding, - ɵɵcomponentHostSyntheticListener, - ɵɵprojectionDef, + ɵɵProvidersFeature, + ɵɵpureFunction0, + ɵɵpureFunction1, + ɵɵpureFunction2, + ɵɵpureFunction3, + ɵɵpureFunction4, + ɵɵpureFunction5, + ɵɵpureFunction6, + ɵɵpureFunction7, + ɵɵpureFunction8, + ɵɵpureFunctionV, + ɵɵqueryRefresh, ɵɵreference, - ɵɵenableBindings, - ɵɵdisableBindings, - ɵɵelementContainerStart, - ɵɵelementContainerEnd, - ɵɵelementContainer, + ɵɵresolveBody, + ɵɵresolveDocument, + ɵɵresolveWindow, + ɵɵrestoreView, + + ɵɵselect, + ɵɵsetComponentScope, + ɵɵsetNgModuleScope, + ɵɵstaticContentQuery, + ɵɵstaticViewQuery, ɵɵstyleMap, ɵɵstyleMapInterpolate1, ɵɵstyleMapInterpolate2, @@ -130,17 +222,6 @@ export { ɵɵstyleMapInterpolate7, ɵɵstyleMapInterpolate8, ɵɵstyleMapInterpolateV, - ɵɵstyleSanitizer, - ɵɵclassMap, - ɵɵclassMapInterpolate1, - ɵɵclassMapInterpolate2, - ɵɵclassMapInterpolate3, - ɵɵclassMapInterpolate4, - ɵɵclassMapInterpolate5, - ɵɵclassMapInterpolate6, - ɵɵclassMapInterpolate7, - ɵɵclassMapInterpolate8, - ɵɵclassMapInterpolateV, ɵɵstyleProp, ɵɵstylePropInterpolate1, ɵɵstylePropInterpolate2, @@ -151,170 +232,72 @@ export { ɵɵstylePropInterpolate7, ɵɵstylePropInterpolate8, ɵɵstylePropInterpolateV, - ɵɵclassProp, - - ɵɵselect, - ɵɵadvance, + ɵɵstyleSanitizer, ɵɵtemplate, - ɵɵembeddedViewEnd, - store as ɵstore, - ɵɵpipe, - ComponentDef as ɵComponentDef, - ɵɵComponentDefWithMeta, - ɵɵFactoryDef, - DirectiveDef as ɵDirectiveDef, - ɵɵDirectiveDefWithMeta, - PipeDef as ɵPipeDef, - ɵɵPipeDefWithMeta, - whenRendered as ɵwhenRendered, - ɵɵi18n, - ɵɵi18nAttributes, - ɵɵi18nExp, - ɵɵi18nStart, - ɵɵi18nEnd, - ɵɵi18nApply, - ɵɵi18nPostprocess, - setLocaleId as ɵsetLocaleId, - setClassMetadata as ɵsetClassMetadata, - ɵɵresolveWindow, - ɵɵresolveDocument, - ɵɵresolveBody, + ɵɵtemplateRefExtractor, + ɵɵtext, + ɵɵtextInterpolate, + ɵɵtextInterpolate1, + ɵɵtextInterpolate2, + ɵɵtextInterpolate3, + ɵɵtextInterpolate4, + ɵɵtextInterpolate5, + ɵɵtextInterpolate6, + ɵɵtextInterpolate7, + ɵɵtextInterpolate8, + ɵɵtextInterpolateV, + ɵɵupdateSyntheticHostBinding, + ɵɵviewQuery, } from './render3/index'; - - +export { + LContext as ɵLContext, +} from './render3/interfaces/context'; +export { + setDocument as ɵsetDocument +} from './render3/interfaces/document'; +export { + Player as ɵPlayer, + PlayerFactory as ɵPlayerFactory, + PlayerHandler as ɵPlayerHandler, + PlayState as ɵPlayState, +} from './render3/interfaces/player'; export { compileComponent as ɵcompileComponent, compileDirective as ɵcompileDirective, } from './render3/jit/directive'; +export { + resetJitOptions as ɵresetJitOptions, +} from './render3/jit/jit_options'; export { compileNgModule as ɵcompileNgModule, compileNgModuleDefs as ɵcompileNgModuleDefs, + flushModuleScopingQueueAsMuchAsPossible as ɵflushModuleScopingQueueAsMuchAsPossible, patchComponentDefWithScope as ɵpatchComponentDefWithScope, resetCompiledComponents as ɵresetCompiledComponents, - flushModuleScopingQueueAsMuchAsPossible as ɵflushModuleScopingQueueAsMuchAsPossible, transitiveScopesFor as ɵtransitiveScopesFor, } from './render3/jit/module'; export { compilePipe as ɵcompilePipe, } from './render3/jit/pipe'; export { - resetJitOptions as ɵresetJitOptions, -} from './render3/jit/jit_options'; - + publishDefaultGlobalUtils as ɵpublishDefaultGlobalUtils +, + publishGlobalUtil as ɵpublishGlobalUtil} from './render3/util/global_utils'; export { - NgModuleDef as ɵNgModuleDef, - ɵɵNgModuleDefWithMeta, - NgModuleTransitiveScopes as ɵNgModuleTransitiveScopes, -} from './metadata/ng_module'; - + bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml, + bypassSanitizationTrustResourceUrl as ɵbypassSanitizationTrustResourceUrl, + bypassSanitizationTrustScript as ɵbypassSanitizationTrustScript, + bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle, + bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl, +} from './sanitization/bypass'; export { - ɵɵsanitizeHtml, - ɵɵsanitizeStyle, ɵɵdefaultStyleSanitizer, + ɵɵsanitizeHtml, + ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, + ɵɵsanitizeStyle, ɵɵsanitizeUrl, - ɵɵsanitizeResourceUrl, ɵɵsanitizeUrlOrResourceUrl, } from './sanitization/sanitization'; -export { - bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml, - bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle, - bypassSanitizationTrustScript as ɵbypassSanitizationTrustScript, - bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl, - bypassSanitizationTrustResourceUrl as ɵbypassSanitizationTrustResourceUrl, -} from './sanitization/bypass'; - -export { - getLContext as ɵgetLContext -} from './render3/context_discovery'; - -export { - NG_ELEMENT_ID as ɵNG_ELEMENT_ID, - NG_COMP_DEF as ɵNG_COMP_DEF, - NG_DIR_DEF as ɵNG_DIR_DEF, - NG_PIPE_DEF as ɵNG_PIPE_DEF, - NG_MOD_DEF as ɵNG_MOD_DEF, -} from './render3/fields'; - -export { - NG_PROV_DEF as ɵNG_PROV_DEF, - NG_INJ_DEF as ɵNG_INJ_DEF, -} from './di/interface/defs'; - -export { - Player as ɵPlayer, - PlayerFactory as ɵPlayerFactory, - PlayState as ɵPlayState, - PlayerHandler as ɵPlayerHandler, -} from './render3/interfaces/player'; - -export { - LContext as ɵLContext, -} from './render3/interfaces/context'; - -export { - setDocument as ɵsetDocument -} from './render3/interfaces/document'; - -// we reexport these symbols just so that they are retained during the dead code elimination -// performed by rollup while it's creating fesm files. -// -// no code actually imports these symbols from the @angular/core entry point -export { - compileNgModuleFactory__POST_R3__ as ɵcompileNgModuleFactory__POST_R3__, - isBoundToModule__POST_R3__ as ɵisBoundToModule__POST_R3__ -} from './application_ref'; -export { - SWITCH_COMPILE_COMPONENT__POST_R3__ as ɵSWITCH_COMPILE_COMPONENT__POST_R3__, - SWITCH_COMPILE_DIRECTIVE__POST_R3__ as ɵSWITCH_COMPILE_DIRECTIVE__POST_R3__, - SWITCH_COMPILE_PIPE__POST_R3__ as ɵSWITCH_COMPILE_PIPE__POST_R3__, -} from './metadata/directives'; -export { - SWITCH_COMPILE_NGMODULE__POST_R3__ as ɵSWITCH_COMPILE_NGMODULE__POST_R3__, -} from './metadata/ng_module'; -export { - getDebugNode__POST_R3__ as ɵgetDebugNode__POST_R3__, -} from './debug/debug_node'; -export { - SWITCH_COMPILE_INJECTABLE__POST_R3__ as ɵSWITCH_COMPILE_INJECTABLE__POST_R3__, -} from './di/injectable'; -export { - SWITCH_IVY_ENABLED__POST_R3__ as ɵSWITCH_IVY_ENABLED__POST_R3__, -} from './ivy_switch'; -export { - SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as ɵSWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__, -} from './change_detection/change_detector_ref'; -export { - Compiler_compileModuleSync__POST_R3__ as ɵCompiler_compileModuleSync__POST_R3__, - Compiler_compileModuleAsync__POST_R3__ as ɵCompiler_compileModuleAsync__POST_R3__, - Compiler_compileModuleAndAllComponentsSync__POST_R3__ as ɵCompiler_compileModuleAndAllComponentsSync__POST_R3__, - Compiler_compileModuleAndAllComponentsAsync__POST_R3__ as ɵCompiler_compileModuleAndAllComponentsAsync__POST_R3__, -} from './linker/compiler'; -export { - SWITCH_ELEMENT_REF_FACTORY__POST_R3__ as ɵSWITCH_ELEMENT_REF_FACTORY__POST_R3__, -} from './linker/element_ref'; -export { - SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as ɵSWITCH_TEMPLATE_REF_FACTORY__POST_R3__, -} from './linker/template_ref'; -export { - SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ as ɵSWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__, -} from './linker/view_container_ref'; -export { - SWITCH_RENDERER2_FACTORY__POST_R3__ as ɵSWITCH_RENDERER2_FACTORY__POST_R3__, -} from './render/api'; - -export { getModuleFactory__POST_R3__ as ɵgetModuleFactory__POST_R3__ } from './linker/ng_module_factory_loader'; - -export { registerNgModuleType as ɵregisterNgModuleType } from './linker/ng_module_factory_registration'; - -export { - publishGlobalUtil as ɵpublishGlobalUtil, - publishDefaultGlobalUtils as ɵpublishDefaultGlobalUtils -} from './render3/util/global_utils'; - -export {createInjector as ɵcreateInjector} from './di/r3_injector'; - -export {INJECTOR_IMPL__POST_R3__ as ɵINJECTOR_IMPL__POST_R3__} from './di/injector'; - // clang-format on diff --git a/packages/core/src/debug/debug_node.ts b/packages/core/src/debug/debug_node.ts index 3270a568f13be..c6161dbe81110 100644 --- a/packages/core/src/debug/debug_node.ts +++ b/packages/core/src/debug/debug_node.ts @@ -10,7 +10,7 @@ import {Injector} from '../di'; import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces/container'; import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node'; import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks'; -import {DECLARATION_COMPONENT_VIEW, LView, PARENT, TData, TVIEW, T_HOST} from '../render3/interfaces/view'; +import {DECLARATION_COMPONENT_VIEW, LView, PARENT, T_HOST, TData, TVIEW} from '../render3/interfaces/view'; import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, getOwningComponent, loadLContext} from '../render3/util/discovery_utils'; import {INTERPOLATION_DELIMITER, renderStringify} from '../render3/util/misc_utils'; import {getComponentLViewByIndex, getNativeByTNodeOrNull} from '../render3/util/view_utils'; @@ -53,15 +53,25 @@ export class DebugNode__PRE_R3__ { } } - get injector(): Injector { return this._debugContext.injector; } + get injector(): Injector { + return this._debugContext.injector; + } - get componentInstance(): any { return this._debugContext.component; } + get componentInstance(): any { + return this._debugContext.component; + } - get context(): any { return this._debugContext.context; } + get context(): any { + return this._debugContext.context; + } - get references(): {[key: string]: any} { return this._debugContext.references; } + get references(): {[key: string]: any} { + return this._debugContext.references; + } - get providerTokens(): any[] { return this._debugContext.providerTokens; } + get providerTokens(): any[] { + return this._debugContext.providerTokens; + } } /** @@ -70,9 +80,9 @@ export class DebugNode__PRE_R3__ { export interface DebugElement extends DebugNode { readonly name: string; readonly properties: {[key: string]: any}; - readonly attributes: {[key: string]: string | null}; + readonly attributes: {[key: string]: string|null}; readonly classes: {[key: string]: boolean}; - readonly styles: {[key: string]: string | null}; + readonly styles: {[key: string]: string|null}; readonly childNodes: DebugNode[]; readonly nativeElement: any; readonly children: DebugElement[]; @@ -83,11 +93,11 @@ export interface DebugElement extends DebugNode { triggerEventHandler(eventName: string, eventObj: any): void; } export class DebugElement__PRE_R3__ extends DebugNode__PRE_R3__ implements DebugElement { - readonly name !: string; + readonly name!: string; readonly properties: {[key: string]: any} = {}; - readonly attributes: {[key: string]: string | null} = {}; + readonly attributes: {[key: string]: string|null} = {}; readonly classes: {[key: string]: boolean} = {}; - readonly styles: {[key: string]: string | null} = {}; + readonly styles: {[key: string]: string|null} = {}; readonly childNodes: DebugNode[] = []; readonly nativeElement: any; @@ -99,14 +109,14 @@ export class DebugElement__PRE_R3__ extends DebugNode__PRE_R3__ implements Debug addChild(child: DebugNode) { if (child) { this.childNodes.push(child); - (child as{parent: DebugNode}).parent = this; + (child as {parent: DebugNode}).parent = this; } } removeChild(child: DebugNode) { const childIndex = this.childNodes.indexOf(child); if (childIndex !== -1) { - (child as{parent: DebugNode | null}).parent = null; + (child as {parent: DebugNode | null}).parent = null; this.childNodes.splice(childIndex, 1); } } @@ -119,7 +129,7 @@ export class DebugElement__PRE_R3__ extends DebugNode__PRE_R3__ implements Debug if (c.parent) { (c.parent as DebugElement__PRE_R3__).removeChild(c); } - (child as{parent: DebugNode}).parent = this; + (child as {parent: DebugNode}).parent = this; }); } } @@ -132,7 +142,7 @@ export class DebugElement__PRE_R3__ extends DebugNode__PRE_R3__ implements Debug if (newChild.parent) { (newChild.parent as DebugElement__PRE_R3__).removeChild(newChild); } - (newChild as{parent: DebugNode}).parent = this; + (newChild as {parent: DebugNode}).parent = this; this.childNodes.splice(refIndex, 0, newChild); } } @@ -155,9 +165,8 @@ export class DebugElement__PRE_R3__ extends DebugNode__PRE_R3__ implements Debug } get children(): DebugElement[] { - return this - .childNodes // - .filter((node) => node instanceof DebugElement__PRE_R3__) as DebugElement[]; + return this.childNodes // + .filter((node) => node instanceof DebugElement__PRE_R3__) as DebugElement[]; } triggerEventHandler(eventName: string, eventObj: any) { @@ -205,14 +214,18 @@ function _queryNodeChildren( class DebugNode__POST_R3__ implements DebugNode { readonly nativeNode: Node; - constructor(nativeNode: Node) { this.nativeNode = nativeNode; } + constructor(nativeNode: Node) { + this.nativeNode = nativeNode; + } get parent(): DebugElement|null { const parent = this.nativeNode.parentNode as Element; return parent ? new DebugElement__POST_R3__(parent) : null; } - get injector(): Injector { return getInjector(this.nativeNode); } + get injector(): Injector { + return getInjector(this.nativeNode); + } get componentInstance(): any { const nativeElement = this.nativeNode; @@ -227,9 +240,13 @@ class DebugNode__POST_R3__ implements DebugNode { return getListeners(this.nativeNode as Element).filter(listener => listener.type === 'dom'); } - get references(): {[key: string]: any;} { return getLocalRefs(this.nativeNode); } + get references(): {[key: string]: any;} { + return getLocalRefs(this.nativeNode); + } - get providerTokens(): any[] { return getInjectionTokens(this.nativeNode as Element); } + get providerTokens(): any[] { + return getInjectionTokens(this.nativeNode as Element); + } } class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugElement { @@ -244,11 +261,11 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme get name(): string { try { - const context = loadLContext(this.nativeNode) !; + const context = loadLContext(this.nativeNode)!; const lView = context.lView; const tData = lView[TVIEW].data; const tNode = tData[context.nodeIndex] as TNode; - return tNode.tagName !; + return tNode.tagName!; } catch (e) { return this.nativeNode.nodeName; } @@ -285,8 +302,8 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme return properties; } - get attributes(): {[key: string]: string | null;} { - const attributes: {[key: string]: string | null;} = {}; + get attributes(): {[key: string]: string|null;} { + const attributes: {[key: string]: string|null;} = {}; const element = this.nativeElement; if (!element) { @@ -343,9 +360,9 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme return attributes; } - get styles(): {[key: string]: string | null} { + get styles(): {[key: string]: string|null} { if (this.nativeElement && (this.nativeElement as HTMLElement).style) { - return (this.nativeElement as HTMLElement).style as{[key: string]: any}; + return (this.nativeElement as HTMLElement).style as {[key: string]: any}; } return {}; } @@ -438,7 +455,7 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme } } -function copyDomProperties(element: Element | null, properties: {[name: string]: string}): void { +function copyDomProperties(element: Element|null, properties: {[name: string]: string}): void { if (element) { // Skip own properties (as those are patched) let obj = Object.getPrototypeOf(element); @@ -481,8 +498,8 @@ function _queryAllR3( parentElement: DebugElement, predicate: Predicate<DebugNode>, matches: DebugNode[], elementsOnly: false): void; function _queryAllR3( - parentElement: DebugElement, predicate: Predicate<DebugElement>| Predicate<DebugNode>, - matches: DebugElement[] | DebugNode[], elementsOnly: boolean) { + parentElement: DebugElement, predicate: Predicate<DebugElement>|Predicate<DebugNode>, + matches: DebugElement[]|DebugNode[], elementsOnly: boolean) { const context = loadLContext(parentElement.nativeNode, false); if (context !== null) { const parentTNode = context.lView[TVIEW].data[context.nodeIndex] as TNode; @@ -506,8 +523,8 @@ function _queryAllR3( * @param rootNativeNode the root native node on which predicate should not be matched */ function _queryNodeChildrenR3( - tNode: TNode, lView: LView, predicate: Predicate<DebugElement>| Predicate<DebugNode>, - matches: DebugElement[] | DebugNode[], elementsOnly: boolean, rootNativeNode: any) { + tNode: TNode, lView: LView, predicate: Predicate<DebugElement>|Predicate<DebugNode>, + matches: DebugElement[]|DebugNode[], elementsOnly: boolean, rootNativeNode: any) { const nativeNode = getNativeByTNodeOrNull(tNode, lView); // For each type of TNode, specific logic is executed. if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) { @@ -520,7 +537,7 @@ function _queryNodeChildrenR3( const componentView = getComponentLViewByIndex(tNode.index, lView); if (componentView && componentView[TVIEW].firstChild) { _queryNodeChildrenR3( - componentView[TVIEW].firstChild !, componentView, predicate, matches, elementsOnly, + componentView[TVIEW].firstChild!, componentView, predicate, matches, elementsOnly, rootNativeNode); } } else { @@ -556,17 +573,17 @@ function _queryNodeChildrenR3( } else if (tNode.type === TNodeType.Projection) { // Case 3: the TNode is a projection insertion point (i.e. a <ng-content>). // The nodes projected at this location all need to be processed. - const componentView = lView ![DECLARATION_COMPONENT_VIEW]; + const componentView = lView![DECLARATION_COMPONENT_VIEW]; const componentHost = componentView[T_HOST] as TElementNode; const head: TNode|null = - (componentHost.projection as(TNode | null)[])[tNode.projection as number]; + (componentHost.projection as (TNode | null)[])[tNode.projection as number]; if (Array.isArray(head)) { for (let nativeNode of head) { _addQueryMatchR3(nativeNode, predicate, matches, elementsOnly, rootNativeNode); } } else if (head) { - const nextLView = componentView[PARENT] !as LView; + const nextLView = componentView[PARENT]! as LView; const nextTNode = nextLView[TVIEW].data[head.index] as TNode; _queryNodeChildrenR3(nextTNode, nextLView, predicate, matches, elementsOnly, rootNativeNode); } @@ -596,12 +613,12 @@ function _queryNodeChildrenR3( * @param rootNativeNode the root native node on which predicate should not be matched */ function _queryNodeChildrenInContainerR3( - lContainer: LContainer, predicate: Predicate<DebugElement>| Predicate<DebugNode>, - matches: DebugElement[] | DebugNode[], elementsOnly: boolean, rootNativeNode: any) { + lContainer: LContainer, predicate: Predicate<DebugElement>|Predicate<DebugNode>, + matches: DebugElement[]|DebugNode[], elementsOnly: boolean, rootNativeNode: any) { for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { const childView = lContainer[i]; _queryNodeChildrenR3( - childView[TVIEW].node !, childView, predicate, matches, elementsOnly, rootNativeNode); + childView[TVIEW].node!, childView, predicate, matches, elementsOnly, rootNativeNode); } } @@ -615,8 +632,8 @@ function _queryNodeChildrenInContainerR3( * @param rootNativeNode the root native node on which predicate should not be matched */ function _addQueryMatchR3( - nativeNode: any, predicate: Predicate<DebugElement>| Predicate<DebugNode>, - matches: DebugElement[] | DebugNode[], elementsOnly: boolean, rootNativeNode: any) { + nativeNode: any, predicate: Predicate<DebugElement>|Predicate<DebugNode>, + matches: DebugElement[]|DebugNode[], elementsOnly: boolean, rootNativeNode: any) { if (rootNativeNode !== nativeNode) { const debugNode = getDebugNode(nativeNode); if (!debugNode) { @@ -645,8 +662,8 @@ function _addQueryMatchR3( * @param elementsOnly whether only elements should be searched */ function _queryNativeNodeDescendants( - parentNode: any, predicate: Predicate<DebugElement>| Predicate<DebugNode>, - matches: DebugElement[] | DebugNode[], elementsOnly: boolean) { + parentNode: any, predicate: Predicate<DebugElement>|Predicate<DebugNode>, + matches: DebugElement[]|DebugNode[], elementsOnly: boolean) { const nodes = parentNode.childNodes; const length = nodes.length; @@ -757,7 +774,9 @@ export function removeDebugNodeFromIndex(node: DebugNode) { * * @publicApi */ -export interface Predicate<T> { (value: T): boolean; } +export interface Predicate<T> { + (value: T): boolean; +} /** * @publicApi diff --git a/packages/core/src/di/forward_ref.ts b/packages/core/src/di/forward_ref.ts index 65708c7a4dc53..e13705b91a6a0 100644 --- a/packages/core/src/di/forward_ref.ts +++ b/packages/core/src/di/forward_ref.ts @@ -21,7 +21,9 @@ import {stringify} from '../util/stringify'; * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref_fn'} * @publicApi */ -export interface ForwardRefFn { (): any; } +export interface ForwardRefFn { + (): any; +} const __forward_ref__ = getClosureSafeProperty({__forward_ref__: getClosureSafeProperty}); @@ -39,7 +41,9 @@ const __forward_ref__ = getClosureSafeProperty({__forward_ref__: getClosureSafeP */ export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> { (<any>forwardRefFn).__forward_ref__ = forwardRef; - (<any>forwardRefFn).toString = function() { return stringify(this()); }; + (<any>forwardRefFn).toString = function() { + return stringify(this()); + }; return (<Type<any>><any>forwardRefFn); } diff --git a/packages/core/src/di/injectable.ts b/packages/core/src/di/injectable.ts index e6a3c10050104..fc20a2a2697a3 100644 --- a/packages/core/src/di/injectable.ts +++ b/packages/core/src/di/injectable.ts @@ -7,9 +7,9 @@ */ import {Type} from '../interface/type'; -import {TypeDecorator, makeDecorator} from '../util/decorators'; +import {makeDecorator, TypeDecorator} from '../util/decorators'; -import {InjectableType, getInjectableDef, ɵɵdefineInjectable} from './interface/defs'; +import {getInjectableDef, InjectableType, ɵɵdefineInjectable} from './interface/defs'; import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueSansProvider} from './interface/provider'; import {compileInjectable as render3CompileInjectable} from './jit/injectable'; import {convertInjectableProviderToFactory} from './util'; @@ -21,8 +21,8 @@ import {convertInjectableProviderToFactory} from './util'; * * @publicApi */ -export type InjectableProvider = ValueSansProvider | ExistingSansProvider | - StaticClassSansProvider | ConstructorSansProvider | FactorySansProvider | ClassSansProvider; +export type InjectableProvider = ValueSansProvider|ExistingSansProvider|StaticClassSansProvider| + ConstructorSansProvider|FactorySansProvider|ClassSansProvider; /** * Type of the Injectable decorator / constructor function. @@ -50,11 +50,11 @@ export interface InjectableDecorator { * */ (): TypeDecorator; - (options?: {providedIn: Type<any>| 'root' | 'platform' | 'any' | null}& + (options?: {providedIn: Type<any>|'root'|'platform'|'any'|null}& InjectableProvider): TypeDecorator; - new (): Injectable; - new (options?: {providedIn: Type<any>| 'root' | 'platform' | 'any' | null}& - InjectableProvider): Injectable; + new(): Injectable; + new(options?: {providedIn: Type<any>|'root'|'platform'|'any'|null}& + InjectableProvider): Injectable; } /** @@ -91,9 +91,9 @@ export const Injectable: InjectableDecorator = makeDecorator( /** * Supports @Injectable() in JIT mode for Render2. */ -function render2CompileInjectable(injectableType: Type<any>, options?: { - providedIn?: Type<any>| 'root' | 'platform' | 'any' | null -} & InjectableProvider): void { +function render2CompileInjectable( + injectableType: Type<any>, + options?: {providedIn?: Type<any>|'root'|'platform'|'any'|null}&InjectableProvider): void { if (options && options.providedIn !== undefined && !getInjectableDef(injectableType)) { (injectableType as InjectableType<any>).ɵprov = ɵɵdefineInjectable({ token: injectableType, diff --git a/packages/core/src/di/injection_token.ts b/packages/core/src/di/injection_token.ts index 5a3a6feb4eed3..f45c903ce6df7 100644 --- a/packages/core/src/di/injection_token.ts +++ b/packages/core/src/di/injection_token.ts @@ -57,8 +57,7 @@ export class InjectionToken<T> { readonly ɵprov: never|undefined; constructor(protected _desc: string, options?: { - providedIn?: Type<any>| 'root' | 'platform' | 'any' | null, - factory: () => T + providedIn?: Type<any>|'root'|'platform'|'any'|null, factory: () => T }) { this.ɵprov = undefined; if (typeof options == 'number') { @@ -75,7 +74,11 @@ export class InjectionToken<T> { } } - toString(): string { return `InjectionToken ${this._desc}`; } + toString(): string { + return `InjectionToken ${this._desc}`; + } } -export interface InjectableDefToken<T> extends InjectionToken<T> { ɵprov: never; } +export interface InjectableDefToken<T> extends InjectionToken<T> { + ɵprov: never; +} diff --git a/packages/core/src/di/injector.ts b/packages/core/src/di/injector.ts index e88b36d9a4753..607b610613bfb 100644 --- a/packages/core/src/di/injector.ts +++ b/packages/core/src/di/injector.ts @@ -11,7 +11,7 @@ import {stringify} from '../util/stringify'; import {resolveForwardRef} from './forward_ref'; import {InjectionToken} from './injection_token'; -import {INJECTOR, NG_TEMP_TOKEN_PATH, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, catchInjectorError, formatError, setCurrentInjector, ɵɵinject} from './injector_compatibility'; +import {catchInjectorError, formatError, INJECTOR, NG_TEMP_TOKEN_PATH, NullInjector, setCurrentInjector, THROW_IF_NOT_FOUND, USE_VALUE, ɵɵinject} from './injector_compatibility'; import {getInjectableDef, ɵɵdefineInjectable} from './interface/defs'; import {InjectFlags} from './interface/injector'; import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './interface/provider'; @@ -20,12 +20,12 @@ import {createInjector} from './r3_injector'; import {INJECTOR_SCOPE} from './scope'; export function INJECTOR_IMPL__PRE_R3__( - providers: StaticProvider[], parent: Injector | undefined, name: string) { + providers: StaticProvider[], parent: Injector|undefined, name: string) { return new StaticInjector(providers, parent, name); } export function INJECTOR_IMPL__POST_R3__( - providers: StaticProvider[], parent: Injector | undefined, name: string) { + providers: StaticProvider[], parent: Injector|undefined, name: string) { return createInjector({name: name}, parent, providers, name); } @@ -166,8 +166,9 @@ export class StaticInjector implements Injector { const providedIn = injectableDef && injectableDef.providedIn; if (providedIn === 'any' || providedIn != null && providedIn === this.scope) { records.set( - token, record = resolveProvider( - {provide: token, useFactory: injectableDef.factory, deps: EMPTY})); + token, + record = resolveProvider( + {provide: token, useFactory: injectableDef.factory, deps: EMPTY})); } } if (record === undefined) { @@ -193,7 +194,7 @@ export class StaticInjector implements Injector { } type SupportedProvider = - ValueProvider | ExistingProvider | StaticClassProvider | ConstructorProvider | FactoryProvider; + ValueProvider|ExistingProvider|StaticClassProvider|ConstructorProvider|FactoryProvider; interface Record { fn: Function; @@ -293,7 +294,7 @@ function recursivelyProcessProviders(records: Map<any, Record>, provider: Static } function tryResolveToken( - token: any, record: Record | undefined | null, records: Map<any, Record|null>, parent: Injector, + token: any, record: Record|undefined|null, records: Map<any, Record|null>, parent: Injector, notFoundValue: any, flags: InjectFlags): any { try { return resolveToken(token, record, records, parent, notFoundValue, flags); @@ -313,7 +314,7 @@ function tryResolveToken( } function resolveToken( - token: any, record: Record | undefined | null, records: Map<any, Record|null>, parent: Injector, + token: any, record: Record|undefined|null, records: Map<any, Record|null>, parent: Injector, notFoundValue: any, flags: InjectFlags): any { let value; if (record && !(flags & InjectFlags.SkipSelf)) { diff --git a/packages/core/src/di/injector_compatibility.ts b/packages/core/src/di/injector_compatibility.ts index bb420a7b23b17..2bef2aa01d486 100644 --- a/packages/core/src/di/injector_compatibility.ts +++ b/packages/core/src/di/injector_compatibility.ts @@ -33,7 +33,7 @@ import {Inject, Optional, Self, SkipSelf} from './metadata'; export const INJECTOR = new InjectionToken<Injector>( 'INJECTOR', -1 as any // `-1` is used by Ivy DI system as special value to recognize it as `Injector`. - ); +); const _THROW_IF_NOT_FOUND = {}; export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND; @@ -55,7 +55,7 @@ export const USE_VALUE = */ let _currentInjector: Injector|undefined|null = undefined; -export function setCurrentInjector(injector: Injector | null | undefined): Injector|undefined|null { +export function setCurrentInjector(injector: Injector|null|undefined): Injector|undefined|null { const former = _currentInjector; _currentInjector = injector; return former; @@ -70,25 +70,25 @@ export function setCurrentInjector(injector: Injector | null | undefined): Injec * 1. `Injector` should not depend on ivy logic. * 2. To maintain tree shake-ability we don't want to bring in unnecessary code. */ -let _injectImplementation: - (<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined; +let _injectImplementation: (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)| + undefined; /** * Sets the current inject implementation. */ export function setInjectImplementation( - impl: (<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null) | undefined): - (<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined { + impl: (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)| + undefined): (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined { const previous = _injectImplementation; _injectImplementation = impl; return previous; } -export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>): T; -export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T| +export function injectInjectorOnly<T>(token: Type<T>|InjectionToken<T>): T; +export function injectInjectorOnly<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags): T| null; export function injectInjectorOnly<T>( - token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null { + token: Type<T>|InjectionToken<T>, flags = InjectFlags.Default): T|null { if (_currentInjector === undefined) { throw new Error(`inject() must be called from an injection context`); } else if (_currentInjector === null) { @@ -104,15 +104,15 @@ export function injectInjectorOnly<T>( * Must be used in the context of a factory function such as one defined for an * `InjectionToken`. Throws an error if not called from such a context. * - * (Additional documentation moved to `inject`, as it is the public API, and an alias for this instruction) + * (Additional documentation moved to `inject`, as it is the public API, and an alias for this + * instruction) * * @see inject * @codeGenApi */ -export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>): T; -export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null; -export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T| - null { +export function ɵɵinject<T>(token: Type<T>|InjectionToken<T>): T; +export function ɵɵinject<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags): T|null; +export function ɵɵinject<T>(token: Type<T>|InjectionToken<T>, flags = InjectFlags.Default): T|null { return (_injectImplementation || injectInjectorOnly)(resolveForwardRef(token), flags); } @@ -130,10 +130,12 @@ export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags = InjectF */ export function ɵɵinvalidFactoryDep(index: number): never { const msg = ngDevMode ? - `This constructor is not compatible with Angular Dependency Injection because its dependency at index ${index} of the parameter list is invalid. + `This constructor is not compatible with Angular Dependency Injection because its dependency at index ${ + index} of the parameter list is invalid. This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator. -Please check that 1) the type for the parameter at index ${index} is correct and 2) the correct Angular decorators are defined for this class and its ancestors.` : +Please check that 1) the type for the parameter at index ${ + index} is correct and 2) the correct Angular decorators are defined for this class and its ancestors.` : 'invalid'; throw new Error(msg); } @@ -172,7 +174,7 @@ export const inject = ɵɵinject; * `InjectableDef`. */ export function injectRootLimpMode<T>( - token: Type<T>| InjectionToken<T>, notFoundValue: T | undefined, flags: InjectFlags): T|null { + token: Type<T>|InjectionToken<T>, notFoundValue: T|undefined, flags: InjectFlags): T|null { const injectableDef: ɵɵInjectableDef<T>|null = getInjectableDef(token); if (injectableDef && injectableDef.providedIn == 'root') { return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() : @@ -183,7 +185,7 @@ export function injectRootLimpMode<T>( throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`); } -export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): any[] { +export function injectArgs(types: (Type<any>|InjectionToken<any>|any[])[]): any[] { const args: any[] = []; for (let i = 0; i < types.length; i++) { const arg = resolveForwardRef(types[i]); @@ -210,7 +212,7 @@ export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): an } } - args.push(ɵɵinject(type !, flags)); + args.push(ɵɵinject(type!, flags)); } else { args.push(ɵɵinject(arg)); } @@ -236,7 +238,7 @@ export class NullInjector implements Injector { export function catchInjectorError( - e: any, token: any, injectorErrorName: string, source: string | null): never { + e: any, token: any, injectorErrorName: string, source: string|null): never { const tokenPath: any[] = e[NG_TEMP_TOKEN_PATH]; if (token[SOURCE]) { tokenPath.unshift(token[SOURCE]); @@ -248,7 +250,7 @@ export function catchInjectorError( } export function formatError( - text: string, obj: any, injectorErrorName: string, source: string | null = null): string { + text: string, obj: any, injectorErrorName: string, source: string|null = null): string { text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.substr(2) : text; let context = stringify(obj); if (Array.isArray(obj)) { @@ -264,5 +266,6 @@ export function formatError( } context = `{${parts.join(', ')}}`; } - return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`; + return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${ + text.replace(NEW_LINE, '\n ')}`; } diff --git a/packages/core/src/di/interface/defs.ts b/packages/core/src/di/interface/defs.ts index f30abbdd09bc5..b2d5bae5a84f4 100644 --- a/packages/core/src/di/interface/defs.ts +++ b/packages/core/src/di/interface/defs.ts @@ -140,13 +140,14 @@ export interface InjectorTypeWithProviders<T> { */ export function ɵɵdefineInjectable<T>(opts: { token: unknown, - providedIn?: Type<any>| 'root' | 'platform' | 'any' | null, - factory: () => T, + providedIn?: Type<any>|'root'|'platform'|'any'|null, factory: () => T, }): never { return ({ - token: opts.token, providedIn: opts.providedIn as any || null, factory: opts.factory, - value: undefined, - } as ɵɵInjectableDef<T>) as never; + token: opts.token, + providedIn: opts.providedIn as any || null, + factory: opts.factory, + value: undefined, + } as ɵɵInjectableDef<T>) as never; } /** @@ -179,8 +180,10 @@ export const defineInjectable = ɵɵdefineInjectable; export function ɵɵdefineInjector(options: {factory: () => any, providers?: any[], imports?: any[]}): never { return ({ - factory: options.factory, providers: options.providers || [], imports: options.imports || [], - } as ɵɵInjectorDef<any>) as never; + factory: options.factory, + providers: options.providers || [], + imports: options.imports || [], + } as ɵɵInjectorDef<any>) as never; } /** @@ -219,15 +222,17 @@ function getOwnDefinition<T>(type: any, def: ɵɵInjectableDef<T>): ɵɵInjectab */ export function getInheritedInjectableDef<T>(type: any): ɵɵInjectableDef<T>|null { // See `jit/injectable.ts#compileInjectable` for context on NG_PROV_DEF_FALLBACK. - const def = type && (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF] || - (type[NG_PROV_DEF_FALLBACK] && type[NG_PROV_DEF_FALLBACK]())); + const def = type && + (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF] || + (type[NG_PROV_DEF_FALLBACK] && type[NG_PROV_DEF_FALLBACK]())); if (def) { const typeName = getTypeName(type); // TODO(FW-1307): Re-add ngDevMode when closure can handle it // ngDevMode && console.warn( - `DEPRECATED: DI is instantiating a token "${typeName}" that inherits its @Injectable decorator but does not provide one itself.\n` + + `DEPRECATED: DI is instantiating a token "${ + typeName}" that inherits its @Injectable decorator but does not provide one itself.\n` + `This will become an error in v10. Please add @Injectable() to the "${typeName}" class.`); return def; } else { diff --git a/packages/core/src/di/interface/provider.ts b/packages/core/src/di/interface/provider.ts index ec8720d089ac2..486ed494d0979 100644 --- a/packages/core/src/di/interface/provider.ts +++ b/packages/core/src/di/interface/provider.ts @@ -255,8 +255,8 @@ export interface FactoryProvider extends FactorySansProvider { * * @publicApi */ -export type StaticProvider = ValueProvider | ExistingProvider | StaticClassProvider | - ConstructorProvider | FactoryProvider | any[]; +export type StaticProvider = + ValueProvider|ExistingProvider|StaticClassProvider|ConstructorProvider|FactoryProvider|any[]; /** @@ -329,8 +329,8 @@ export interface ClassProvider extends ClassSansProvider { * * @publicApi */ -export type Provider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider | - ExistingProvider | FactoryProvider | any[]; +export type Provider = TypeProvider|ValueProvider|ClassProvider|ConstructorProvider| + ExistingProvider|FactoryProvider|any[]; /** * Describes a function that is used to process provider lists (such as provider diff --git a/packages/core/src/di/jit/environment.ts b/packages/core/src/di/jit/environment.ts index d544fcc446443..a89bbf0e6901e 100644 --- a/packages/core/src/di/jit/environment.ts +++ b/packages/core/src/di/jit/environment.ts @@ -31,9 +31,9 @@ function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null { if (isForwardRef(type)) { return (() => { - const factory = getFactoryOf<T>(resolveForwardRef(typeAny)); - return factory ? factory() : null; - }) as any; + const factory = getFactoryOf<T>(resolveForwardRef(typeAny)); + return factory ? factory() : null; + }) as any; } const def = getInjectableDef<T>(typeAny) || getInjectorDef<T>(typeAny); diff --git a/packages/core/src/di/jit/injectable.ts b/packages/core/src/di/jit/injectable.ts index e8419d3a470c5..e21ab787e29cc 100644 --- a/packages/core/src/di/jit/injectable.ts +++ b/packages/core/src/di/jit/injectable.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {R3InjectableMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade'; +import {getCompilerFacade, R3InjectableMetadataFacade} from '../../compiler/compiler_facade'; import {Type} from '../../interface/type'; import {NG_FACTORY_DEF} from '../../render3/fields'; import {getClosureSafeProperty} from '../../util/property'; @@ -76,7 +76,7 @@ export function compileInjectable(type: Type<any>, srcMeta?: Injectable): void { } } -type UseClassProvider = Injectable & ClassSansProvider & {deps?: any[]}; +type UseClassProvider = Injectable&ClassSansProvider&{deps?: any[]}; const USE_VALUE = getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty}); diff --git a/packages/core/src/di/jit/util.ts b/packages/core/src/di/jit/util.ts index ae81dcb89bd59..ab0a61cd85180 100644 --- a/packages/core/src/di/jit/util.ts +++ b/packages/core/src/di/jit/util.ts @@ -7,7 +7,7 @@ */ import {ChangeDetectorRef} from '../../change_detection/change_detector_ref'; -import {CompilerFacade, R3DependencyMetadataFacade, R3ResolvedDependencyType, getCompilerFacade} from '../../compiler/compiler_facade'; +import {CompilerFacade, getCompilerFacade, R3DependencyMetadataFacade, R3ResolvedDependencyType} from '../../compiler/compiler_facade'; import {Type} from '../../interface/type'; import {ReflectionCapabilities} from '../../reflection/reflection_capabilities'; import {Attribute, Host, Inject, Optional, Self, SkipSelf} from '../metadata'; @@ -27,7 +27,7 @@ export function convertDependencies(deps: any[]): R3DependencyMetadataFacade[] { return deps.map(dep => reflectDependency(compiler, dep)); } -function reflectDependency(compiler: CompilerFacade, dep: any | any[]): R3DependencyMetadataFacade { +function reflectDependency(compiler: CompilerFacade, dep: any|any[]): R3DependencyMetadataFacade { const meta: R3DependencyMetadataFacade = { token: null, host: false, diff --git a/packages/core/src/di/metadata.ts b/packages/core/src/di/metadata.ts index c572f235342a6..b29c5b4ac0653 100644 --- a/packages/core/src/di/metadata.ts +++ b/packages/core/src/di/metadata.ts @@ -33,7 +33,7 @@ export interface InjectDecorator { * </code-example> */ (token: any): any; - new (token: any): Inject; + new(token: any): Inject; } /** @@ -82,7 +82,7 @@ export interface OptionalDecorator { * */ (): any; - new (): Optional; + new(): Optional; } /** @@ -128,7 +128,7 @@ export interface SelfDecorator { * */ (): any; - new (): Self; + new(): Self; } /** @@ -175,7 +175,7 @@ export interface SkipSelfDecorator { * */ (): any; - new (): SkipSelf; + new(): SkipSelf; } /** @@ -215,7 +215,7 @@ export interface HostDecorator { * </code-example> */ (): any; - new (): Host; + new(): Host; } /** @@ -262,7 +262,7 @@ export interface AttributeDecorator { * */ (name: string): any; - new (name: string): Attribute; + new(name: string): Attribute; } /** diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index 10fc23852cc5b..bbe401037c4a4 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -15,11 +15,12 @@ import {throwCyclicDependencyError, throwInvalidProviderError, throwMixedMultiPr import {FactoryFn} from '../render3/interfaces/definition'; import {deepForEach, newArray} from '../util/array_utils'; import {stringify} from '../util/stringify'; + import {resolveForwardRef} from './forward_ref'; import {InjectionToken} from './injection_token'; import {Injector} from './injector'; -import {INJECTOR, NG_TEMP_TOKEN_PATH, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, catchInjectorError, injectArgs, setCurrentInjector, ɵɵinject} from './injector_compatibility'; -import {InjectorType, InjectorTypeWithProviders, getInheritedInjectableDef, getInjectableDef, getInjectorDef, ɵɵInjectableDef} from './interface/defs'; +import {catchInjectorError, injectArgs, INJECTOR, NG_TEMP_TOKEN_PATH, NullInjector, setCurrentInjector, THROW_IF_NOT_FOUND, USE_VALUE, ɵɵinject} from './injector_compatibility'; +import {getInheritedInjectableDef, getInjectableDef, getInjectorDef, InjectorType, InjectorTypeWithProviders, ɵɵInjectableDef} from './interface/defs'; import {InjectFlags} from './interface/injector'; import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './interface/provider'; import {INJECTOR_SCOPE} from './scope'; @@ -29,8 +30,8 @@ import {INJECTOR_SCOPE} from './scope'; /** * Internal type for a single provider in a deep provider array. */ -type SingleProvider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider | - ExistingProvider | FactoryProvider | StaticClassProvider; +type SingleProvider = TypeProvider|ValueProvider|ClassProvider|ConstructorProvider|ExistingProvider| + FactoryProvider|StaticClassProvider; /** * Marker which indicates that a value has not yet been created from the factory function. @@ -76,8 +77,8 @@ interface Record<T> { * @publicApi */ export function createInjector( - defType: /* InjectorType<any> */ any, parent: Injector | null = null, - additionalProviders: StaticProvider[] | null = null, name?: string): Injector { + defType: /* InjectorType<any> */ any, parent: Injector|null = null, + additionalProviders: StaticProvider[]|null = null, name?: string): Injector { const injector = createInjectorWithoutInjectorInstances(defType, parent, additionalProviders, name); injector._resolveInjectorDefTypes(); @@ -90,8 +91,8 @@ export function createInjector( * should be resolved at a later point by calling `_resolveInjectorDefTypes`. */ export function createInjectorWithoutInjectorInstances( - defType: /* InjectorType<any> */ any, parent: Injector | null = null, - additionalProviders: StaticProvider[] | null = null, name?: string): R3Injector { + defType: /* InjectorType<any> */ any, parent: Injector|null = null, + additionalProviders: StaticProvider[]|null = null, name?: string): R3Injector { return new R3Injector(defType, additionalProviders, parent || getNullInjector(), name); } @@ -124,7 +125,9 @@ export class R3Injector { /** * Flag indicating that this injector was previously destroyed. */ - get destroyed(): boolean { return this._destroyed; } + get destroyed(): boolean { + return this._destroyed; + } private _destroyed = false; constructor( @@ -135,9 +138,10 @@ export class R3Injector { // Start off by creating Records for every provider declared in every InjectorType // included transitively in additional providers then do the same for `def`. This order is // important because `def` may include providers that override ones in additionalProviders. - additionalProviders && deepForEach( - additionalProviders, provider => this.processProvider( - provider, def, additionalProviders)); + additionalProviders && + deepForEach( + additionalProviders, + provider => this.processProvider(provider, def, additionalProviders)); deepForEach([def], injectorDef => this.processInjectorType(injectorDef, [], dedupStack)); @@ -235,7 +239,9 @@ export class R3Injector { } /** @internal */ - _resolveInjectorDefTypes() { this.injectorDefTypes.forEach(defType => this.get(defType)); } + _resolveInjectorDefTypes() { + this.injectorDefTypes.forEach(defType => this.get(defType)); + } toString() { const tokens = <string[]>[], records = this.records; @@ -285,8 +291,8 @@ export class R3Injector { // Check for circular dependencies. if (ngDevMode && parents.indexOf(defType) !== -1) { const defName = stringify(defType); - throw new Error( - `Circular dependency in DI detected for type ${defName}. Dependency path: ${parents.map(defType => stringify(defType)).join(' > ')} > ${defName}.`); + throw new Error(`Circular dependency in DI detected for type ${defName}. Dependency path: ${ + parents.map(defType => stringify(defType)).join(' > ')} > ${defName}.`); } // Check for multiple imports of the same module @@ -335,7 +341,7 @@ export class R3Injector { for (let i = 0; i < importTypesWithProviders.length; i++) { const {ngModule, providers} = importTypesWithProviders[i]; deepForEach( - providers !, + providers!, provider => this.processProvider(provider, ngModule, providers || EMPTY_ARRAY)); } } @@ -383,11 +389,11 @@ export class R3Injector { } } else { multiRecord = makeRecord(undefined, NOT_YET, true); - multiRecord.factory = () => injectArgs(multiRecord !.multi !); + multiRecord.factory = () => injectArgs(multiRecord!.multi!); this.records.set(token, multiRecord); } token = provider; - multiRecord.multi !.push(provider); + multiRecord.multi!.push(provider); } else { const existing = this.records.get(token); if (existing && existing.multi !== undefined) { @@ -402,7 +408,7 @@ export class R3Injector { throwCyclicDependencyError(stringify(token)); } else if (record.value === NOT_YET) { record.value = CIRCULAR; - record.value = record.factory !(); + record.value = record.factory!(); } if (typeof record.value === 'object' && record.value && hasOnDestroy(record.value)) { this.onDestroy.add(record.value); @@ -421,7 +427,7 @@ export class R3Injector { } } -function injectableDefOrInjectorDefFactory(token: Type<any>| InjectionToken<any>): FactoryFn<any> { +function injectableDefOrInjectorDefFactory(token: Type<any>|InjectionToken<any>): FactoryFn<any> { // Most tokens will have an injectable def directly on them, which specifies a factory directly. const injectableDef = getInjectableDef(token); const factory = injectableDef !== null ? injectableDef.factory : getFactoryDef(token); @@ -519,7 +525,7 @@ export function providerToFactory( } function makeRecord<T>( - factory: (() => T) | undefined, value: T | {}, multi: boolean = false): Record<T> { + factory: (() => T)|undefined, value: T|{}, multi: boolean = false): Record<T> { return { factory: factory, value: value, @@ -547,14 +553,14 @@ export function isClassProvider(value: SingleProvider): value is ClassProvider { return !!(value as StaticClassProvider | ClassProvider).useClass; } -function hasDeps(value: ClassProvider | ConstructorProvider | StaticClassProvider): - value is ClassProvider&{deps: any[]} { +function hasDeps(value: ClassProvider|ConstructorProvider| + StaticClassProvider): value is ClassProvider&{deps: any[]} { return !!(value as any).deps; } function hasOnDestroy(value: any): value is OnDestroy { return value !== null && typeof value === 'object' && - typeof(value as OnDestroy).ngOnDestroy === 'function'; + typeof (value as OnDestroy).ngOnDestroy === 'function'; } function couldBeInjectableType(value: any): value is Type<any>|InjectionToken<any> { diff --git a/packages/core/src/di/reflective_errors.ts b/packages/core/src/di/reflective_errors.ts index 584b863fedf0e..4a45534f46912 100644 --- a/packages/core/src/di/reflective_errors.ts +++ b/packages/core/src/di/reflective_errors.ts @@ -143,7 +143,8 @@ export function instantiationError( key: ReflectiveKey): InjectionError { return injectionError(injector, key, function(keys: ReflectiveKey[]) { const first = stringify(keys[0].token); - return `${originalException.message}: Error during instantiation of ${first}!${constructResolvingPath(keys)}.`; + return `${originalException.message}: Error during instantiation of ${first}!${ + constructResolvingPath(keys)}.`; }, originalException); } @@ -193,7 +194,7 @@ export function invalidProviderError(provider: any) { * ``` * */ -export function noAnnotationError(typeOrFunc: Type<any>| Function, params: any[][]): Error { +export function noAnnotationError(typeOrFunc: Type<any>|Function, params: any[][]): Error { const signature: string[] = []; for (let i = 0, ii = params.length; i < ii; i++) { const parameter = params[i]; diff --git a/packages/core/src/di/reflective_injector.ts b/packages/core/src/di/reflective_injector.ts index 6b979b8f4df41..c6c5d30a78cd4 100644 --- a/packages/core/src/di/reflective_injector.ts +++ b/packages/core/src/di/reflective_injector.ts @@ -308,7 +308,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector { createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector { const inj = new ReflectiveInjector_(providers); - (inj as{parent: Injector | null}).parent = this; + (inj as {parent: Injector | null}).parent = this; return inj; } @@ -335,7 +335,9 @@ export class ReflectiveInjector_ implements ReflectiveInjector { return this._instantiateProvider(provider); } - private _getMaxNumberOfObjects(): number { return this.objs.length; } + private _getMaxNumberOfObjects(): number { + return this.objs.length; + } private _instantiateProvider(provider: ResolvedReflectiveProvider): any { if (provider.multiProvider) { @@ -451,7 +453,9 @@ export class ReflectiveInjector_ implements ReflectiveInjector { return `ReflectiveInjector(providers: [${providers}])`; } - toString(): string { return this.displayName; } + toString(): string { + return this.displayName; + } } function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] { diff --git a/packages/core/src/di/reflective_key.ts b/packages/core/src/di/reflective_key.ts index f7219f60f57d0..6de91cad9bcb2 100644 --- a/packages/core/src/di/reflective_key.ts +++ b/packages/core/src/di/reflective_key.ts @@ -50,7 +50,9 @@ export class ReflectiveKey { /** * @returns the number of keys registered in the system. */ - static get numberOfKeys(): number { return _globalKeyRegistry.numberOfKeys; } + static get numberOfKeys(): number { + return _globalKeyRegistry.numberOfKeys; + } } export class KeyRegistry { @@ -60,7 +62,7 @@ export class KeyRegistry { if (token instanceof ReflectiveKey) return token; if (this._allKeys.has(token)) { - return this._allKeys.get(token) !; + return this._allKeys.get(token)!; } const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys); @@ -68,7 +70,9 @@ export class KeyRegistry { return newKey; } - get numberOfKeys(): number { return this._allKeys.size; } + get numberOfKeys(): number { + return this._allKeys.size; + } } const _globalKeyRegistry = new KeyRegistry(); diff --git a/packages/core/src/di/reflective_provider.ts b/packages/core/src/di/reflective_provider.ts index 11ff4445552b9..c794bfb878d27 100644 --- a/packages/core/src/di/reflective_provider.ts +++ b/packages/core/src/di/reflective_provider.ts @@ -18,7 +18,7 @@ import {ReflectiveKey} from './reflective_key'; interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider, ExistingProvider, - FactoryProvider {} + FactoryProvider {} /** * `Dependency` is used by the framework to extend DI. @@ -184,7 +184,7 @@ function _normalizeProviders( providers: Provider[], res: NormalizedProvider[]): NormalizedProvider[] { providers.forEach(b => { if (b instanceof Type) { - res.push({ provide: b, useClass: b } as NormalizedProvider); + res.push({provide: b, useClass: b} as NormalizedProvider); } else if (b && typeof b == 'object' && (b as any).provide !== undefined) { res.push(b as NormalizedProvider); @@ -221,7 +221,7 @@ function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] { } function _extractToken( - typeOrFunc: any, metadata: any[] | any, params: any[][]): ReflectiveDependency { + typeOrFunc: any, metadata: any[]|any, params: any[][]): ReflectiveDependency { let token: any = null; let optional = false; @@ -264,6 +264,6 @@ function _extractToken( } function _createDependency( - token: any, optional: boolean, visibility: Self | SkipSelf | null): ReflectiveDependency { + token: any, optional: boolean, visibility: Self|SkipSelf|null): ReflectiveDependency { return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility); } diff --git a/packages/core/src/di/util.ts b/packages/core/src/di/util.ts index 32b5bc595365a..dd5bf2c0aed8e 100644 --- a/packages/core/src/di/util.ts +++ b/packages/core/src/di/util.ts @@ -19,8 +19,9 @@ const USE_VALUE = const EMPTY_ARRAY: any[] = []; export function convertInjectableProviderToFactory( - type: Type<any>, provider?: ValueSansProvider | ExistingSansProvider | StaticClassSansProvider | - ConstructorSansProvider | FactorySansProvider | ClassSansProvider): () => any { + type: Type<any>, + provider?: ValueSansProvider|ExistingSansProvider|StaticClassSansProvider| + ConstructorSansProvider|FactorySansProvider|ClassSansProvider): () => any { if (!provider) { const reflectionCapabilities = new ReflectionCapabilities(); const deps = reflectionCapabilities.parameters(type); @@ -51,6 +52,6 @@ export function convertInjectableProviderToFactory( const reflectionCapabilities = new ReflectionCapabilities(); deps = reflectionCapabilities.parameters(type); } - return () => new type(...injectArgs(deps !)); + return () => new type(...injectArgs(deps!)); } } diff --git a/packages/core/src/event_emitter.ts b/packages/core/src/event_emitter.ts index 07143d9774a9e..02397f378d62e 100644 --- a/packages/core/src/event_emitter.ts +++ b/packages/core/src/event_emitter.ts @@ -83,7 +83,9 @@ export class EventEmitter<T extends any> extends Subject<T> { * Emits an event containing a given value. * @param value The value to emit. */ - emit(value?: T) { super.next(value); } + emit(value?: T) { + super.next(value); + } /** * Registers handlers for events emitted by this instance. @@ -101,29 +103,46 @@ export class EventEmitter<T extends any> extends Subject<T> { if (generatorOrNext && typeof generatorOrNext === 'object') { schedulerFn = this.__isAsync ? (value: any) => { setTimeout(() => generatorOrNext.next(value)); - } : (value: any) => { generatorOrNext.next(value); }; + } : (value: any) => { + generatorOrNext.next(value); + }; if (generatorOrNext.error) { - errorFn = this.__isAsync ? (err) => { setTimeout(() => generatorOrNext.error(err)); } : - (err) => { generatorOrNext.error(err); }; + errorFn = this.__isAsync ? (err) => { + setTimeout(() => generatorOrNext.error(err)); + } : (err) => { + generatorOrNext.error(err); + }; } if (generatorOrNext.complete) { - completeFn = this.__isAsync ? () => { setTimeout(() => generatorOrNext.complete()); } : - () => { generatorOrNext.complete(); }; + completeFn = this.__isAsync ? () => { + setTimeout(() => generatorOrNext.complete()); + } : () => { + generatorOrNext.complete(); + }; } } else { - schedulerFn = this.__isAsync ? (value: any) => { setTimeout(() => generatorOrNext(value)); } : - (value: any) => { generatorOrNext(value); }; + schedulerFn = this.__isAsync ? (value: any) => { + setTimeout(() => generatorOrNext(value)); + } : (value: any) => { + generatorOrNext(value); + }; if (error) { - errorFn = - this.__isAsync ? (err) => { setTimeout(() => error(err)); } : (err) => { error(err); }; + errorFn = this.__isAsync ? (err) => { + setTimeout(() => error(err)); + } : (err) => { + error(err); + }; } if (complete) { - completeFn = - this.__isAsync ? () => { setTimeout(() => complete()); } : () => { complete(); }; + completeFn = this.__isAsync ? () => { + setTimeout(() => complete()); + } : () => { + complete(); + }; } } diff --git a/packages/core/src/i18n/locale_data_api.ts b/packages/core/src/i18n/locale_data_api.ts index 63816bba38eea..75c9082080054 100644 --- a/packages/core/src/i18n/locale_data_api.ts +++ b/packages/core/src/i18n/locale_data_api.ts @@ -5,9 +5,10 @@ * 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 localeEn from './locale_en'; import {global} from '../util/global'; +import localeEn from './locale_en'; + /** * This const is used to store the locale data registered with `registerLocaleData` */ @@ -19,7 +20,7 @@ let LOCALE_DATA: {[localeId: string]: any} = {}; * * The signature `registerLocaleData(data: any, extraData?: any)` is deprecated since v5.1 */ -export function registerLocaleData(data: any, localeId?: string | any, extraData?: any): void { +export function registerLocaleData(data: any, localeId?: string|any, extraData?: any): void { if (typeof localeId !== 'string') { extraData = localeId; localeId = data[LocaleDataIndex.LocaleId]; @@ -151,7 +152,11 @@ export const enum ExtraLocaleDataIndex { /** * Index of each value in currency data (used to describe CURRENCIES_EN in currencies.ts) */ -export const enum CurrencyIndex {Symbol = 0, SymbolNarrow, NbOfDigits} +export const enum CurrencyIndex { + Symbol = 0, + SymbolNarrow, + NbOfDigits +} /** * Returns the canonical form of a locale name - lowercase with `_` replaced with `-`. diff --git a/packages/core/src/interface/lifecycle_hooks.ts b/packages/core/src/interface/lifecycle_hooks.ts index 0beb6d97086eb..45414583a9397 100644 --- a/packages/core/src/interface/lifecycle_hooks.ts +++ b/packages/core/src/interface/lifecycle_hooks.ts @@ -90,12 +90,12 @@ export interface OnInit { */ export interface DoCheck { /** - * A callback method that performs change-detection, invoked - * after the default change-detector runs. - * See `KeyValueDiffers` and `IterableDiffers` for implementing - * custom change checking for collections. - * - */ + * A callback method that performs change-detection, invoked + * after the default change-detector runs. + * See `KeyValueDiffers` and `IterableDiffers` for implementing + * custom change checking for collections. + * + */ ngDoCheck(): void; } diff --git a/packages/core/src/interface/simple_change.ts b/packages/core/src/interface/simple_change.ts index 6152cbd7ad506..081967868e54f 100644 --- a/packages/core/src/interface/simple_change.ts +++ b/packages/core/src/interface/simple_change.ts @@ -20,7 +20,9 @@ export class SimpleChange { /** * Check whether the new value is the first value assigned. */ - isFirstChange(): boolean { return this.firstChange; } + isFirstChange(): boolean { + return this.firstChange; + } } /** @@ -32,4 +34,6 @@ export class SimpleChange { * * @publicApi */ -export interface SimpleChanges { [propName: string]: SimpleChange; } +export interface SimpleChanges { + [propName: string]: SimpleChange; +} diff --git a/packages/core/src/interface/type.ts b/packages/core/src/interface/type.ts index e8b0188042593..a8c3f6a51be09 100644 --- a/packages/core/src/interface/type.ts +++ b/packages/core/src/interface/type.ts @@ -30,11 +30,15 @@ export function isType(v: any): v is Type<any> { * * @publicApi */ -export interface AbstractType<T> extends Function { prototype: T; } +export interface AbstractType<T> extends Function { + prototype: T; +} -export interface Type<T> extends Function { new (...args: any[]): T; } +export interface Type<T> extends Function { + new(...args: any[]): T; +} -export type Mutable<T extends{[x: string]: any}, K extends string> = { +export type Mutable<T extends {[x: string]: any}, K extends string> = { [P in K]: T[P]; }; diff --git a/packages/core/src/linker.ts b/packages/core/src/linker.ts index b3b25b222ece8..4e34360e945f3 100644 --- a/packages/core/src/linker.ts +++ b/packages/core/src/linker.ts @@ -7,12 +7,12 @@ */ // Public API for compiler -export {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, ModuleWithComponentFactories} from './linker/compiler'; +export {Compiler, COMPILER_OPTIONS, CompilerFactory, CompilerOptions, ModuleWithComponentFactories} from './linker/compiler'; export {ComponentFactory, ComponentRef} from './linker/component_factory'; export {ComponentFactoryResolver} from './linker/component_factory_resolver'; export {ElementRef} from './linker/element_ref'; export {NgModuleFactory, NgModuleRef} from './linker/ng_module_factory'; -export {NgModuleFactoryLoader, getModuleFactory} from './linker/ng_module_factory_loader'; +export {getModuleFactory, NgModuleFactoryLoader} from './linker/ng_module_factory_loader'; export {QueryList} from './linker/query_list'; export {SystemJsNgModuleLoader, SystemJsNgModuleLoaderConfig} from './linker/system_js_ng_module_factory_loader'; export {TemplateRef} from './linker/template_ref'; diff --git a/packages/core/src/linker/compiler.ts b/packages/core/src/linker/compiler.ts index c6ad53f774d96..8d063b5e44834 100644 --- a/packages/core/src/linker/compiler.ts +++ b/packages/core/src/linker/compiler.ts @@ -60,7 +60,7 @@ export const Compiler_compileModuleAndAllComponentsSync__POST_R3__: <T>(moduleTy ModuleWithComponentFactories<T> = function<T>(moduleType: Type<T>): ModuleWithComponentFactories<T> { const ngModuleFactory = Compiler_compileModuleSync__POST_R3__(moduleType); - const moduleDef = getNgModuleDef(moduleType) !; + const moduleDef = getNgModuleDef(moduleType)!; const componentFactories = maybeUnwrapFn(moduleDef.declarations) .reduce((factories: ComponentFactory<any>[], declaration: Type<any>) => { @@ -133,7 +133,9 @@ export class Compiler { /** * Returns the id for a given NgModule, if one is defined and known to the compiler. */ - getModuleId(moduleType: Type<any>): string|undefined { return undefined; } + getModuleId(moduleType: Type<any>): string|undefined { + return undefined; + } } /** diff --git a/packages/core/src/linker/component_factory_resolver.ts b/packages/core/src/linker/component_factory_resolver.ts index 90cdd073cf960..6d84307ddd9fb 100644 --- a/packages/core/src/linker/component_factory_resolver.ts +++ b/packages/core/src/linker/component_factory_resolver.ts @@ -14,8 +14,8 @@ import {ComponentFactory, ComponentRef} from './component_factory'; import {NgModuleRef} from './ng_module_factory'; export function noComponentFactoryError(component: Function) { - const error = Error( - `No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`); + const error = Error(`No component factory found for ${ + stringify(component)}. Did you add it to @NgModule.entryComponents?`); (error as any)[ERROR_COMPONENT] = component; return error; } @@ -28,7 +28,7 @@ export function getComponent(error: Error): Type<any> { class _NullComponentFactoryResolver implements ComponentFactoryResolver { - resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> { + resolveComponentFactory<T>(component: {new(...args: any[]): T}): ComponentFactory<T> { throw noComponentFactoryError(component); } } @@ -63,7 +63,7 @@ export class CodegenComponentFactoryResolver implements ComponentFactoryResolver } } - resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> { + resolveComponentFactory<T>(component: {new(...args: any[]): T}): ComponentFactory<T> { let factory = this._factories.get(component); if (!factory && this._parent) { factory = this._parent.resolveComponentFactory(component); diff --git a/packages/core/src/linker/element_ref.ts b/packages/core/src/linker/element_ref.ts index 81eb088558b18..c357e307a33d3 100644 --- a/packages/core/src/linker/element_ref.ts +++ b/packages/core/src/linker/element_ref.ts @@ -48,7 +48,9 @@ export class ElementRef<T extends any = any> { */ public nativeElement: T; - constructor(nativeElement: T) { this.nativeElement = nativeElement; } + constructor(nativeElement: T) { + this.nativeElement = nativeElement; + } /** * @internal diff --git a/packages/core/src/linker/ng_module_factory_loader.ts b/packages/core/src/linker/ng_module_factory_loader.ts index 2f6f710c6e08b..717a3ab8821d5 100644 --- a/packages/core/src/linker/ng_module_factory_loader.ts +++ b/packages/core/src/linker/ng_module_factory_loader.ts @@ -43,6 +43,8 @@ export function getModuleFactory__POST_R3__(id: string): NgModuleFactory<any> { */ export const getModuleFactory: (id: string) => NgModuleFactory<any> = getModuleFactory__PRE_R3__; -function noModuleError(id: string, ): Error { +function noModuleError( + id: string, + ): Error { return new Error(`No module with ID ${id} loaded`); } diff --git a/packages/core/src/linker/ng_module_factory_registration.ts b/packages/core/src/linker/ng_module_factory_registration.ts index 3008d8434a825..639a772fb6e91 100644 --- a/packages/core/src/linker/ng_module_factory_registration.ts +++ b/packages/core/src/linker/ng_module_factory_registration.ts @@ -32,7 +32,7 @@ export function registerModuleFactory(id: string, factory: NgModuleFactory<any>) modules.set(id, factory); } -function assertSameOrNotExisting(id: string, type: Type<any>| null, incoming: Type<any>): void { +function assertSameOrNotExisting(id: string, type: Type<any>|null, incoming: Type<any>): void { if (type && type !== incoming) { throw new Error( `Duplicate module registered for ${id} - ${stringify(type)} vs ${stringify(type.name)}`); diff --git a/packages/core/src/linker/query_list.ts b/packages/core/src/linker/query_list.ts index f89d35cd42200..855ad74c3e9c9 100644 --- a/packages/core/src/linker/query_list.ts +++ b/packages/core/src/linker/query_list.ts @@ -13,7 +13,7 @@ import {flatten} from '../util/array_utils'; import {getSymbolIterator} from '../util/symbol'; function symbolIterator<T>(this: QueryList<T>): Iterator<T> { - return ((this as any as{_results: Array<T>})._results as any)[getSymbolIterator()](); + return ((this as any as {_results: Array<T>})._results as any)[getSymbolIterator()](); } /** @@ -49,9 +49,9 @@ export class QueryList<T> implements Iterable<T> { readonly length: number = 0; // TODO(issue/24571): remove '!'. - readonly first !: T; + readonly first!: T; // TODO(issue/24571): remove '!'. - readonly last !: T; + readonly last!: T; constructor() { // This function should be declared on the prototype, but doing so there will cause the class @@ -67,7 +67,9 @@ export class QueryList<T> implements Iterable<T> { * See * [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) */ - map<U>(fn: (item: T, index: number, array: T[]) => U): U[] { return this._results.map(fn); } + map<U>(fn: (item: T, index: number, array: T[]) => U): U[] { + return this._results.map(fn); + } /** * See @@ -97,7 +99,9 @@ export class QueryList<T> implements Iterable<T> { * See * [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) */ - forEach(fn: (item: T, index: number, array: T[]) => void): void { this._results.forEach(fn); } + forEach(fn: (item: T, index: number, array: T[]) => void): void { + this._results.forEach(fn); + } /** * See @@ -110,9 +114,13 @@ export class QueryList<T> implements Iterable<T> { /** * Returns a copy of the internal results list as an Array. */ - toArray(): T[] { return this._results.slice(); } + toArray(): T[] { + return this._results.slice(); + } - toString(): string { return this._results.toString(); } + toString(): string { + return this._results.toString(); + } /** * Updates the stored data of the query list, and resets the `dirty` flag to `false`, so that @@ -123,19 +131,23 @@ export class QueryList<T> implements Iterable<T> { */ reset(resultsTree: Array<T|any[]>): void { this._results = flatten(resultsTree); - (this as{dirty: boolean}).dirty = false; - (this as{length: number}).length = this._results.length; - (this as{last: T}).last = this._results[this.length - 1]; - (this as{first: T}).first = this._results[0]; + (this as {dirty: boolean}).dirty = false; + (this as {length: number}).length = this._results.length; + (this as {last: T}).last = this._results[this.length - 1]; + (this as {first: T}).first = this._results[0]; } /** * Triggers a change event by emitting on the `changes` {@link EventEmitter}. */ - notifyOnChanges(): void { (this.changes as EventEmitter<any>).emit(this); } + notifyOnChanges(): void { + (this.changes as EventEmitter<any>).emit(this); + } /** internal */ - setDirty() { (this as{dirty: boolean}).dirty = true; } + setDirty() { + (this as {dirty: boolean}).dirty = true; + } /** internal */ destroy(): void { @@ -148,5 +160,5 @@ export class QueryList<T> implements Iterable<T> { // there) and this declaration is left here to ensure that TypeScript considers QueryList to // implement the Iterable interface. This is required for template type-checking of NgFor loops // over QueryLists to work correctly, since QueryList must be assignable to NgIterable. - [Symbol.iterator] !: () => Iterator<T>; + [Symbol.iterator]!: () => Iterator<T>; } diff --git a/packages/core/src/linker/system_js_ng_module_factory_loader.ts b/packages/core/src/linker/system_js_ng_module_factory_loader.ts index 6221d6733377c..9b0075807c588 100644 --- a/packages/core/src/linker/system_js_ng_module_factory_loader.ts +++ b/packages/core/src/linker/system_js_ng_module_factory_loader.ts @@ -32,13 +32,13 @@ export abstract class SystemJsNgModuleLoaderConfig { * Prefix to add when computing the name of the factory module for a given module name. */ // TODO(issue/24571): remove '!'. - factoryPathPrefix !: string; + factoryPathPrefix!: string; /** * Suffix to add when computing the name of the factory module for a given module name. */ // TODO(issue/24571): remove '!'. - factoryPathSuffix !: string; + factoryPathSuffix!: string; } const DEFAULT_CONFIG: SystemJsNgModuleLoaderConfig = { diff --git a/packages/core/src/metadata/di.ts b/packages/core/src/metadata/di.ts index 3702f4e60b8af..fd7c10b799df1 100644 --- a/packages/core/src/metadata/di.ts +++ b/packages/core/src/metadata/di.ts @@ -75,7 +75,7 @@ export interface AttributeDecorator { * @publicApi */ (name: string): any; - new (name: string): Attribute; + new(name: string): Attribute; } @@ -158,7 +158,7 @@ export interface ContentChildrenDecorator { * @Annotation */ (selector: Type<any>|Function|string, opts?: {descendants?: boolean, read?: any}): any; - new (selector: Type<any>|Function|string, opts?: {descendants?: boolean, read?: any}): Query; + new(selector: Type<any>|Function|string, opts?: {descendants?: boolean, read?: any}): Query; } /** @@ -219,7 +219,7 @@ export interface ContentChildDecorator { * @Annotation */ (selector: Type<any>|Function|string, opts?: {read?: any, static?: boolean}): any; - new (selector: Type<any>|Function|string, opts?: {read?: any, static?: boolean}): ContentChild; + new(selector: Type<any>|Function|string, opts?: {read?: any, static?: boolean}): ContentChild; } /** @@ -238,8 +238,9 @@ export type ContentChild = Query; * @publicApi */ export const ContentChild: ContentChildDecorator = makePropDecorator( - 'ContentChild', (selector?: any, data: any = {}) => - ({selector, first: true, isViewQuery: false, descendants: true, ...data}), + 'ContentChild', + (selector?: any, data: any = {}) => + ({selector, first: true, isViewQuery: false, descendants: true, ...data}), Query); /** @@ -275,7 +276,7 @@ export interface ViewChildrenDecorator { * @Annotation */ (selector: Type<any>|Function|string, opts?: {read?: any}): any; - new (selector: Type<any>|Function|string, opts?: {read?: any}): ViewChildren; + new(selector: Type<any>|Function|string, opts?: {read?: any}): ViewChildren; } /** @@ -292,8 +293,9 @@ export type ViewChildren = Query; * @publicApi */ export const ViewChildren: ViewChildrenDecorator = makePropDecorator( - 'ViewChildren', (selector?: any, data: any = {}) => - ({selector, first: false, isViewQuery: true, descendants: true, ...data}), + 'ViewChildren', + (selector?: any, data: any = {}) => + ({selector, first: false, isViewQuery: true, descendants: true, ...data}), Query); /** @@ -342,7 +344,7 @@ export interface ViewChildDecorator { * @Annotation */ (selector: Type<any>|Function|string, opts?: {read?: any, static?: boolean}): any; - new (selector: Type<any>|Function|string, opts?: {read?: any, static?: boolean}): ViewChild; + new(selector: Type<any>|Function|string, opts?: {read?: any, static?: boolean}): ViewChild; } /** @@ -359,6 +361,7 @@ export type ViewChild = Query; * @publicApi */ export const ViewChild: ViewChildDecorator = makePropDecorator( - 'ViewChild', (selector: any, data: any) => - ({selector, first: true, isViewQuery: true, descendants: true, ...data}), + 'ViewChild', + (selector: any, data: any) => + ({selector, first: true, isViewQuery: true, descendants: true, ...data}), Query); diff --git a/packages/core/src/metadata/directives.ts b/packages/core/src/metadata/directives.ts index 08646edc27fbc..74cda20f57def 100644 --- a/packages/core/src/metadata/directives.ts +++ b/packages/core/src/metadata/directives.ts @@ -11,7 +11,7 @@ import {Provider} from '../di'; import {Type} from '../interface/type'; import {compileComponent as render3CompileComponent, compileDirective as render3CompileDirective} from '../render3/jit/directive'; import {compilePipe as render3CompilePipe} from '../render3/jit/pipe'; -import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators'; +import {makeDecorator, makePropDecorator, TypeDecorator} from '../util/decorators'; import {noop} from '../util/noop'; import {ViewEncapsulation} from './view'; @@ -73,7 +73,7 @@ export interface DirectiveDecorator { /** * See the `Directive` decorator. */ - new (obj?: Directive): Directive; + new(obj?: Directive): Directive; } /** @@ -445,7 +445,7 @@ export interface ComponentDecorator { /** * See the `Component` decorator. */ - new (obj: Component): Component; + new(obj: Component): Component; } /** @@ -598,7 +598,7 @@ export interface PipeDecorator { /** * See the `Pipe` decorator. */ - new (obj: Pipe): Pipe; + new(obj: Pipe): Pipe; } /** @@ -641,52 +641,52 @@ export const Pipe: PipeDecorator = makeDecorator( */ export interface InputDecorator { /** - * Decorator that marks a class field as an input property and supplies configuration metadata. - * The input property is bound to a DOM property in the template. During change detection, - * Angular automatically updates the data property with the DOM property's value. - * - * @usageNotes - * - * You can supply an optional name to use in templates when the - * component is instantiated, that maps to the - * name of the bound property. By default, the original - * name of the bound property is used for input binding. - * - * The following example creates a component with two input properties, - * one of which is given a special binding name. - * - * ```typescript - * @Component({ - * selector: 'bank-account', - * template: ` - * Bank Name: {{bankName}} - * Account Id: {{id}} - * ` - * }) - * class BankAccount { - * // This property is bound using its original name. - * @Input() bankName: string; - * // this property value is bound to a different property name - * // when this component is instantiated in a template. - * @Input('account-id') id: string; - * - * // this property is not bound, and is not automatically updated by Angular - * normalizedBankName: string; - * } - * - * @Component({ - * selector: 'app', - * template: ` - * <bank-account bankName="RBC" account-id="4747"></bank-account> - * ` - * }) - * class App {} - * ``` - * - * @see [Input and Output properties](guide/template-syntax#input-and-output-properties) - */ + * Decorator that marks a class field as an input property and supplies configuration metadata. + * The input property is bound to a DOM property in the template. During change detection, + * Angular automatically updates the data property with the DOM property's value. + * + * @usageNotes + * + * You can supply an optional name to use in templates when the + * component is instantiated, that maps to the + * name of the bound property. By default, the original + * name of the bound property is used for input binding. + * + * The following example creates a component with two input properties, + * one of which is given a special binding name. + * + * ```typescript + * @Component({ + * selector: 'bank-account', + * template: ` + * Bank Name: {{bankName}} + * Account Id: {{id}} + * ` + * }) + * class BankAccount { + * // This property is bound using its original name. + * @Input() bankName: string; + * // this property value is bound to a different property name + * // when this component is instantiated in a template. + * @Input('account-id') id: string; + * + * // this property is not bound, and is not automatically updated by Angular + * normalizedBankName: string; + * } + * + * @Component({ + * selector: 'app', + * template: ` + * <bank-account bankName="RBC" account-id="4747"></bank-account> + * ` + * }) + * class App {} + * ``` + * + * @see [Input and Output properties](guide/template-syntax#input-and-output-properties) + */ (bindingPropertyName?: string): any; - new (bindingPropertyName?: string): any; + new(bindingPropertyName?: string): any; } /** @@ -715,23 +715,23 @@ export const Input: InputDecorator = */ export interface OutputDecorator { /** - * Decorator that marks a class field as an output property and supplies configuration metadata. - * The DOM property bound to the output property is automatically updated during change detection. - * - * @usageNotes - * - * You can supply an optional name to use in templates when the - * component is instantiated, that maps to the - * name of the bound property. By default, the original - * name of the bound property is used for output binding. - * - * See `Input` decorator for an example of providing a binding name. - * - * @see [Input and Output properties](guide/template-syntax#input-and-output-properties) - * - */ + * Decorator that marks a class field as an output property and supplies configuration metadata. + * The DOM property bound to the output property is automatically updated during change detection. + * + * @usageNotes + * + * You can supply an optional name to use in templates when the + * component is instantiated, that maps to the + * name of the bound property. By default, the original + * name of the bound property is used for output binding. + * + * See `Input` decorator for an example of providing a binding name. + * + * @see [Input and Output properties](guide/template-syntax#input-and-output-properties) + * + */ (bindingPropertyName?: string): any; - new (bindingPropertyName?: string): any; + new(bindingPropertyName?: string): any; } /** @@ -741,8 +741,8 @@ export interface OutputDecorator { */ export interface Output { /** - * The name of the DOM property to which the output property is bound. - */ + * The name of the DOM property to which the output property is bound. + */ bindingPropertyName?: string; } @@ -791,7 +791,7 @@ export interface HostBindingDecorator { * */ (hostPropertyName?: string): any; - new (hostPropertyName?: string): any; + new(hostPropertyName?: string): any; } /** @@ -825,7 +825,7 @@ export interface HostListenerDecorator { * and provides a handler method to run when that event occurs. */ (eventName: string, args?: string[]): any; - new (eventName: string, args?: string[]): any; + new(eventName: string, args?: string[]): any; } /** diff --git a/packages/core/src/metadata/ng_module.ts b/packages/core/src/metadata/ng_module.ts index 9a8303228ae65..2d947c53fc3c7 100644 --- a/packages/core/src/metadata/ng_module.ts +++ b/packages/core/src/metadata/ng_module.ts @@ -13,7 +13,7 @@ import {convertInjectableProviderToFactory} from '../di/util'; import {Type} from '../interface/type'; import {SchemaMetadata} from '../metadata/schema'; import {compileNgModule as render3CompileNgModule} from '../render3/jit/module'; -import {TypeDecorator, makeDecorator} from '../util/decorators'; +import {makeDecorator, TypeDecorator} from '../util/decorators'; /** @@ -107,7 +107,7 @@ export interface NgModuleDecorator { * Decorator that marks a class as an NgModule and supplies configuration metadata. */ (obj?: NgModule): TypeDecorator; - new (obj?: NgModule): NgModule; + new(obj?: NgModule): NgModule; } /** @@ -345,7 +345,9 @@ export const NgModule: NgModuleDecorator = makeDecorator( * * @publicApi */ -export interface DoBootstrap { ngDoBootstrap(appRef: ApplicationRef): void; } +export interface DoBootstrap { + ngDoBootstrap(appRef: ApplicationRef): void; +} function preR3NgModuleCompile(moduleType: Type<any>, metadata?: NgModule): void { let imports = (metadata && metadata.imports) || []; diff --git a/packages/core/src/metadata/resource_loading.ts b/packages/core/src/metadata/resource_loading.ts index 0aed1c1cd381a..e1b8bc7eb76c3 100644 --- a/packages/core/src/metadata/resource_loading.ts +++ b/packages/core/src/metadata/resource_loading.ts @@ -122,7 +122,7 @@ export function isComponentResourceResolutionQueueEmpty() { return componentResourceResolutionQueue.size === 0; } -function unwrapResponse(response: string | {text(): Promise<string>}): string|Promise<string> { +function unwrapResponse(response: string|{text(): Promise<string>}): string|Promise<string> { return typeof response == 'string' ? response : response.text(); } diff --git a/packages/core/src/metadata/schema.ts b/packages/core/src/metadata/schema.ts index 54c3d56dbe56e..7fa3e3f1b0a73 100644 --- a/packages/core/src/metadata/schema.ts +++ b/packages/core/src/metadata/schema.ts @@ -16,7 +16,9 @@ * * @publicApi */ -export interface SchemaMetadata { name: string; } +export interface SchemaMetadata { + name: string; +} /** * Defines a schema that allows an NgModule to contain the following: diff --git a/packages/core/src/platform_core_providers.ts b/packages/core/src/platform_core_providers.ts index aeae1d103a414..2d8e7d38fc28b 100644 --- a/packages/core/src/platform_core_providers.ts +++ b/packages/core/src/platform_core_providers.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {PlatformRef, createPlatformFactory} from './application_ref'; +import {createPlatformFactory, PlatformRef} from './application_ref'; import {PLATFORM_ID} from './application_tokens'; import {Console} from './console'; import {Injector, StaticProvider} from './di'; diff --git a/packages/core/src/r3_symbols.ts b/packages/core/src/r3_symbols.ts index 66771f54db9a6..28833b367bf1b 100644 --- a/packages/core/src/r3_symbols.ts +++ b/packages/core/src/r3_symbols.ts @@ -22,7 +22,7 @@ */ export {ɵɵinject} from './di/injector_compatibility'; -export {ɵɵInjectableDef, ɵɵInjectorDef, ɵɵdefineInjectable, ɵɵdefineInjector} from './di/interface/defs'; +export {ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵInjectableDef, ɵɵInjectorDef} from './di/interface/defs'; export {NgModuleDef, ɵɵNgModuleDefWithMeta} from './metadata/ng_module'; export {ɵɵdefineNgModule} from './render3/definition'; export {ɵɵFactoryDef} from './render3/interfaces/definition'; diff --git a/packages/core/src/reflection/reflection_capabilities.ts b/packages/core/src/reflection/reflection_capabilities.ts index 0aac8ca00cedc..32a5112659c7f 100644 --- a/packages/core/src/reflection/reflection_capabilities.ts +++ b/packages/core/src/reflection/reflection_capabilities.ts @@ -6,11 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {Type, isType} from '../interface/type'; +import {isType, Type} from '../interface/type'; import {newArray} from '../util/array_utils'; import {ANNOTATIONS, PARAMETERS, PROP_METADATA} from '../util/decorators'; import {global} from '../util/global'; import {stringify} from '../util/stringify'; + import {PlatformReflectionCapabilities} from './platform_reflection_capabilities'; import {GetterFn, MethodFn, SetterFn} from './types'; @@ -42,11 +43,17 @@ export function isDelegateCtor(typeStr: string): boolean { export class ReflectionCapabilities implements PlatformReflectionCapabilities { private _reflect: any; - constructor(reflect?: any) { this._reflect = reflect || global['Reflect']; } + constructor(reflect?: any) { + this._reflect = reflect || global['Reflect']; + } - isReflectionEnabled(): boolean { return true; } + isReflectionEnabled(): boolean { + return true; + } - factory<T>(t: Type<T>): (args: any[]) => T { return (...args: any[]) => new t(...args); } + factory<T>(t: Type<T>): (args: any[]) => T { + return (...args: any[]) => new t(...args); + } /** @internal */ _zipTypesAndAnnotations(paramTypes: any[], paramAnnotations: any[]): any[][] { @@ -235,9 +242,13 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { return type instanceof Type && lcProperty in type.prototype; } - guards(type: any): {[key: string]: any} { return {}; } + guards(type: any): {[key: string]: any} { + return {}; + } - getter(name: string): GetterFn { return <GetterFn>new Function('o', 'return o.' + name + ';'); } + getter(name: string): GetterFn { + return <GetterFn>new Function('o', 'return o.' + name + ';'); + } setter(name: string): SetterFn { return <SetterFn>new Function('o', 'v', 'return o.' + name + ' = v;'); @@ -259,12 +270,16 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { return `./${stringify(type)}`; } - resourceUri(type: any): string { return `./${stringify(type)}`; } + resourceUri(type: any): string { + return `./${stringify(type)}`; + } resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any { return runtime; } - resolveEnum(enumIdentifier: any, name: string): any { return enumIdentifier[name]; } + resolveEnum(enumIdentifier: any, name: string): any { + return enumIdentifier[name]; + } } function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] { diff --git a/packages/core/src/reflection/reflector.ts b/packages/core/src/reflection/reflector.ts index 6d7b8634f03d0..c8ec7710dfe56 100644 --- a/packages/core/src/reflection/reflector.ts +++ b/packages/core/src/reflection/reflector.ts @@ -20,9 +20,13 @@ export {GetterFn, MethodFn, SetterFn}; export class Reflector { constructor(public reflectionCapabilities: PlatformReflectionCapabilities) {} - updateCapabilities(caps: PlatformReflectionCapabilities) { this.reflectionCapabilities = caps; } + updateCapabilities(caps: PlatformReflectionCapabilities) { + this.reflectionCapabilities = caps; + } - factory(type: Type<any>): Function { return this.reflectionCapabilities.factory(type); } + factory(type: Type<any>): Function { + return this.reflectionCapabilities.factory(type); + } parameters(typeOrFunc: Type<any>): any[][] { return this.reflectionCapabilities.parameters(typeOrFunc); @@ -40,15 +44,25 @@ export class Reflector { return this.reflectionCapabilities.hasLifecycleHook(type, lcProperty); } - getter(name: string): GetterFn { return this.reflectionCapabilities.getter(name); } + getter(name: string): GetterFn { + return this.reflectionCapabilities.getter(name); + } - setter(name: string): SetterFn { return this.reflectionCapabilities.setter(name); } + setter(name: string): SetterFn { + return this.reflectionCapabilities.setter(name); + } - method(name: string): MethodFn { return this.reflectionCapabilities.method(name); } + method(name: string): MethodFn { + return this.reflectionCapabilities.method(name); + } - importUri(type: any): string { return this.reflectionCapabilities.importUri(type); } + importUri(type: any): string { + return this.reflectionCapabilities.importUri(type); + } - resourceUri(type: any): string { return this.reflectionCapabilities.resourceUri(type); } + resourceUri(type: any): string { + return this.reflectionCapabilities.resourceUri(type); + } resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any { return this.reflectionCapabilities.resolveIdentifier(name, moduleUrl, members, runtime); diff --git a/packages/core/src/render/api.ts b/packages/core/src/render/api.ts index f03f26eaf3442..36fcc6df541da 100644 --- a/packages/core/src/render/api.ts +++ b/packages/core/src/render/api.ts @@ -145,7 +145,7 @@ export abstract class Renderer2 { * This is used as a performance optimization for production mode. */ // TODO(issue/24571): remove '!'. - destroyNode !: ((node: any) => void) | null; + destroyNode!: ((node: any) => void)|null; /** * Appends a child to a given parent node in the host element DOM. * @param parent The parent node. diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index 186b0fde7766a..56c74663d5946 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -20,9 +20,10 @@ import {LView, TVIEW, TView} from './interfaces/view'; export function assertTNodeForLView(tNode: TNode, lView: LView) { - tNode.hasOwnProperty('tView_') && assertEqual( - (tNode as any as{tView_: TView}).tView_, lView[TVIEW], - 'This TNode does not belong to this LView.'); + tNode.hasOwnProperty('tView_') && + assertEqual( + (tNode as any as {tView_: TView}).tView_, lView[TVIEW], + 'This TNode does not belong to this LView.'); } export function assertComponentType( @@ -45,9 +46,9 @@ export function assertPreviousIsParent(isParent: boolean) { assertEqual(isParent, true, 'previousOrParentTNode should be a parent'); } -export function assertHasParent(tNode: TNode | null) { +export function assertHasParent(tNode: TNode|null) { assertDefined(tNode, 'previousOrParentTNode should exist!'); - assertDefined(tNode !.parent, 'previousOrParentTNode should have a parent'); + assertDefined(tNode!.parent, 'previousOrParentTNode should have a parent'); } export function assertDataNext(lView: LView, index: number, arr?: any[]) { diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 892e49bd38108..c923209584084 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -12,15 +12,16 @@ import {Type} from '../core'; import {Injector} from '../di/injector'; import {Sanitizer} from '../sanitization/sanitizer'; import {assertDataInRange} from '../util/assert'; + import {assertComponentType} from './assert'; import {getComponentDef} from './definition'; import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di'; import {registerPostOrderHooks} from './hooks'; -import {CLEAN_PROMISE, addHostBindingsToExpandoInstructions, addToViewTree, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, growHostVarsSpace, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderView} from './instructions/shared'; +import {addHostBindingsToExpandoInstructions, addToViewTree, CLEAN_PROMISE, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, growHostVarsSpace, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderView} from './instructions/shared'; import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition'; import {TElementNode, TNode, TNodeType} from './interfaces/node'; import {PlayerHandler} from './interfaces/player'; -import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; +import {domRendererFactory3, RElement, Renderer3, RendererFactory3} from './interfaces/renderer'; import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW, TViewType} from './interfaces/view'; import {writeDirectClass, writeDirectStyle} from './node_manipulation'; import {enterView, getPreviousOrParentTNode, leaveView, setSelectedIndex} from './state'; @@ -107,7 +108,7 @@ export const NULL_INJECTOR: Injector = { */ export function renderComponent<T>( componentType: ComponentType<T>| - Type<T>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */ + Type<T>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */ , opts: CreateComponentOptions = {}): T { ngDevMode && publishDefaultGlobalUtils(); @@ -115,11 +116,11 @@ export function renderComponent<T>( const rendererFactory = opts.rendererFactory || domRendererFactory3; const sanitizer = opts.sanitizer || null; - const componentDef = getComponentDef<T>(componentType) !; - if (componentDef.type != componentType) (componentDef as{type: Type<any>}).type = componentType; + const componentDef = getComponentDef<T>(componentType)!; + if (componentDef.type != componentType) (componentDef as {type: Type<any>}).type = componentType; // The first index of the first selector is the tag name. - const componentTag = componentDef.selectors ![0] ![0] as string; + const componentTag = componentDef.selectors![0]![0] as string; const hostRenderer = rendererFactory.createRenderer(null, null); const hostRNode = locateHostElement(hostRenderer, opts.host || componentTag, componentDef.encapsulation); @@ -168,9 +169,8 @@ export function renderComponent<T>( * @returns Component view created */ export function createRootComponentView( - rNode: RElement | null, def: ComponentDef<any>, rootView: LView, - rendererFactory: RendererFactory3, hostRenderer: Renderer3, - sanitizer?: Sanitizer | null): LView { + rNode: RElement|null, def: ComponentDef<any>, rootView: LView, + rendererFactory: RendererFactory3, hostRenderer: Renderer3, sanitizer?: Sanitizer|null): LView { const tView = rootView[TVIEW]; ngDevMode && assertDataInRange(rootView, 0 + HEADER_OFFSET); rootView[0 + HEADER_OFFSET] = rNode; @@ -213,7 +213,7 @@ export function createRootComponentView( */ export function createRootComponent<T>( componentView: LView, componentDef: ComponentDef<T>, rootLView: LView, rootContext: RootContext, - hostFeatures: HostFeature[] | null): any { + hostFeatures: HostFeature[]|null): any { const tView = rootLView[TVIEW]; // Create directive instance with factory() and store at next index in viewData const component = instantiateRootComponent(tView, rootLView, componentDef); @@ -270,13 +270,13 @@ export function createRootContext( * ``` */ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): void { - const rootTView = readPatchedLView(component) ![TVIEW]; + const rootTView = readPatchedLView(component)![TVIEW]; const dirIndex = rootTView.data.length - 1; // TODO(misko): replace `as TNode` with createTNode call. (needs refactoring to lose dep on // LNode). registerPostOrderHooks( - rootTView, { directiveStart: dirIndex, directiveEnd: dirIndex + 1 } as TNode); + rootTView, {directiveStart: dirIndex, directiveEnd: dirIndex + 1} as TNode); } /** diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 7655bbfa33352..86f9908b8a817 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -21,13 +21,13 @@ import {VERSION} from '../version'; import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider'; import {assertComponentType} from './assert'; -import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component'; +import {createRootComponent, createRootComponentView, createRootContext, LifecycleHooksFeature} from './component'; import {getComponentDef} from './definition'; import {NodeInjector} from './di'; import {assignTViewNodeToLView, createLView, createTView, elementCreate, locateHostElement, renderView} from './instructions/shared'; import {ComponentDef} from './interfaces/definition'; import {TContainerNode, TElementContainerNode, TElementNode} from './interfaces/node'; -import {RNode, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; +import {domRendererFactory3, RendererFactory3, RNode} from './interfaces/renderer'; import {LView, LViewFlags, TVIEW, TViewType} from './interfaces/view'; import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces'; import {writeDirectClass} from './node_manipulation'; @@ -43,11 +43,13 @@ export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolve /** * @param ngModule The NgModuleRef to which all resolved factories are bound. */ - constructor(private ngModule?: viewEngine_NgModuleRef<any>) { super(); } + constructor(private ngModule?: viewEngine_NgModuleRef<any>) { + super(); + } resolveComponentFactory<T>(component: Type<T>): viewEngine_ComponentFactory<T> { ngDevMode && assertComponentType(component); - const componentDef = getComponentDef(component) !; + const componentDef = getComponentDef(component)!; return new ComponentFactory(componentDef, this.ngModule); } } @@ -79,7 +81,7 @@ export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDUL function createChainedInjector(rootViewInjector: Injector, moduleInjector: Injector): Injector { return { - get: <T>(token: Type<T>| InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): T => { + get: <T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): T => { const value = rootViewInjector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as T, flags); if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR || @@ -205,8 +207,9 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> { // projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade // case). Here we do normalize passed data structure to be an array of arrays to avoid // complex checks down the line. - tElementNode.projection = - projectableNodes.map((nodesforSlot: RNode[]) => { return Array.from(nodesforSlot); }); + tElementNode.projection = projectableNodes.map((nodesforSlot: RNode[]) => { + return Array.from(nodesforSlot); + }); } // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and @@ -227,7 +230,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> { if (!rootSelectorOrNode || isIsolated) { // The host element of the internal or isolated root view is attached to the component's host // view node. - componentRef.hostView._tViewNode !.child = tElementNode; + componentRef.hostView._tViewNode!.child = tElementNode; } return componentRef; } @@ -272,7 +275,9 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> { this.componentType = componentType; } - get injector(): Injector { return new NodeInjector(this._tNode, this._rootLView); } + get injector(): Injector { + return new NodeInjector(this._tNode, this._rootLView); + } destroy(): void { if (this.destroyCbs) { diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index 5429aba0edc79..c7da4fc3abb7b 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -18,7 +18,8 @@ import {getComponentLViewByIndex, getNativeByTNodeOrNull, readPatchedData, unwra -/** Returns the matching `LContext` data for a given DOM node, directive or component instance. +/** + * Returns the matching `LContext` data for a given DOM node, directive or component instance. * * This function will examine the provided DOM element, component, or directive instance\'s * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched @@ -43,7 +44,7 @@ export function getLContext(target: any): LContext|null { // only when it's an array is it considered an LView instance // ... otherwise it's an already constructed LContext instance if (Array.isArray(mpValue)) { - const lView: LView = mpValue !; + const lView: LView = mpValue!; let nodeIndex: number; let component: any = undefined; let directives: any[]|null|undefined = undefined; @@ -173,7 +174,7 @@ export function getComponentViewByInstance(componentInstance: {}): LView { * Assigns the given data to the given target (which could be a component, * directive or DOM node instance) using monkey-patching. */ -export function attachPatchData(target: any, data: LView | LContext) { +export function attachPatchData(target: any, data: LView|LContext) { target[MONKEY_PATCH_KEY_NAME] = data; } @@ -191,7 +192,7 @@ export function isDirectiveInstance(instance: any): boolean { function findViaNativeElement(lView: LView, target: RElement): number { let tNode = lView[TVIEW].firstChild; while (tNode) { - const native = getNativeByTNodeOrNull(tNode, lView) !; + const native = getNativeByTNodeOrNull(tNode, lView)!; if (native === target) { return tNode.index; } diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 3b1215be9f44c..f4a7fedce7488 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -303,7 +303,7 @@ export function ɵɵdefineComponent<T>(componentDefinition: { decls: componentDefinition.decls, vars: componentDefinition.vars, factory: null, - template: componentDefinition.template || null !, + template: componentDefinition.template || null!, consts: componentDefinition.consts || null, ngContentSelectors: componentDefinition.ngContentSelectors, hostBindings: componentDefinition.hostBindings || null, @@ -311,8 +311,8 @@ export function ɵɵdefineComponent<T>(componentDefinition: { hostAttrs: componentDefinition.hostAttrs || null, contentQueries: componentDefinition.contentQueries || null, declaredInputs: declaredInputs, - inputs: null !, // assigned in noSideEffects - outputs: null !, // assigned in noSideEffects + inputs: null!, // assigned in noSideEffects + outputs: null!, // assigned in noSideEffects exportAs: componentDefinition.exportAs || null, onChanges: null, onInit: typePrototype.ngOnInit || null, @@ -323,8 +323,8 @@ export function ɵɵdefineComponent<T>(componentDefinition: { afterViewChecked: typePrototype.ngAfterViewChecked || null, onDestroy: typePrototype.ngOnDestroy || null, onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush, - directiveDefs: null !, // assigned in noSideEffects - pipeDefs: null !, // assigned in noSideEffects + directiveDefs: null!, // assigned in noSideEffects + pipeDefs: null!, // assigned in noSideEffects selectors: componentDefinition.selectors || EMPTY_ARRAY, viewQuery: componentDefinition.viewQuery || null, features: componentDefinition.features as DirectiveDefFeature[] || null, @@ -339,9 +339,9 @@ export function ɵɵdefineComponent<T>(componentDefinition: { schemas: componentDefinition.schemas || null, tView: null, }; - const directiveTypes = componentDefinition.directives !; + const directiveTypes = componentDefinition.directives!; const feature = componentDefinition.features; - const pipeTypes = componentDefinition.pipes !; + const pipeTypes = componentDefinition.pipes!; def.id += _renderCompCount++; def.inputs = invertObject(componentDefinition.inputs, declaredInputs), def.outputs = invertObject(componentDefinition.outputs), @@ -373,7 +373,7 @@ export function extractDirectiveDef(type: Type<any>): DirectiveDef<any>|Componen if (ngDevMode && !def) { throw new Error(`'${type.name}' is neither 'ComponentType' or 'DirectiveType'.`); } - return def !; + return def!; } export function extractPipeDef(type: Type<any>): PipeDef<any> { @@ -381,7 +381,7 @@ export function extractPipeDef(type: Type<any>): PipeDef<any> { if (ngDevMode && !def) { throw new Error(`'${type.name}' is not a 'PipeType'.`); } - return def !; + return def!; } export const autoRegisterModuleById: {[id: string]: NgModuleType} = {}; @@ -425,8 +425,9 @@ export function ɵɵdefineNgModule<T>(def: { id: def.id || null, }; if (def.id != null) { - noSideEffects( - () => { autoRegisterModuleById[def.id !] = def.type as unknown as NgModuleType; }); + noSideEffects(() => { + autoRegisterModuleById[def.id!] = def.type as unknown as NgModuleType; + }); } return res as never; } @@ -443,7 +444,7 @@ export function ɵɵdefineNgModule<T>(def: { */ export function ɵɵsetNgModuleScope(type: any, scope: { /** List of components, directives, and pipes declared by this module. */ - declarations?: Type<any>[] | (() => Type<any>[]); + declarations?: Type<any>[]|(() => Type<any>[]); /** List of modules or `ModuleWithProviders` imported by this module. */ imports?: Type<any>[] | (() => Type<any>[]); @@ -455,11 +456,11 @@ export function ɵɵsetNgModuleScope(type: any, scope: { exports?: Type<any>[] | (() => Type<any>[]); }): void { return noSideEffects(() => { - const ngModuleDef = getNgModuleDef(type, true); - ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY; - ngModuleDef.imports = scope.imports || EMPTY_ARRAY; - ngModuleDef.exports = scope.exports || EMPTY_ARRAY; - }) as never; + const ngModuleDef = getNgModuleDef(type, true); + ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY; + ngModuleDef.imports = scope.imports || EMPTY_ARRAY; + ngModuleDef.exports = scope.exports || EMPTY_ARRAY; + }) as never; } /** @@ -518,13 +519,13 @@ export function ɵɵsetNgModuleScope(type: any, scope: { */ function invertObject<T>( - obj?: {[P in keyof T]?: string | [string, string]}, + obj?: {[P in keyof T]?: string|[string, string]}, secondary?: {[key: string]: string}): {[P in keyof T]: string} { if (obj == null) return EMPTY_OBJ as any; const newLookup: any = {}; for (const minifiedKey in obj) { if (obj.hasOwnProperty(minifiedKey)) { - let publicName: string|[string, string] = obj[minifiedKey] !; + let publicName: string|[string, string] = obj[minifiedKey]!; let declaredName = publicName; if (Array.isArray(publicName)) { declaredName = publicName[1]; @@ -555,142 +556,143 @@ function invertObject<T>( * * @codeGenApi */ -export const ɵɵdefineDirective = ɵɵdefineComponent as any as<T>(directiveDefinition: { - /** - * Directive type, needed to configure the injector. - */ - type: Type<T>; - - /** The selectors that will be used to match nodes to this directive. */ - selectors?: CssSelectorList; - - /** - * A map of input names. - * - * The format is in: `{[actualPropertyName: string]:(string|[string, string])}`. - * - * Given: - * ``` - * class MyComponent { - * @Input() - * publicInput1: string; - * - * @Input('publicInput2') - * declaredInput2: string; - * } - * ``` - * - * is described as: - * ``` - * { - * publicInput1: 'publicInput1', - * declaredInput2: ['declaredInput2', 'publicInput2'], - * } - * ``` - * - * Which the minifier may translate to: - * ``` - * { - * minifiedPublicInput1: 'publicInput1', - * minifiedDeclaredInput2: [ 'publicInput2', 'declaredInput2'], - * } - * ``` - * - * This allows the render to re-construct the minified, public, and declared names - * of properties. - * - * NOTE: - * - Because declared and public name are usually same we only generate the array - * `['declared', 'public']` format when they differ. - * - The reason why this API and `outputs` API is not the same is that `NgOnChanges` has - * inconsistent behavior in that it uses declared names rather than minified or public. For - * this reason `NgOnChanges` will be deprecated and removed in future version and this - * API will be simplified to be consistent with `output`. - */ - inputs?: {[P in keyof T]?: string | [string, string]}; - - /** - * A map of output names. - * - * The format is in: `{[actualPropertyName: string]:string}`. - * - * Which the minifier may translate to: `{[minifiedPropertyName: string]:string}`. - * - * This allows the render to re-construct the minified and non-minified names - * of properties. - */ - outputs?: {[P in keyof T]?: string}; - - /** - * A list of optional features to apply. - * - * See: {@link NgOnChangesFeature}, {@link ProvidersFeature}, {@link InheritDefinitionFeature} - */ - features?: DirectiveDefFeature[]; - - /** - * Function executed by the parent template to allow child directive to apply host bindings. - */ - hostBindings?: HostBindingsFunction<T>; - - /** - * The number of bindings in this directive `hostBindings` (including pure fn bindings). - * - * Used to calculate the length of the component's LView array, so we - * can pre-fill the array and set the host binding start index. - */ - hostVars?: number; - - /** - * Assign static attribute values to a host element. - * - * This property will assign static attribute values as well as class and style - * values to a host element. Since attribute values can consist of different types of values, the - * `hostAttrs` array must include the values in the following format: - * - * attrs = [ - * // static attributes (like `title`, `name`, `id`...) - * attr1, value1, attr2, value, - * - * // a single namespace value (like `x:id`) - * NAMESPACE_MARKER, namespaceUri1, name1, value1, - * - * // another single namespace value (like `x:name`) - * NAMESPACE_MARKER, namespaceUri2, name2, value2, - * - * // a series of CSS classes that will be applied to the element (no spaces) - * CLASSES_MARKER, class1, class2, class3, - * - * // a series of CSS styles (property + value) that will be applied to the element - * STYLES_MARKER, prop1, value1, prop2, value2 - * ] - * - * All non-class and non-style attributes must be defined at the start of the list - * first before all class and style values are set. When there is a change in value - * type (like when classes and styles are introduced) a marker must be used to separate - * the entries. The marker values themselves are set via entries found in the - * [AttributeMarker] enum. - */ - hostAttrs?: TAttributes; - - /** - * Function to create instances of content queries associated with a given directive. - */ - contentQueries?: ContentQueriesFunction<T>; - - /** - * Additional set of instructions specific to view query processing. This could be seen as a - * set of instructions to be inserted into the template function. - */ - viewQuery?: ViewQueriesFunction<T>| null; - - /** - * Defines the name that can be used in the template to assign this directive to a variable. - * - * See: {@link Directive.exportAs} - */ - exportAs?: string[]; -}) => never; +export const ɵɵdefineDirective = + ɵɵdefineComponent as any as<T>(directiveDefinition: { + /** + * Directive type, needed to configure the injector. + */ + type: Type<T>; + + /** The selectors that will be used to match nodes to this directive. */ + selectors?: CssSelectorList; + + /** + * A map of input names. + * + * The format is in: `{[actualPropertyName: string]:(string|[string, string])}`. + * + * Given: + * ``` + * class MyComponent { + * @Input() + * publicInput1: string; + * + * @Input('publicInput2') + * declaredInput2: string; + * } + * ``` + * + * is described as: + * ``` + * { + * publicInput1: 'publicInput1', + * declaredInput2: ['declaredInput2', 'publicInput2'], + * } + * ``` + * + * Which the minifier may translate to: + * ``` + * { + * minifiedPublicInput1: 'publicInput1', + * minifiedDeclaredInput2: [ 'publicInput2', 'declaredInput2'], + * } + * ``` + * + * This allows the render to re-construct the minified, public, and declared names + * of properties. + * + * NOTE: + * - Because declared and public name are usually same we only generate the array + * `['declared', 'public']` format when they differ. + * - The reason why this API and `outputs` API is not the same is that `NgOnChanges` has + * inconsistent behavior in that it uses declared names rather than minified or public. For + * this reason `NgOnChanges` will be deprecated and removed in future version and this + * API will be simplified to be consistent with `output`. + */ + inputs?: {[P in keyof T]?: string | [string, string]}; + + /** + * A map of output names. + * + * The format is in: `{[actualPropertyName: string]:string}`. + * + * Which the minifier may translate to: `{[minifiedPropertyName: string]:string}`. + * + * This allows the render to re-construct the minified and non-minified names + * of properties. + */ + outputs?: {[P in keyof T]?: string}; + + /** + * A list of optional features to apply. + * + * See: {@link NgOnChangesFeature}, {@link ProvidersFeature}, {@link InheritDefinitionFeature} + */ + features?: DirectiveDefFeature[]; + + /** + * Function executed by the parent template to allow child directive to apply host bindings. + */ + hostBindings?: HostBindingsFunction<T>; + + /** + * The number of bindings in this directive `hostBindings` (including pure fn bindings). + * + * Used to calculate the length of the component's LView array, so we + * can pre-fill the array and set the host binding start index. + */ + hostVars?: number; + + /** + * Assign static attribute values to a host element. + * + * This property will assign static attribute values as well as class and style + * values to a host element. Since attribute values can consist of different types of values, + * the `hostAttrs` array must include the values in the following format: + * + * attrs = [ + * // static attributes (like `title`, `name`, `id`...) + * attr1, value1, attr2, value, + * + * // a single namespace value (like `x:id`) + * NAMESPACE_MARKER, namespaceUri1, name1, value1, + * + * // another single namespace value (like `x:name`) + * NAMESPACE_MARKER, namespaceUri2, name2, value2, + * + * // a series of CSS classes that will be applied to the element (no spaces) + * CLASSES_MARKER, class1, class2, class3, + * + * // a series of CSS styles (property + value) that will be applied to the element + * STYLES_MARKER, prop1, value1, prop2, value2 + * ] + * + * All non-class and non-style attributes must be defined at the start of the list + * first before all class and style values are set. When there is a change in value + * type (like when classes and styles are introduced) a marker must be used to separate + * the entries. The marker values themselves are set via entries found in the + * [AttributeMarker] enum. + */ + hostAttrs?: TAttributes; + + /** + * Function to create instances of content queries associated with a given directive. + */ + contentQueries?: ContentQueriesFunction<T>; + + /** + * Additional set of instructions specific to view query processing. This could be seen as a + * set of instructions to be inserted into the template function. + */ + viewQuery?: ViewQueriesFunction<T>| null; + + /** + * Defines the name that can be used in the template to assign this directive to a variable. + * + * See: {@link Directive.exportAs} + */ + exportAs?: string[]; + }) => never; /** * Create a pipe definition object. @@ -719,12 +721,12 @@ export function ɵɵdefinePipe<T>(pipeDef: { pure?: boolean }): never { return (<PipeDef<T>>{ - type: pipeDef.type, - name: pipeDef.name, - factory: null, - pure: pipeDef.pure !== false, - onDestroy: pipeDef.type.prototype.ngOnDestroy || null - }) as never; + type: pipeDef.type, + name: pipeDef.name, + factory: null, + pure: pipeDef.pure !== false, + onDestroy: pipeDef.type.prototype.ngOnDestroy || null + }) as never; } /** diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index c70505a2d1e6d..14a40667cd42f 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -21,10 +21,10 @@ import {getFactoryDef} from './definition'; import {NG_ELEMENT_ID, NG_FACTORY_DEF} from './fields'; import {registerPreOrderHooks} from './hooks'; import {DirectiveDef, FactoryFn} from './interfaces/definition'; -import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector'; +import {isFactory, NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE} from './interfaces/injector'; import {AttributeMarker, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeProviderIndexes, TNodeType} from './interfaces/node'; import {isComponentDef, isComponentHost} from './interfaces/type_checks'; -import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, TData, TVIEW, TView, T_HOST} from './interfaces/view'; +import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, T_HOST, TData, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; import {enterDI, leaveDI} from './state'; import {isNameOnlyAttributeMarker} from './util/attrs_utils'; @@ -71,7 +71,7 @@ import {stringifyForError} from './util/misc_utils'; */ let includeViewProviders = true; -function setIncludeViewProviders(v: boolean): boolean { +export function setIncludeViewProviders(v: boolean): boolean { const oldValue = includeViewProviders; includeViewProviders = v; return oldValue; @@ -97,7 +97,7 @@ let nextNgElementId = 0; * @param type The directive token to register */ export function bloomAdd( - injectorIndex: number, tView: TView, type: Type<any>| InjectionToken<any>| string): void { + injectorIndex: number, tView: TView, type: Type<any>|InjectionToken<any>|string): void { ngDevMode && assertEqual(tView.firstCreatePass, true, 'expected firstCreatePass to be true'); let id: number|undefined = typeof type !== 'string' ? (type as any)[NG_ELEMENT_ID] : type.charCodeAt(0) || 0; @@ -141,7 +141,7 @@ export function bloomAdd( * @returns Node injector */ export function getOrCreateNodeInjectorForNode( - tNode: TElementNode | TContainerNode | TElementContainerNode, hostView: LView): number { + tNode: TElementNode|TContainerNode|TElementContainerNode, hostView: LView): number { const existingInjectorIndex = getInjectorIndex(tNode, hostView); if (existingInjectorIndex !== -1) { return existingInjectorIndex; @@ -175,7 +175,7 @@ export function getOrCreateNodeInjectorForNode( return injectorIndex; } -function insertBloom(arr: any[], footer: TNode | null): void { +function insertBloom(arr: any[], footer: TNode|null): void { arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer); } @@ -211,7 +211,7 @@ export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeIn let hostTNode = view[T_HOST]; let viewOffset = 1; while (hostTNode && hostTNode.injectorIndex === -1) { - view = view[DECLARATION_VIEW] !; + view = view[DECLARATION_VIEW]!; hostTNode = view ? view[T_HOST] : null; viewOffset++; } @@ -229,7 +229,7 @@ export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeIn * @param token The type or the injection token to be made public */ export function diPublicInInjector( - injectorIndex: number, tView: TView, token: InjectionToken<any>| Type<any>): void { + injectorIndex: number, tView: TView, token: InjectionToken<any>|Type<any>): void { bloomAdd(injectorIndex, tView, token); } @@ -265,8 +265,9 @@ export function diPublicInInjector( * @publicApi */ export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): string|null { - ngDevMode && assertNodeOfPossibleTypes( - tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); + ngDevMode && + assertNodeOfPossibleTypes( + tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); ngDevMode && assertDefined(tNode, 'expecting tNode'); if (attrNameToInject === 'class') { return tNode.classes; @@ -327,7 +328,7 @@ export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): str * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided */ export function getOrCreateInjectable<T>( - tNode: TDirectiveHostNode | null, lView: LView, token: Type<T>| InjectionToken<T>, + tNode: TDirectiveHostNode|null, lView: LView, token: Type<T>|InjectionToken<T>, flags: InjectFlags = InjectFlags.Default, notFoundValue?: any): T|null { if (tNode !== null) { const bloomHash = bloomHashBitOrFactory(token); @@ -443,8 +444,8 @@ export function getOrCreateInjectable<T>( const NOT_FOUND = {}; function searchTokensOnInjector<T>( - injectorIndex: number, lView: LView, token: Type<T>| InjectionToken<T>, - previousTView: TView | null, flags: InjectFlags, hostTElementNode: TNode | null) { + injectorIndex: number, lView: LView, token: Type<T>|InjectionToken<T>, + previousTView: TView|null, flags: InjectFlags, hostTElementNode: TNode|null) { const currentTView = lView[TVIEW]; const tNode = currentTView.data[injectorIndex + TNODE] as TNode; // First, we need to determine if view providers can be accessed by the starting element. @@ -490,8 +491,8 @@ function searchTokensOnInjector<T>( * @returns Index of a found directive or provider, or null when none found. */ export function locateDirectiveOrProvider<T>( - tNode: TNode, tView: TView, token: Type<T>| InjectionToken<T>, canAccessViewProviders: boolean, - isHostSpecialCase: boolean | number): number|null { + tNode: TNode, tView: TView, token: Type<T>|InjectionToken<T>, canAccessViewProviders: boolean, + isHostSpecialCase: boolean|number): number|null { const nodeProviderIndexes = tNode.providerIndexes; const tInjectables = tView.data; @@ -521,12 +522,12 @@ export function locateDirectiveOrProvider<T>( } /** -* Retrieve or instantiate the injectable from the `LView` at particular `index`. -* -* This function checks to see if the value has already been instantiated and if so returns the -* cached `injectable`. Otherwise if it detects that the value is still a factory it -* instantiates the `injectable` and caches the value. -*/ + * Retrieve or instantiate the injectable from the `LView` at particular `index`. + * + * This function checks to see if the value has already been instantiated and if so returns the + * cached `injectable`. Otherwise if it detects that the value is still a factory it + * instantiates the `injectable` and caches the value. + */ export function getNodeInjectable( lView: LView, tView: TView, index: number, tNode: TDirectiveHostNode): any { let value = lView[index]; @@ -577,8 +578,8 @@ export function getNodeInjectable( * @returns the matching bit to check in the bloom filter or `null` if the token is not known. * When the returned value is negative then it represents special values such as `Injector`. */ -export function bloomHashBitOrFactory(token: Type<any>| InjectionToken<any>| string): number| - Function|undefined { +export function bloomHashBitOrFactory(token: Type<any>|InjectionToken<any>|string): number|Function| + undefined { ngDevMode && assertDefined(token, 'token must be defined'); if (typeof token === 'string') { return token.charCodeAt(0) || 0; @@ -588,8 +589,7 @@ export function bloomHashBitOrFactory(token: Type<any>| InjectionToken<any>| str return (typeof tokenId === 'number' && tokenId > 0) ? tokenId & BLOOM_MASK : tokenId; } -export function bloomHasToken( - bloomHash: number, injectorIndex: number, injectorView: LView | TData) { +export function bloomHasToken(bloomHash: number, injectorIndex: number, injectorView: LView|TData) { // Create a mask that targets the specific bit associated with the directive we're looking for. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding // to bit positions 0 - 31 in a 32 bit integer. @@ -639,9 +639,9 @@ export function ɵɵgetFactoryOf<T>(type: Type<any>): FactoryFn<T>|null { if (isForwardRef(type)) { return (() => { - const factory = ɵɵgetFactoryOf<T>(resolveForwardRef(typeAny)); - return factory ? factory() : null; - }) as any; + const factory = ɵɵgetFactoryOf<T>(resolveForwardRef(typeAny)); + return factory ? factory() : null; + }) as any; } let factory = getFactoryDef<T>(typeAny); diff --git a/packages/core/src/render3/di_setup.ts b/packages/core/src/render3/di_setup.ts index 483ae76f4497c..5119bc37060ea 100644 --- a/packages/core/src/render3/di_setup.ts +++ b/packages/core/src/render3/di_setup.ts @@ -10,6 +10,7 @@ import {resolveForwardRef} from '../di/forward_ref'; import {ClassProvider, Provider} from '../di/interface/provider'; import {isClassProvider, isTypeProvider, providerToFactory} from '../di/r3_injector'; +import {assertDefined} from '../util/assert'; import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from './di'; import {ɵɵdirectiveInject} from './instructions/all'; @@ -17,7 +18,7 @@ import {DirectiveDef} from './interfaces/definition'; import {NodeInjectorFactory} from './interfaces/injector'; import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNodeProviderIndexes} from './interfaces/node'; import {isComponentDef} from './interfaces/type_checks'; -import {LView, TData, TVIEW, TView} from './interfaces/view'; +import {DestroyHookData, LView, TData, TVIEW, TView} from './interfaces/view'; import {getLView, getPreviousOrParentTNode, getTView} from './state'; @@ -149,7 +150,7 @@ function resolveProvider( if (!isViewProvider && doesViewProvidersFactoryExist) { lInjectablesBlueprint[existingViewProvidersFactoryIndex].providerFactory = factory; } - registerDestroyHooksIfSupported(tView, provider, tInjectables.length); + registerDestroyHooksIfSupported(tView, provider, tInjectables.length, 0); tInjectables.push(token); tNode.directiveStart++; tNode.directiveEnd++; @@ -160,16 +161,19 @@ function resolveProvider( lView.push(factory); } else { // Cases 1.b and 2.b - registerDestroyHooksIfSupported( - tView, provider, existingProvidersFactoryIndex > -1 ? - existingProvidersFactoryIndex : - existingViewProvidersFactoryIndex); - multiFactoryAdd( - lInjectablesBlueprint ![isViewProvider ? existingViewProvidersFactoryIndex : existingProvidersFactoryIndex], + const indexInFactory = multiFactoryAdd( + lInjectablesBlueprint! + [isViewProvider ? existingViewProvidersFactoryIndex : + existingProvidersFactoryIndex], providerFactory, !isViewProvider && isComponent); + registerDestroyHooksIfSupported( + tView, provider, + existingProvidersFactoryIndex > -1 ? existingProvidersFactoryIndex : + existingViewProvidersFactoryIndex, + indexInFactory); } if (!isViewProvider && isComponent && doesViewProvidersFactoryExist) { - lInjectablesBlueprint[existingViewProvidersFactoryIndex].componentProviders !++; + lInjectablesBlueprint[existingViewProvidersFactoryIndex].componentProviders!++; } } } @@ -180,28 +184,47 @@ function resolveProvider( * @param tView `TView` in which to register the hook. * @param provider Provider whose hook should be registered. * @param contextIndex Index under which to find the context for the hook when it's being invoked. + * @param indexInFactory Only required for `multi` providers. Index of the provider in the multi + * provider factory. */ function registerDestroyHooksIfSupported( - tView: TView, provider: Exclude<Provider, any[]>, contextIndex: number) { + tView: TView, provider: Exclude<Provider, any[]>, contextIndex: number, + indexInFactory?: number) { const providerIsTypeProvider = isTypeProvider(provider); if (providerIsTypeProvider || isClassProvider(provider)) { const prototype = ((provider as ClassProvider).useClass || provider).prototype; const ngOnDestroy = prototype.ngOnDestroy; if (ngOnDestroy) { - (tView.destroyHooks || (tView.destroyHooks = [])).push(contextIndex, ngOnDestroy); + const hooks = tView.destroyHooks || (tView.destroyHooks = []); + + if (!providerIsTypeProvider && ((provider as ClassProvider)).multi) { + ngDevMode && + assertDefined( + indexInFactory, 'indexInFactory when registering multi factory destroy hook'); + const existingCallbacksIndex = hooks.indexOf(contextIndex); + + if (existingCallbacksIndex === -1) { + hooks.push(contextIndex, [indexInFactory, ngOnDestroy]); + } else { + (hooks[existingCallbacksIndex + 1] as DestroyHookData).push(indexInFactory!, ngOnDestroy); + } + } else { + hooks.push(contextIndex, ngOnDestroy); + } } } } /** * Add a factory in a multi factory. + * @returns Index at which the factory was inserted. */ function multiFactoryAdd( - multiFactory: NodeInjectorFactory, factory: () => any, isComponentProvider: boolean): void { - multiFactory.multi !.push(factory); + multiFactory: NodeInjectorFactory, factory: () => any, isComponentProvider: boolean): number { if (isComponentProvider) { - multiFactory.componentProviders !++; + multiFactory.componentProviders!++; } + return multiFactory.multi!.push(factory) - 1; } /** @@ -220,7 +243,7 @@ function indexOf(item: any, arr: any[], begin: number, end: number) { function multiProvidersFactoryResolver( this: NodeInjectorFactory, _: undefined, tData: TData, lData: LView, tNode: TDirectiveHostNode): any[] { - return multiResolve(this.multi !, []); + return multiResolve(this.multi!, []); } /** @@ -231,12 +254,12 @@ function multiProvidersFactoryResolver( function multiViewProvidersFactoryResolver( this: NodeInjectorFactory, _: undefined, tData: TData, lView: LView, tNode: TDirectiveHostNode): any[] { - const factories = this.multi !; + const factories = this.multi!; let result: any[]; if (this.providerFactory) { - const componentCount = this.providerFactory.componentProviders !; + const componentCount = this.providerFactory.componentProviders!; const multiProviders = - getNodeInjectable(lView, lView[TVIEW], this.providerFactory !.index !, tNode); + getNodeInjectable(lView, lView[TVIEW], this.providerFactory!.index!, tNode); // Copy the section of the array which contains `multi` `providers` from the component result = multiProviders.slice(0, componentCount); // Insert the `viewProvider` instances. @@ -258,7 +281,7 @@ function multiViewProvidersFactoryResolver( */ function multiResolve(factories: Array<() => any>, result: any[]): any[] { for (let i = 0; i < factories.length; i++) { - const factory = factories[i] !as() => null; + const factory = factories[i]! as () => null; result.push(factory()); } return result; diff --git a/packages/core/src/render3/empty.ts b/packages/core/src/render3/empty.ts index 36be572fbe5d1..fa7af07343e6f 100644 --- a/packages/core/src/render3/empty.ts +++ b/packages/core/src/render3/empty.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 {initNgDevMode} from '../util/ng_dev_mode'; /** diff --git a/packages/core/src/render3/errors.ts b/packages/core/src/render3/errors.ts index d92cb4ebc27e1..b1fc6a6457e2c 100644 --- a/packages/core/src/render3/errors.ts +++ b/packages/core/src/render3/errors.ts @@ -47,7 +47,8 @@ export function throwErrorIfNoChangesMode( creationMode: boolean, oldValue: any, currValue: any, propName?: string): never|void { const field = propName ? ` for '${propName}'` : ''; let msg = - `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${field}: '${oldValue}'. Current value: '${currValue}'.`; + `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${ + field}: '${oldValue}'. Current value: '${currValue}'.`; if (creationMode) { msg += ` It seems like the view has been created after its parent and its children have been dirty checked.` + diff --git a/packages/core/src/render3/features/copy_definition_feature.ts b/packages/core/src/render3/features/copy_definition_feature.ts index 3d4bc0f2569c0..4cad1d4446cca 100644 --- a/packages/core/src/render3/features/copy_definition_feature.ts +++ b/packages/core/src/render3/features/copy_definition_feature.ts @@ -64,16 +64,16 @@ const COPY_COMPONENT_FIELDS: Exclude<keyof ComponentDef<unknown>, keyof Directiv * * @codeGenApi */ -export function ɵɵCopyDefinitionFeature(definition: DirectiveDef<any>| ComponentDef<any>): void { - let superType = getSuperType(definition.type) !; +export function ɵɵCopyDefinitionFeature(definition: DirectiveDef<any>|ComponentDef<any>): void { + let superType = getSuperType(definition.type)!; let superDef: DirectiveDef<any>|ComponentDef<any>|undefined = undefined; if (isComponentDef(definition)) { // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance. - superDef = superType.ɵcmp !; + superDef = superType.ɵcmp!; } else { // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance. - superDef = superType.ɵdir !; + superDef = superType.ɵdir!; } // Needed because `definition` fields are readonly. diff --git a/packages/core/src/render3/features/inherit_definition_feature.ts b/packages/core/src/render3/features/inherit_definition_feature.ts index 4d96eafef300c..66b0f689fb8a6 100644 --- a/packages/core/src/render3/features/inherit_definition_feature.ts +++ b/packages/core/src/render3/features/inherit_definition_feature.ts @@ -27,7 +27,7 @@ type WritableDef = Writable<DirectiveDef<any>|ComponentDef<any>>; * * @codeGenApi */ -export function ɵɵInheritDefinitionFeature(definition: DirectiveDef<any>| ComponentDef<any>): void { +export function ɵɵInheritDefinitionFeature(definition: DirectiveDef<any>|ComponentDef<any>): void { let superType = getSuperType(definition.type); let shouldInheritFields = true; const inheritanceChain: WritableDef[] = [definition]; diff --git a/packages/core/src/render3/features/ng_onchanges_feature.ts b/packages/core/src/render3/features/ng_onchanges_feature.ts index 7d749c624fdd7..3e7bf13830688 100644 --- a/packages/core/src/render3/features/ng_onchanges_feature.ts +++ b/packages/core/src/render3/features/ng_onchanges_feature.ts @@ -13,7 +13,7 @@ import {DirectiveDef, DirectiveDefFeature} from '../interfaces/definition'; const PRIVATE_PREFIX = '__ngOnChanges_'; -type OnChangesExpando = OnChanges & { +type OnChangesExpando = OnChanges&{ __ngOnChanges_: SimpleChanges|null|undefined; // tslint:disable-next-line:no-any Can hold any value [key: string]: any; @@ -45,7 +45,7 @@ type OnChangesExpando = OnChanges & { export function ɵɵNgOnChangesFeature<T>(definition: DirectiveDef<T>): void { if (definition.type.prototype.ngOnChanges) { definition.setInput = ngOnChangesSetInput; - (definition as{onChanges: Function}).onChanges = wrapOnChanges(); + (definition as {onChanges: Function}).onChanges = wrapOnChanges(); } } @@ -61,9 +61,9 @@ function wrapOnChanges() { const current = simpleChangesStore && simpleChangesStore.current; if (current) { - const previous = simpleChangesStore !.previous; + const previous = simpleChangesStore!.previous; if (previous === EMPTY_OBJ) { - simpleChangesStore !.previous = current; + simpleChangesStore!.previous = current; } else { // New changes are copied to the previous store, so that we don't lose history for inputs // which were not changed this time @@ -71,7 +71,7 @@ function wrapOnChanges() { previous[key] = current[key]; } } - simpleChangesStore !.current = null; + simpleChangesStore!.current = null; this.ngOnChanges(current); } }; @@ -84,7 +84,7 @@ function ngOnChangesSetInput<T>( const current = simpleChangesStore.current || (simpleChangesStore.current = {}); const previous = simpleChangesStore.previous; - const declaredName = (this.declaredInputs as{[key: string]: string})[publicName]; + const declaredName = (this.declaredInputs as {[key: string]: string})[publicName]; const previousChange = previous[declaredName]; current[declaredName] = new SimpleChange( previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ); diff --git a/packages/core/src/render3/global_utils_api.ts b/packages/core/src/render3/global_utils_api.ts index 330918cf0369f..feb419de1d3ae 100644 --- a/packages/core/src/render3/global_utils_api.ts +++ b/packages/core/src/render3/global_utils_api.ts @@ -16,4 +16,4 @@ */ export {applyChanges} from './util/change_detection_utils'; -export {Listener, getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents} from './util/discovery_utils'; +export {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents, Listener} from './util/discovery_utils'; diff --git a/packages/core/src/render3/hooks.ts b/packages/core/src/render3/hooks.ts index eebaed60aa362..0667d62e23b51 100644 --- a/packages/core/src/render3/hooks.ts +++ b/packages/core/src/render3/hooks.ts @@ -79,8 +79,8 @@ export function registerPostOrderHooks(tView: TView, tNode: TNode): void { if (directiveDef.afterContentChecked) { (tView.contentHooks || (tView.contentHooks = [])).push(i, directiveDef.afterContentChecked); - (tView.contentCheckHooks || (tView.contentCheckHooks = [ - ])).push(i, directiveDef.afterContentChecked); + (tView.contentCheckHooks || (tView.contentCheckHooks = [])) + .push(i, directiveDef.afterContentChecked); } if (directiveDef.afterViewInit) { @@ -132,7 +132,7 @@ export function registerPostOrderHooks(tView: TView, tNode: TNode): void { * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ -export function executeCheckHooks(lView: LView, hooks: HookData, nodeIndex?: number | null) { +export function executeCheckHooks(lView: LView, hooks: HookData, nodeIndex?: number|null) { callHooks(lView, hooks, InitPhaseState.InitPhaseCompleted, nodeIndex); } @@ -150,10 +150,11 @@ export function executeCheckHooks(lView: LView, hooks: HookData, nodeIndex?: num * case, when executing select(number)) */ export function executeInitAndCheckHooks( - lView: LView, hooks: HookData, initPhase: InitPhaseState, nodeIndex?: number | null) { - ngDevMode && assertNotEqual( - initPhase, InitPhaseState.InitPhaseCompleted, - 'Init pre-order hooks should not be called more than once'); + lView: LView, hooks: HookData, initPhase: InitPhaseState, nodeIndex?: number|null) { + ngDevMode && + assertNotEqual( + initPhase, InitPhaseState.InitPhaseCompleted, + 'Init pre-order hooks should not be called more than once'); if ((lView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase) { callHooks(lView, hooks, initPhase, nodeIndex); } @@ -188,17 +189,18 @@ export function incrementInitPhaseFlags(lView: LView, initPhase: InitPhaseState) */ function callHooks( currentView: LView, arr: HookData, initPhase: InitPhaseState, - currentNodeIndex: number | null | undefined): void { - ngDevMode && assertEqual( - getCheckNoChangesMode(), false, - 'Hooks should never be run in the check no changes mode.'); + currentNodeIndex: number|null|undefined): void { + ngDevMode && + assertEqual( + getCheckNoChangesMode(), false, + 'Hooks should never be run in the check no changes mode.'); const startIndex = currentNodeIndex !== undefined ? (currentView[PREORDER_HOOK_FLAGS] & PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask) : 0; const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1; let lastNodeIndexFound = 0; for (let i = startIndex; i < arr.length; i++) { - const hook = arr[i + 1] as() => void; + const hook = arr[i + 1] as () => void; if (typeof hook === 'number') { lastNodeIndexFound = arr[i] as number; if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) { @@ -229,7 +231,7 @@ function callHooks( */ function callHook(currentView: LView, initPhase: InitPhaseState, arr: HookData, i: number) { const isInitHook = arr[i] < 0; - const hook = arr[i + 1] as() => void; + const hook = arr[i + 1] as () => void; const directiveIndex = isInitHook ? -arr[i] : arr[i] as number; const directive = currentView[directiveIndex]; if (isInitHook) { diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index 640c3eefd9b7b..b8f10aeff1272 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -8,11 +8,12 @@ import '../util/ng_i18n_closure_mode'; import {DEFAULT_LOCALE_ID, getPluralCase} from '../i18n/localization'; -import {SRCSET_ATTRS, URI_ATTRS, VALID_ATTRS, VALID_ELEMENTS, getTemplateContent} from '../sanitization/html_sanitizer'; +import {getTemplateContent, SRCSET_ATTRS, URI_ATTRS, VALID_ATTRS, VALID_ELEMENTS} from '../sanitization/html_sanitizer'; import {InertBodyHelper} from '../sanitization/inert_body'; import {_sanitizeUrl, sanitizeSrcset} from '../sanitization/url_sanitizer'; import {addAllToArray} from '../util/array_utils'; import {assertDataInRange, assertDefined, assertEqual} from '../util/assert'; + import {bindingUpdated} from './bindings'; import {attachPatchData} from './context_discovery'; import {setDelayProjection} from './instructions/all'; @@ -25,7 +26,7 @@ import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjecti import {RComment, RElement, RText} from './interfaces/renderer'; import {SanitizerFn} from './interfaces/sanitization'; import {isLContainer} from './interfaces/type_checks'; -import {HEADER_OFFSET, LView, RENDERER, TVIEW, TView, T_HOST} from './interfaces/view'; +import {HEADER_OFFSET, LView, RENDERER, T_HOST, TVIEW, TView} from './interfaces/view'; import {appendChild, applyProjection, createTextNode, nativeRemoveNode} from './node_manipulation'; import {getBindingIndex, getIsParent, getLView, getPreviousOrParentTNode, getTView, nextBindingIndex, setIsNotParent, setPreviousOrParentTNode} from './state'; import {renderStringify} from './util/misc_utils'; @@ -105,14 +106,14 @@ interface IcuCase { * @param pattern (sub)Pattern to be broken. * */ -function extractParts(pattern: string): (string | IcuExpression)[] { +function extractParts(pattern: string): (string|IcuExpression)[] { if (!pattern) { return []; } let prevPos = 0; const braceStack = []; - const results: (string | IcuExpression)[] = []; + const results: (string|IcuExpression)[] = []; const braces = /[{}]/g; // lastIndex doesn't get set to 0 so we have to. braces.lastIndex = 0; @@ -158,7 +159,7 @@ function extractParts(pattern: string): (string | IcuExpression)[] { */ function parseICUBlock(pattern: string): IcuExpression { const cases = []; - const values: (string | IcuExpression)[][] = []; + const values: (string|IcuExpression)[][] = []; let icuType = IcuType.plural; let mainBinding = 0; pattern = pattern.replace(ICU_BLOCK_REGEXP, function(str: string, binding: string, type: string) { @@ -219,7 +220,8 @@ function removeInnerTemplateTranslation(message: string): string { ngDevMode && assertEqual( inTemplate, false, - `Tag mismatch: unable to find the end of the sub-template in the translation "${message}"`); + `Tag mismatch: unable to find the end of the sub-template in the translation "${ + message}"`); res += message.substr(index); return res; @@ -241,7 +243,7 @@ function removeInnerTemplateTranslation(message: string): string { * external template and removes all sub-templates. */ export function getTranslationForTemplate(message: string, subTemplateIndex?: number) { - if (typeof subTemplateIndex !== 'number') { + if (isRootTemplateMessage(subTemplateIndex)) { // We want the root template message, ignore all sub-templates return removeInnerTemplateTranslation(message); } else { @@ -263,7 +265,7 @@ export function getTranslationForTemplate(message: string, subTemplateIndex?: nu */ function generateBindingUpdateOpCodes( str: string, destinationNode: number, attrName?: string, - sanitizeFn: SanitizerFn | null = null): I18nUpdateOpCodes { + sanitizeFn: SanitizerFn|null = null): I18nUpdateOpCodes { const updateOpCodes: I18nUpdateOpCodes = [null, null]; // Alloc space for mask and size const textParts = str.split(BINDING_REGEXP); let mask = 0; @@ -374,6 +376,10 @@ export function ɵɵi18nStart(index: number, message: string, subTemplateIndex?: // This is reset to 0 when `i18nStartFirstPass` is called. let i18nVarsCount: number; +function allocNodeIndex(startIndex: number): number { + return startIndex + i18nVarsCount++; +} + /** * See `i18nStart` above. */ @@ -407,81 +413,90 @@ function i18nStartFirstPass( const updateOpCodes: I18nUpdateOpCodes = []; const icuExpressions: TIcu[] = []; - const templateTranslation = getTranslationForTemplate(message, subTemplateIndex); - const msgParts = replaceNgsp(templateTranslation).split(PH_REGEXP); - for (let i = 0; i < msgParts.length; i++) { - let value = msgParts[i]; - if (i & 1) { - // Odd indexes are placeholders (elements and sub-templates) - if (value.charAt(0) === '/') { - // It is a closing tag - if (value.charAt(1) === TagType.ELEMENT) { - const phIndex = parseInt(value.substr(2), 10); - parentIndex = parentIndexStack[--parentIndexPointer]; - createOpCodes.push(phIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd); - } - } else { - const phIndex = parseInt(value.substr(1), 10); - const isElement = value.charAt(0) === TagType.ELEMENT; - // The value represents a placeholder that we move to the designated index. - // Note: positive indicies indicate that a TNode with a given index should also be marked as - // parent while executing `Select` instruction. - createOpCodes.push( - (isElement ? phIndex : ~phIndex) << I18nMutateOpCode.SHIFT_REF | - I18nMutateOpCode.Select, - parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild); - - if (isElement) { - parentIndexStack[++parentIndexPointer] = parentIndex = phIndex; - } - } - } else { - // Even indexes are text (including bindings & ICU expressions) - const parts = extractParts(value); - for (let j = 0; j < parts.length; j++) { - if (j & 1) { - // Odd indexes are ICU expressions - const icuExpression = parts[j] as IcuExpression; - - // Verify that ICU expression has the right shape. Translations might contain invalid - // constructions (while original messages were correct), so ICU parsing at runtime may not - // succeed (thus `icuExpression` remains a string). - if (typeof icuExpression !== 'object') { - throw new Error(`Unable to parse ICU expression in "${templateTranslation}" message.`); + if (message === '' && isRootTemplateMessage(subTemplateIndex)) { + // If top level translation is an empty string, do not invoke additional processing + // and just create op codes for empty text node instead. + createOpCodes.push( + message, allocNodeIndex(startIndex), + parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild); + } else { + const templateTranslation = getTranslationForTemplate(message, subTemplateIndex); + const msgParts = replaceNgsp(templateTranslation).split(PH_REGEXP); + for (let i = 0; i < msgParts.length; i++) { + let value = msgParts[i]; + if (i & 1) { + // Odd indexes are placeholders (elements and sub-templates) + if (value.charAt(0) === '/') { + // It is a closing tag + if (value.charAt(1) === TagType.ELEMENT) { + const phIndex = parseInt(value.substr(2), 10); + parentIndex = parentIndexStack[--parentIndexPointer]; + createOpCodes.push(phIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd); } - - // Create the comment node that will anchor the ICU expression - const icuNodeIndex = startIndex + i18nVarsCount++; + } else { + const phIndex = parseInt(value.substr(1), 10); + const isElement = value.charAt(0) === TagType.ELEMENT; + // The value represents a placeholder that we move to the designated index. + // Note: positive indicies indicate that a TNode with a given index should also be marked + // as parent while executing `Select` instruction. createOpCodes.push( - COMMENT_MARKER, ngDevMode ? `ICU ${icuNodeIndex}` : '', icuNodeIndex, + (isElement ? phIndex : ~phIndex) << I18nMutateOpCode.SHIFT_REF | + I18nMutateOpCode.Select, parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild); - // Update codes for the ICU expression - const mask = getBindingMask(icuExpression); - icuStart(icuExpressions, icuExpression, icuNodeIndex, icuNodeIndex); - // Since this is recursive, the last TIcu that was pushed is the one we want - const tIcuIndex = icuExpressions.length - 1; - updateOpCodes.push( - toMaskBit(icuExpression.mainBinding), // mask of the main binding - 3, // skip 3 opCodes if not changed - -1 - icuExpression.mainBinding, - icuNodeIndex << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.IcuSwitch, tIcuIndex, - mask, // mask of all the bindings of this ICU expression - 2, // skip 2 opCodes if not changed - icuNodeIndex << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.IcuUpdate, tIcuIndex); - } else if (parts[j] !== '') { - const text = parts[j] as string; - // Even indexes are text (including bindings) - const hasBinding = text.match(BINDING_REGEXP); - // Create text nodes - const textNodeIndex = startIndex + i18nVarsCount++; - createOpCodes.push( - // If there is a binding, the value will be set during update - hasBinding ? '' : text, textNodeIndex, - parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild); + if (isElement) { + parentIndexStack[++parentIndexPointer] = parentIndex = phIndex; + } + } + } else { + // Even indexes are text (including bindings & ICU expressions) + const parts = extractParts(value); + for (let j = 0; j < parts.length; j++) { + if (j & 1) { + // Odd indexes are ICU expressions + const icuExpression = parts[j] as IcuExpression; + + // Verify that ICU expression has the right shape. Translations might contain invalid + // constructions (while original messages were correct), so ICU parsing at runtime may + // not succeed (thus `icuExpression` remains a string). + if (typeof icuExpression !== 'object') { + throw new Error( + `Unable to parse ICU expression in "${templateTranslation}" message.`); + } - if (hasBinding) { - addAllToArray(generateBindingUpdateOpCodes(text, textNodeIndex), updateOpCodes); + // Create the comment node that will anchor the ICU expression + const icuNodeIndex = allocNodeIndex(startIndex); + createOpCodes.push( + COMMENT_MARKER, ngDevMode ? `ICU ${icuNodeIndex}` : '', icuNodeIndex, + parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild); + + // Update codes for the ICU expression + const mask = getBindingMask(icuExpression); + icuStart(icuExpressions, icuExpression, icuNodeIndex, icuNodeIndex); + // Since this is recursive, the last TIcu that was pushed is the one we want + const tIcuIndex = icuExpressions.length - 1; + updateOpCodes.push( + toMaskBit(icuExpression.mainBinding), // mask of the main binding + 3, // skip 3 opCodes if not changed + -1 - icuExpression.mainBinding, + icuNodeIndex << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.IcuSwitch, tIcuIndex, + mask, // mask of all the bindings of this ICU expression + 2, // skip 2 opCodes if not changed + icuNodeIndex << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.IcuUpdate, tIcuIndex); + } else if (parts[j] !== '') { + const text = parts[j] as string; + // Even indexes are text (including bindings) + const hasBinding = text.match(BINDING_REGEXP); + // Create text nodes + const textNodeIndex = allocNodeIndex(startIndex); + createOpCodes.push( + // If there is a binding, the value will be set during update + hasBinding ? '' : text, textNodeIndex, + parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild); + + if (hasBinding) { + addAllToArray(generateBindingUpdateOpCodes(text, textNodeIndex), updateOpCodes); + } } } } @@ -508,7 +523,7 @@ function i18nStartFirstPass( } function appendI18nNode( - tView: TView, tNode: TNode, parentTNode: TNode, previousTNode: TNode | null, + tView: TView, tNode: TNode, parentTNode: TNode, previousTNode: TNode|null, lView: LView): TNode { ngDevMode && ngDevMode.rendererMoveNode++; const nextNode = tNode.next; @@ -556,6 +571,10 @@ function appendI18nNode( return tNode; } +function isRootTemplateMessage(subTemplateIndex: number|undefined): subTemplateIndex is undefined { + return subTemplateIndex === undefined; +} + /** * Handles message string post-processing for internationalization. * @@ -577,7 +596,7 @@ function appendI18nNode( * @codeGenApi */ export function ɵɵi18nPostprocess( - message: string, replacements: {[key: string]: (string | string[])} = {}): string { + message: string, replacements: {[key: string]: (string|string[])} = {}): string { /** * Step 1: resolve all multi-value placeholders like [�#5�|�*1:1��#2:1�|�#4:1�] * @@ -660,7 +679,7 @@ export function ɵɵi18nPostprocess( if (!list.length) { throw new Error(`i18n postprocess: unmatched ICU - ${match} with key: ${key}`); } - return list.shift() !; + return list.shift()!; } return match; }); @@ -687,9 +706,10 @@ export function ɵɵi18nEnd(): void { * See `i18nEnd` above. */ function i18nEndFirstPass(tView: TView, lView: LView) { - ngDevMode && assertEqual( - getBindingIndex(), tView.bindingStartIndex, - 'i18nEnd should be called before any binding'); + ngDevMode && + assertEqual( + getBindingIndex(), tView.bindingStartIndex, + 'i18nEnd should be called before any binding'); const rootIndex = i18nIndexStack[i18nIndexStackPointer--]; const tI18n = tView.data[rootIndex + HEADER_OFFSET] as TI18n; @@ -709,8 +729,9 @@ function i18nEndFirstPass(tView: TView, lView: LView) { } // Check if an element has any local refs and skip them const tNode = getTNode(tView, index); - if (tNode && (tNode.type === TNodeType.Container || tNode.type === TNodeType.Element || - tNode.type === TNodeType.ElementContainer) && + if (tNode && + (tNode.type === TNodeType.Container || tNode.type === TNodeType.Element || + tNode.type === TNodeType.ElementContainer) && tNode.localNames !== null) { // Divide by 2 to get the number of local refs, // since they are stored as an array that also includes directive indexes, @@ -725,8 +746,8 @@ function i18nEndFirstPass(tView: TView, lView: LView) { * Creates and stores the dynamic TNode, and unhooks it from the tree for now. */ function createDynamicNodeAtIndex( - tView: TView, lView: LView, index: number, type: TNodeType, native: RElement | RText | null, - name: string | null): TElementNode|TIcuContainerNode { + tView: TView, lView: LView, index: number, type: TNodeType, native: RElement|RText|null, + name: string|null): TElementNode|TIcuContainerNode { const previousOrParentTNode = getPreviousOrParentTNode(); ngDevMode && assertDataInRange(lView, index + HEADER_OFFSET); lView[index + HEADER_OFFSET] = native; @@ -766,16 +787,16 @@ function readCreateOpCodes( if (destinationNodeIndex === index) { // If the destination node is `i18nStart`, we don't have a // top-level node and we should use the host node instead - destinationTNode = lView[T_HOST] !; + destinationTNode = lView[T_HOST]!; } else { destinationTNode = getTNode(tView, destinationNodeIndex); } ngDevMode && assertDefined( - currentTNode !, + currentTNode!, `You need to create or select a node before you can insert it into the DOM`); previousTNode = - appendI18nNode(tView, currentTNode !, destinationTNode, previousTNode, lView); + appendI18nNode(tView, currentTNode!, destinationTNode, previousTNode, lView); break; case I18nMutateOpCode.Select: // Negative indicies indicate that a given TNode is a sibling node, not a parent node @@ -811,9 +832,10 @@ function readCreateOpCodes( case COMMENT_MARKER: const commentValue = createOpCodes[++i] as string; const commentNodeIndex = createOpCodes[++i] as number; - ngDevMode && assertEqual( - typeof commentValue, 'string', - `Expected "${commentValue}" to be a comment node value`); + ngDevMode && + assertEqual( + typeof commentValue, 'string', + `Expected "${commentValue}" to be a comment node value`); const commentRNode = renderer.createComment(commentValue); ngDevMode && ngDevMode.rendererCreateComment++; previousTNode = currentTNode; @@ -828,9 +850,10 @@ function readCreateOpCodes( case ELEMENT_MARKER: const tagNameValue = createOpCodes[++i] as string; const elementNodeIndex = createOpCodes[++i] as number; - ngDevMode && assertEqual( - typeof tagNameValue, 'string', - `Expected "${tagNameValue}" to be an element node tag name`); + ngDevMode && + assertEqual( + typeof tagNameValue, 'string', + `Expected "${tagNameValue}" to be an element node tag name`); const elementRNode = renderer.createElement(tagNameValue); ngDevMode && ngDevMode.rendererCreateElement++; previousTNode = currentTNode; @@ -850,7 +873,7 @@ function readCreateOpCodes( } function readUpdateOpCodes( - updateOpCodes: I18nUpdateOpCodes, icus: TIcu[] | null, bindingsStartIndex: number, + updateOpCodes: I18nUpdateOpCodes, icus: TIcu[]|null, bindingsStartIndex: number, changeMask: number, tView: TView, lView: LView, bypassCheckBit = false) { let caseCreated = false; for (let i = 0; i < updateOpCodes.length; i++) { @@ -887,7 +910,7 @@ function readUpdateOpCodes( break; case I18nUpdateOpCode.IcuSwitch: tIcuIndex = updateOpCodes[++j] as number; - tIcu = icus ![tIcuIndex]; + tIcu = icus![tIcuIndex]; icuTNode = getTNode(tView, nodeIndex) as TIcuContainerNode; // If there is an active case, delete the old nodes if (icuTNode.activeCaseIndex !== null) { @@ -910,7 +933,7 @@ function readUpdateOpCodes( const activeIndex = nestedIcuTNode.activeCaseIndex; if (activeIndex !== null) { const nestedIcuTIndex = removeOpCode >>> I18nMutateOpCode.SHIFT_REF; - const nestedTIcu = icus ![nestedIcuTIndex]; + const nestedTIcu = icus![nestedIcuTIndex]; addAllToArray(nestedTIcu.remove[activeIndex], removeCodes); } break; @@ -929,7 +952,7 @@ function readUpdateOpCodes( break; case I18nUpdateOpCode.IcuUpdate: tIcuIndex = updateOpCodes[++j] as number; - tIcu = icus ![tIcuIndex]; + tIcu = icus![tIcuIndex]; icuTNode = getTNode(tView, nodeIndex) as TIcuContainerNode; if (icuTNode.activeCaseIndex !== null) { readUpdateOpCodes( @@ -1215,7 +1238,7 @@ function parseIcuCase( if (!inertBodyElement) { throw new Error('Unable to generate inert body element'); } - const wrapper = getTemplateContent(inertBodyElement !) as Element || inertBodyElement; + const wrapper = getTemplateContent(inertBodyElement!) as Element || inertBodyElement; const opCodes: IcuCase = {vars: 0, childIcus: [], create: [], remove: [], update: []}; parseNodes(wrapper.firstChild, opCodes, parentIndex, nestedIcus, tIcus, expandoStartIndex); return opCodes; @@ -1234,7 +1257,7 @@ const NESTED_ICU = /�(\d+)�/; * @param expandoStartIndex Expando start index for the current ICU expression */ function parseNodes( - currentNode: Node | null, icuCase: IcuCase, parentIndex: number, nestedIcus: IcuExpression[], + currentNode: Node|null, icuCase: IcuCase, parentIndex: number, nestedIcus: IcuExpression[], tIcus: TIcu[], expandoStartIndex: number) { if (currentNode) { const nestedIcusToCreate: [IcuExpression, number][] = []; @@ -1254,7 +1277,7 @@ function parseNodes( parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild); const elAttrs = element.attributes; for (let i = 0; i < elAttrs.length; i++) { - const attr = elAttrs.item(i) !; + const attr = elAttrs.item(i)!; const lowerAttrName = attr.name.toLowerCase(); const hasBinding = !!attr.value.match(BINDING_REGEXP); // we assume the input string is safe, unless it's using a binding @@ -1276,8 +1299,8 @@ function parseNodes( } } else { ngDevMode && - console.warn( - `WARNING: ignoring unsafe attribute value ${lowerAttrName} on element ${tagName} (see http://g.co/ng/security#xss)`); + console.warn(`WARNING: ignoring unsafe attribute value ${ + lowerAttrName} on element ${tagName} (see http://g.co/ng/security#xss)`); } } else { icuCase.create.push( @@ -1324,7 +1347,7 @@ function parseNodes( // We do not handle any other type of element icuCase.vars--; } - currentNode = nextNode !; + currentNode = nextNode!; } for (let i = 0; i < nestedIcusToCreate.length; i++) { diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index d88585000efae..cea4adee29992 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -16,13 +16,14 @@ import {getComponent, getDirectives, getHostElement, getRenderedText} from './ut export {ComponentFactory, ComponentFactoryResolver, ComponentRef, injectComponentFactoryResolver} from './component_ref'; export {ɵɵgetFactoryOf, ɵɵgetInheritedFactory} from './di'; - +export {getLocaleId, setLocaleId, ɵɵi18n, ɵɵi18nApply, ɵɵi18nAttributes, ɵɵi18nEnd, ɵɵi18nExp, ɵɵi18nPostprocess, ɵɵi18nStart,} from './i18n'; // clang-format off export { detectChanges, markDirty, store, tick, + ɵɵadvance, ɵɵattribute, ɵɵattributeInterpolate1, @@ -54,7 +55,6 @@ export { ɵɵcontainerRefreshStart, ɵɵdirectiveInject, - ɵɵinvalidFactory, ɵɵelement, ɵɵelementContainer, @@ -69,7 +69,9 @@ export { ɵɵembeddedViewStart, ɵɵgetCurrentView, + ɵɵhostProperty, ɵɵinjectAttribute, + ɵɵinvalidFactory, ɵɵlistener, @@ -81,7 +83,6 @@ export { ɵɵprojection, ɵɵprojectionDef, - ɵɵhostProperty, ɵɵproperty, ɵɵpropertyInterpolate, ɵɵpropertyInterpolate1, @@ -98,7 +99,6 @@ export { // TODO: remove `select` once we've refactored all of the tests not to use it. ɵɵselect, - ɵɵadvance, ɵɵstyleMap, ɵɵstyleMapInterpolate1, ɵɵstyleMapInterpolate2, @@ -139,37 +139,14 @@ export { ɵɵupdateSyntheticHostBinding, } from './instructions/all'; export {RenderFlags} from './interfaces/definition'; -export {CssSelectorList, ProjectionSlots} from './interfaces/projection'; - -export { - ɵɵrestoreView, - - ɵɵenableBindings, - ɵɵdisableBindings, -} from './state'; - -export { - ɵɵi18n, - ɵɵi18nAttributes, - ɵɵi18nExp, - ɵɵi18nStart, - ɵɵi18nEnd, - ɵɵi18nApply, - ɵɵi18nPostprocess, - getLocaleId, - setLocaleId, -} from './i18n'; - -export {NgModuleFactory, NgModuleRef, NgModuleType} from './ng_module_ref'; - export { AttributeMarker } from './interfaces/node'; - +export {CssSelectorList, ProjectionSlots} from './interfaces/projection'; export { setClassMetadata, } from './metadata'; - +export {NgModuleFactory, NgModuleRef, NgModuleType} from './ng_module_ref'; export { ɵɵpipe, ɵɵpipeBind1, @@ -178,16 +155,6 @@ export { ɵɵpipeBind4, ɵɵpipeBindV, } from './pipe'; - -export { - ɵɵqueryRefresh, - ɵɵviewQuery, - ɵɵstaticViewQuery, - ɵɵloadQuery, - ɵɵcontentQuery, - ɵɵstaticContentQuery -} from './query'; - export { ɵɵpureFunction0, ɵɵpureFunction1, @@ -200,41 +167,51 @@ export { ɵɵpureFunction8, ɵɵpureFunctionV, } from './pure_function'; +export { + ɵɵcontentQuery, + ɵɵloadQuery, + ɵɵqueryRefresh, + ɵɵstaticContentQuery +, + ɵɵstaticViewQuery, + ɵɵviewQuery} from './query'; +export { + ɵɵdisableBindings, -export {ɵɵtemplateRefExtractor, ɵɵinjectPipeChangeDetectorRef} from './view_engine_compatibility_prebound'; - -export {ɵɵresolveWindow, ɵɵresolveDocument, ɵɵresolveBody} from './util/misc_utils'; - + ɵɵenableBindings, + ɵɵrestoreView, +} from './state'; +export {NO_CHANGE} from './tokens'; +export { ɵɵresolveBody, ɵɵresolveDocument,ɵɵresolveWindow} from './util/misc_utils'; +export { ɵɵinjectPipeChangeDetectorRef,ɵɵtemplateRefExtractor} from './view_engine_compatibility_prebound'; // clang-format on export { ComponentDef, - ɵɵComponentDefWithMeta, - ɵɵFactoryDef, ComponentTemplate, ComponentType, DirectiveDef, - ɵɵDirectiveDefWithMeta, DirectiveType, - ɵɵNgOnChangesFeature, - ɵɵCopyDefinitionFeature, - ɵɵInheritDefinitionFeature, - ɵɵProvidersFeature, - PipeDef, - ɵɵPipeDefWithMeta, + getComponent, + getDirectives, + getHostElement, + getRenderedText, LifecycleHooksFeature, + PipeDef, + renderComponent, + whenRendered, + ɵɵComponentDefWithMeta, + ɵɵCopyDefinitionFeature, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineNgModule, ɵɵdefinePipe, - getHostElement, - getComponent, - getDirectives, - getRenderedText, - renderComponent, + ɵɵDirectiveDefWithMeta, + ɵɵFactoryDef, + ɵɵInheritDefinitionFeature, + ɵɵNgOnChangesFeature, + ɵɵPipeDefWithMeta, + ɵɵProvidersFeature, ɵɵsetComponentScope, ɵɵsetNgModuleScope, - whenRendered, }; - -export {NO_CHANGE} from './tokens'; diff --git a/packages/core/src/render3/instructions/advance.ts b/packages/core/src/render3/instructions/advance.ts index 551de37caf1fa..a4eabdd557bd6 100644 --- a/packages/core/src/render3/instructions/advance.ts +++ b/packages/core/src/render3/instructions/advance.ts @@ -19,21 +19,21 @@ import {getCheckNoChangesMode, getLView, getSelectedIndex, getTView, setSelected * * ```ts * (rf: RenderFlags, ctx: any) => { - * if (rf & 1) { - * text(0, 'Hello'); - * text(1, 'Goodbye') - * element(2, 'div'); - * } - * if (rf & 2) { - * advance(2); // Advance twice to the <div>. - * property('title', 'test'); - * } - * } - * ``` - * @param delta Number of elements to advance forwards by. - * - * @codeGenApi - */ + * if (rf & 1) { + * text(0, 'Hello'); + * text(1, 'Goodbye') + * element(2, 'div'); + * } + * if (rf & 2) { + * advance(2); // Advance twice to the <div>. + * property('title', 'test'); + * } + * } + * ``` + * @param delta Number of elements to advance forwards by. + * + * @codeGenApi + */ export function ɵɵadvance(delta: number): void { ngDevMode && assertGreaterThan(delta, 0, 'Can only advance forward'); selectIndexInternal(getTView(), getLView(), getSelectedIndex() + delta, getCheckNoChangesMode()); diff --git a/packages/core/src/render3/instructions/attribute.ts b/packages/core/src/render3/instructions/attribute.ts index a3a2bfab8fba8..380eec1ba23f3 100644 --- a/packages/core/src/render3/instructions/attribute.ts +++ b/packages/core/src/render3/instructions/attribute.ts @@ -26,7 +26,7 @@ import {elementAttributeInternal, storePropertyBindingMetadata} from './shared'; * @codeGenApi */ export function ɵɵattribute( - name: string, value: any, sanitizer?: SanitizerFn | null, + name: string, value: any, sanitizer?: SanitizerFn|null, namespace?: string): typeof ɵɵattribute { const lView = getLView(); const bindingIndex = nextBindingIndex(); diff --git a/packages/core/src/render3/instructions/attribute_interpolation.ts b/packages/core/src/render3/instructions/attribute_interpolation.ts index 96f7e8b01e1a4..0de336c78af0f 100644 --- a/packages/core/src/render3/instructions/attribute_interpolation.ts +++ b/packages/core/src/render3/instructions/attribute_interpolation.ts @@ -130,9 +130,10 @@ export function ɵɵattributeInterpolate3( if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); - ngDevMode && storePropertyBindingMetadata( - getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 3, prefix, i0, - i1, suffix); + ngDevMode && + storePropertyBindingMetadata( + getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 3, prefix, i0, i1, + suffix); } return ɵɵattributeInterpolate3; } @@ -177,9 +178,10 @@ export function ɵɵattributeInterpolate4( if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); - ngDevMode && storePropertyBindingMetadata( - getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 4, prefix, i0, - i1, i2, suffix); + ngDevMode && + storePropertyBindingMetadata( + getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 4, prefix, i0, i1, i2, + suffix); } return ɵɵattributeInterpolate4; } @@ -227,9 +229,10 @@ export function ɵɵattributeInterpolate5( if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); - ngDevMode && storePropertyBindingMetadata( - getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 5, prefix, i0, - i1, i2, i3, suffix); + ngDevMode && + storePropertyBindingMetadata( + getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 5, prefix, i0, i1, i2, + i3, suffix); } return ɵɵattributeInterpolate5; } @@ -279,9 +282,10 @@ export function ɵɵattributeInterpolate6( if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); - ngDevMode && storePropertyBindingMetadata( - getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 6, prefix, i0, - i1, i2, i3, i4, suffix); + ngDevMode && + storePropertyBindingMetadata( + getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 6, prefix, i0, i1, i2, + i3, i4, suffix); } return ɵɵattributeInterpolate6; } @@ -333,9 +337,10 @@ export function ɵɵattributeInterpolate7( if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); - ngDevMode && storePropertyBindingMetadata( - getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 7, prefix, i0, - i1, i2, i3, i4, i5, suffix); + ngDevMode && + storePropertyBindingMetadata( + getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 7, prefix, i0, i1, i2, + i3, i4, i5, suffix); } return ɵɵattributeInterpolate7; } @@ -389,9 +394,10 @@ export function ɵɵattributeInterpolate8( if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); - ngDevMode && storePropertyBindingMetadata( - getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 8, prefix, i0, - i1, i2, i3, i4, i5, i6, suffix); + ngDevMode && + storePropertyBindingMetadata( + getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 8, prefix, i0, i1, i2, + i3, i4, i5, i6, suffix); } return ɵɵattributeInterpolate8; } diff --git a/packages/core/src/render3/instructions/change_detection.ts b/packages/core/src/render3/instructions/change_detection.ts index 3a78623e3c91e..c667cfac7c67d 100644 --- a/packages/core/src/render3/instructions/change_detection.ts +++ b/packages/core/src/render3/instructions/change_detection.ts @@ -35,7 +35,7 @@ export function detectChanges(component: {}): void { */ export function markDirty(component: {}): void { ngDevMode && assertDefined(component, 'component'); - const rootView = markViewDirty(getComponentViewByInstance(component)) !; + const rootView = markViewDirty(getComponentViewByInstance(component))!; ngDevMode && assertDefined(rootView[CONTEXT], 'rootContext should be defined'); scheduleTick(rootView[CONTEXT] as RootContext, RootContextFlags.DetectChanges); diff --git a/packages/core/src/render3/instructions/container.ts b/packages/core/src/render3/instructions/container.ts index 70c0ce0cd888c..4685b665c2d14 100644 --- a/packages/core/src/render3/instructions/container.ts +++ b/packages/core/src/render3/instructions/container.ts @@ -13,11 +13,12 @@ import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/c import {ComponentTemplate} from '../interfaces/definition'; import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeType, TViewNode} from '../interfaces/node'; import {isDirectiveHost} from '../interfaces/type_checks'; -import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, RENDERER, TView, TViewType, T_HOST} from '../interfaces/view'; +import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, RENDERER, T_HOST, TView, TViewType} from '../interfaces/view'; import {assertNodeType} from '../node_assert'; import {appendChild, removeView} from '../node_manipulation'; import {getBindingIndex, getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, getTView, setIsNotParent, setPreviousOrParentTNode} from '../state'; import {getConstant, getLContainerActiveIndex, load} from '../util/view_utils'; + import {addToViewTree, createDirectivesInstances, createLContainer, createTNode, createTView, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared'; @@ -45,9 +46,9 @@ export function ɵɵcontainer(index: number): void { } function templateFirstCreatePass( - index: number, tView: TView, lView: LView, templateFn: ComponentTemplate<any>| null, - decls: number, vars: number, tagName?: string | null, attrsIndex?: number | null, - localRefsIndex?: number | null): TContainerNode { + index: number, tView: TView, lView: LView, templateFn: ComponentTemplate<any>|null, + decls: number, vars: number, tagName?: string|null, attrsIndex?: number|null, + localRefsIndex?: number|null): TContainerNode { ngDevMode && assertFirstCreatePass(tView); ngDevMode && ngDevMode.firstCreatePass++; const tViewConsts = tView.consts; @@ -94,8 +95,8 @@ function templateFirstCreatePass( * @codeGenApi */ export function ɵɵtemplate( - index: number, templateFn: ComponentTemplate<any>| null, decls: number, vars: number, - tagName?: string | null, attrsIndex?: number | null, localRefsIndex?: number | null, + index: number, templateFn: ComponentTemplate<any>|null, decls: number, vars: number, + tagName?: string|null, attrsIndex?: number|null, localRefsIndex?: number|null, localRefExtractor?: LocalRefExtractor) { const lView = getLView(); const tView = getTView(); @@ -172,7 +173,7 @@ export function ɵɵcontainerRefreshEnd(): void { } else { ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.View); ngDevMode && assertHasParent(previousOrParentTNode); - previousOrParentTNode = previousOrParentTNode.parent !; + previousOrParentTNode = previousOrParentTNode.parent!; setPreviousOrParentTNode(previousOrParentTNode, false); } @@ -188,11 +189,12 @@ export function ɵɵcontainerRefreshEnd(): void { } function containerInternal( - tView: TView, lView: LView, nodeIndex: number, tagName: string | null, - attrs: TAttributes | null): TContainerNode { - ngDevMode && assertEqual( - getBindingIndex(), tView.bindingStartIndex, - 'container nodes should be created before any bindings'); + tView: TView, lView: LView, nodeIndex: number, tagName: string|null, + attrs: TAttributes|null): TContainerNode { + ngDevMode && + assertEqual( + getBindingIndex(), tView.bindingStartIndex, + 'container nodes should be created before any bindings'); const adjustedIndex = nodeIndex + HEADER_OFFSET; ngDevMode && assertDataInRange(lView, nodeIndex + HEADER_OFFSET); diff --git a/packages/core/src/render3/instructions/di.ts b/packages/core/src/render3/instructions/di.ts index 71a2f8b6d910b..5c0be8f07962a 100644 --- a/packages/core/src/render3/instructions/di.ts +++ b/packages/core/src/render3/instructions/di.ts @@ -37,10 +37,10 @@ import {getLView, getPreviousOrParentTNode} from '../state'; * * @codeGenApi */ -export function ɵɵdirectiveInject<T>(token: Type<T>| InjectionToken<T>): T; -export function ɵɵdirectiveInject<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags): T; +export function ɵɵdirectiveInject<T>(token: Type<T>|InjectionToken<T>): T; +export function ɵɵdirectiveInject<T>(token: Type<T>|InjectionToken<T>, flags: InjectFlags): T; export function ɵɵdirectiveInject<T>( - token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null { + token: Type<T>|InjectionToken<T>, flags = InjectFlags.Default): T|null { const lView = getLView(); // Fall back to inject() if view hasn't been created. This situation can happen in tests // if inject utilities are used before bootstrapping. diff --git a/packages/core/src/render3/instructions/element.ts b/packages/core/src/render3/instructions/element.ts index 52eda6249bcef..2810fa4073da6 100644 --- a/packages/core/src/render3/instructions/element.ts +++ b/packages/core/src/render3/instructions/element.ts @@ -10,23 +10,24 @@ import {assertDataInRange, assertDefined, assertEqual} from '../../util/assert'; import {assertFirstCreatePass, assertHasParent} from '../assert'; import {attachPatchData} from '../context_discovery'; import {registerPostOrderHooks} from '../hooks'; -import {TAttributes, TElementNode, TNode, TNodeType, hasClassInput, hasStyleInput} from '../interfaces/node'; +import {hasClassInput, hasStyleInput, TAttributes, TElementNode, TNode, TNodeType} from '../interfaces/node'; import {RElement} from '../interfaces/renderer'; import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks'; -import {HEADER_OFFSET, LView, RENDERER, TVIEW, TView, T_HOST} from '../interfaces/view'; +import {HEADER_OFFSET, LView, RENDERER, T_HOST, TVIEW, TView} from '../interfaces/view'; import {assertNodeType} from '../node_assert'; import {appendChild, writeDirectClass, writeDirectStyle} from '../node_manipulation'; import {decreaseElementDepthCount, getBindingIndex, getElementDepthCount, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getTView, increaseElementDepthCount, setIsNotParent, setPreviousOrParentTNode} from '../state'; import {computeStaticStyling} from '../styling/static_styling'; import {setUpAttributes} from '../util/attrs_utils'; import {getConstant} from '../util/view_utils'; + import {setDirectiveInputsWhichShadowsStyling} from './property'; import {createDirectivesInstances, elementCreate, executeContentQueries, getOrCreateTNode, matchingSchemas, resolveDirectives, saveResolvedLocalsInData} from './shared'; function elementStartFirstCreatePass( index: number, tView: TView, lView: LView, native: RElement, name: string, - attrsIndex?: number | null, localRefsIndex?: number): TElementNode { + attrsIndex?: number|null, localRefsIndex?: number): TElementNode { ngDevMode && assertFirstCreatePass(tView); ngDevMode && ngDevMode.firstCreatePass++; @@ -64,14 +65,15 @@ function elementStartFirstCreatePass( * @codeGenApi */ export function ɵɵelementStart( - index: number, name: string, attrsIndex?: number | null, localRefsIndex?: number): void { + index: number, name: string, attrsIndex?: number|null, localRefsIndex?: number): void { const lView = getLView(); const tView = getTView(); const adjustedIndex = HEADER_OFFSET + index; - ngDevMode && assertEqual( - getBindingIndex(), tView.bindingStartIndex, - 'elements should be created before any bindings'); + ngDevMode && + assertEqual( + getBindingIndex(), tView.bindingStartIndex, + 'elements should be created before any bindings'); ngDevMode && ngDevMode.rendererCreateElement++; ngDevMode && assertDataInRange(lView, adjustedIndex); @@ -128,7 +130,7 @@ export function ɵɵelementEnd(): void { setIsNotParent(); } else { ngDevMode && assertHasParent(getPreviousOrParentTNode()); - previousOrParentTNode = previousOrParentTNode.parent !; + previousOrParentTNode = previousOrParentTNode.parent!; setPreviousOrParentTNode(previousOrParentTNode, false); } @@ -142,7 +144,7 @@ export function ɵɵelementEnd(): void { if (tView.firstCreatePass) { registerPostOrderHooks(tView, previousOrParentTNode); if (isContentQueryHost(previousOrParentTNode)) { - tView.queries !.elementEnd(previousOrParentTNode); + tView.queries!.elementEnd(previousOrParentTNode); } } @@ -166,7 +168,7 @@ export function ɵɵelementEnd(): void { * @codeGenApi */ export function ɵɵelement( - index: number, name: string, attrsIndex?: number | null, localRefsIndex?: number): void { + index: number, name: string, attrsIndex?: number|null, localRefsIndex?: number): void { ɵɵelementStart(index, name, attrsIndex, localRefsIndex); ɵɵelementEnd(); } @@ -198,11 +200,11 @@ function warnAboutUnknownElement( if (isUnknown && !matchingSchemas(tView, lView, tagName)) { let warning = `'${tagName}' is not a known element:\n`; - warning += - `1. If '${tagName}' is an Angular component, then verify that it is part of this module.\n`; + warning += `1. If '${ + tagName}' is an Angular component, then verify that it is part of this module.\n`; if (tagName && tagName.indexOf('-') > -1) { - warning += - `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`; + warning += `2. If '${ + tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`; } else { warning += `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`; diff --git a/packages/core/src/render3/instructions/element_container.ts b/packages/core/src/render3/instructions/element_container.ts index 54f1cd9dd31b9..d7e3549c6175b 100644 --- a/packages/core/src/render3/instructions/element_container.ts +++ b/packages/core/src/render3/instructions/element_container.ts @@ -11,7 +11,7 @@ import {attachPatchData} from '../context_discovery'; import {registerPostOrderHooks} from '../hooks'; import {TAttributes, TElementContainerNode, TNodeType} from '../interfaces/node'; import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks'; -import {HEADER_OFFSET, LView, RENDERER, TView, T_HOST} from '../interfaces/view'; +import {HEADER_OFFSET, LView, RENDERER, T_HOST, TView} from '../interfaces/view'; import {assertNodeType} from '../node_assert'; import {appendChild} from '../node_manipulation'; import {getBindingIndex, getIsParent, getLView, getPreviousOrParentTNode, getTView, setIsNotParent, setPreviousOrParentTNode} from '../state'; @@ -21,7 +21,7 @@ import {getConstant} from '../util/view_utils'; import {createDirectivesInstances, executeContentQueries, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared'; function elementContainerStartFirstCreatePass( - index: number, tView: TView, lView: LView, attrsIndex?: number | null, + index: number, tView: TView, lView: LView, attrsIndex?: number|null, localRefsIndex?: number): TElementContainerNode { ngDevMode && ngDevMode.firstCreatePass++; @@ -61,15 +61,16 @@ function elementContainerStartFirstCreatePass( * @codeGenApi */ export function ɵɵelementContainerStart( - index: number, attrsIndex?: number | null, localRefsIndex?: number): void { + index: number, attrsIndex?: number|null, localRefsIndex?: number): void { const lView = getLView(); const tView = getTView(); const adjustedIndex = index + HEADER_OFFSET; ngDevMode && assertDataInRange(lView, adjustedIndex); - ngDevMode && assertEqual( - getBindingIndex(), tView.bindingStartIndex, - 'element containers should be created before any bindings'); + ngDevMode && + assertEqual( + getBindingIndex(), tView.bindingStartIndex, + 'element containers should be created before any bindings'); const tNode = tView.firstCreatePass ? elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) : @@ -104,7 +105,7 @@ export function ɵɵelementContainerEnd(): void { setIsNotParent(); } else { ngDevMode && assertHasParent(previousOrParentTNode); - previousOrParentTNode = previousOrParentTNode.parent !; + previousOrParentTNode = previousOrParentTNode.parent!; setPreviousOrParentTNode(previousOrParentTNode, false); } @@ -113,7 +114,7 @@ export function ɵɵelementContainerEnd(): void { if (tView.firstCreatePass) { registerPostOrderHooks(tView, previousOrParentTNode); if (isContentQueryHost(previousOrParentTNode)) { - tView.queries !.elementEnd(previousOrParentTNode); + tView.queries!.elementEnd(previousOrParentTNode); } } } @@ -129,7 +130,7 @@ export function ɵɵelementContainerEnd(): void { * @codeGenApi */ export function ɵɵelementContainer( - index: number, attrsIndex?: number | null, localRefsIndex?: number): void { + index: number, attrsIndex?: number|null, localRefsIndex?: number): void { ɵɵelementContainerStart(index, attrsIndex, localRefsIndex); ɵɵelementContainerEnd(); } diff --git a/packages/core/src/render3/instructions/embedded_view.ts b/packages/core/src/render3/instructions/embedded_view.ts index 777d22911baa2..971d04214f954 100644 --- a/packages/core/src/render3/instructions/embedded_view.ts +++ b/packages/core/src/render3/instructions/embedded_view.ts @@ -11,7 +11,7 @@ import {assertLContainerOrUndefined} from '../assert'; import {ACTIVE_INDEX, ActiveIndexFlag, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container'; import {RenderFlags} from '../interfaces/definition'; import {TContainerNode, TNodeType} from '../interfaces/node'; -import {CONTEXT, LView, LViewFlags, PARENT, TVIEW, TView, TViewType, T_HOST} from '../interfaces/view'; +import {CONTEXT, LView, LViewFlags, PARENT, T_HOST, TVIEW, TView, TViewType} from '../interfaces/view'; import {assertNodeType} from '../node_assert'; import {insertView, removeView} from '../node_manipulation'; import {enterView, getIsParent, getLView, getPreviousOrParentTNode, getTView, leaveView, setIsParent, setPreviousOrParentTNode} from '../state'; @@ -34,7 +34,7 @@ export function ɵɵembeddedViewStart(viewBlockId: number, decls: number, vars: const previousOrParentTNode = getPreviousOrParentTNode(); // The previous node can be a view node if we are processing an inline for loop const containerTNode = previousOrParentTNode.type === TNodeType.View ? - previousOrParentTNode.parent ! : + previousOrParentTNode.parent! : previousOrParentTNode; const lContainer = lView[containerTNode.index] as LContainer; @@ -141,5 +141,5 @@ export function ɵɵembeddedViewEnd(): void { const lContainer = lView[PARENT] as LContainer; ngDevMode && assertLContainerOrUndefined(lContainer); leaveView(); - setPreviousOrParentTNode(viewHost !, false); + setPreviousOrParentTNode(viewHost!, false); } diff --git a/packages/core/src/render3/instructions/host_property.ts b/packages/core/src/render3/instructions/host_property.ts index cc662152dcc16..800519c932450 100644 --- a/packages/core/src/render3/instructions/host_property.ts +++ b/packages/core/src/render3/instructions/host_property.ts @@ -28,7 +28,7 @@ import {elementPropertyInternal, loadComponentRenderer, storePropertyBindingMeta * @codeGenApi */ export function ɵɵhostProperty<T>( - propName: string, value: T, sanitizer?: SanitizerFn | null): typeof ɵɵhostProperty { + propName: string, value: T, sanitizer?: SanitizerFn|null): typeof ɵɵhostProperty { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, value)) { @@ -63,8 +63,8 @@ export function ɵɵhostProperty<T>( * @codeGenApi */ export function ɵɵupdateSyntheticHostBinding<T>( - propName: string, value: T | NO_CHANGE, - sanitizer?: SanitizerFn | null): typeof ɵɵupdateSyntheticHostBinding { + propName: string, value: T|NO_CHANGE, + sanitizer?: SanitizerFn|null): typeof ɵɵupdateSyntheticHostBinding { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, value)) { diff --git a/packages/core/src/render3/instructions/interpolation.ts b/packages/core/src/render3/instructions/interpolation.ts index 21ef666c231aa..8b775b6691ece 100644 --- a/packages/core/src/render3/instructions/interpolation.ts +++ b/packages/core/src/render3/instructions/interpolation.ts @@ -102,10 +102,9 @@ export function interpolation4( const different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); incrementBindingIndex(4); - return different ? - prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + - renderStringify(v3) + suffix : - NO_CHANGE; + return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + + renderStringify(v2) + i2 + renderStringify(v3) + suffix : + NO_CHANGE; } /** @@ -119,10 +118,9 @@ export function interpolation5( different = bindingUpdated(lView, bindingIndex + 4, v4) || different; incrementBindingIndex(5); - return different ? - prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + - renderStringify(v3) + i3 + renderStringify(v4) + suffix : - NO_CHANGE; + return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + + renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + suffix : + NO_CHANGE; } /** @@ -154,11 +152,10 @@ export function interpolation7( different = bindingUpdated3(lView, bindingIndex + 4, v4, v5, v6) || different; incrementBindingIndex(7); - return different ? - prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + - renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + i5 + - renderStringify(v6) + suffix : - NO_CHANGE; + return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + + renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 + + renderStringify(v5) + i5 + renderStringify(v6) + suffix : + NO_CHANGE; } /** @@ -173,9 +170,8 @@ export function interpolation8( different = bindingUpdated4(lView, bindingIndex + 4, v4, v5, v6, v7) || different; incrementBindingIndex(8); - return different ? - prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + - renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + i5 + - renderStringify(v6) + i6 + renderStringify(v7) + suffix : - NO_CHANGE; + return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + + renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 + + renderStringify(v5) + i5 + renderStringify(v6) + i6 + renderStringify(v7) + suffix : + NO_CHANGE; } diff --git a/packages/core/src/render3/instructions/listener.ts b/packages/core/src/render3/instructions/listener.ts index 981924a988033..dc7cc4faefa18 100644 --- a/packages/core/src/render3/instructions/listener.ts +++ b/packages/core/src/render3/instructions/listener.ts @@ -11,12 +11,13 @@ import {assertDataInRange} from '../../util/assert'; import {isObservable} from '../../util/lang'; import {EMPTY_OBJ} from '../empty'; import {PropertyAliasValue, TNode, TNodeFlags, TNodeType} from '../interfaces/node'; -import {GlobalTargetResolver, RElement, Renderer3, isProceduralRenderer} from '../interfaces/renderer'; +import {GlobalTargetResolver, isProceduralRenderer, RElement, Renderer3} from '../interfaces/renderer'; import {isDirectiveHost} from '../interfaces/type_checks'; import {CLEANUP, FLAGS, LView, LViewFlags, RENDERER, TView} from '../interfaces/view'; import {assertNodeOfPossibleTypes} from '../node_assert'; import {getLView, getPreviousOrParentTNode, getTView} from '../state'; import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils'; + import {getLCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared'; @@ -47,26 +48,26 @@ export function ɵɵlistener( } /** -* Registers a synthetic host listener (e.g. `(@foo.start)`) on a component. -* -* This instruction is for compatibility purposes and is designed to ensure that a -* synthetic host listener (e.g. `@HostListener('@foo.start')`) properly gets rendered -* in the component's renderer. Normally all host listeners are evaluated with the -* parent component's renderer, but, in the case of animation @triggers, they need -* to be evaluated with the sub component's renderer (because that's where the -* animation triggers are defined). -* -* Do not use this instruction as a replacement for `listener`. This instruction -* only exists to ensure compatibility with the ViewEngine's host binding behavior. -* -* @param eventName Name of the event -* @param listenerFn The function to be called when event emits -* @param useCapture Whether or not to use capture in event listener -* @param eventTargetResolver Function that returns global target information in case this listener -* should be attached to a global object like window, document or body + * Registers a synthetic host listener (e.g. `(@foo.start)`) on a component. + * + * This instruction is for compatibility purposes and is designed to ensure that a + * synthetic host listener (e.g. `@HostListener('@foo.start')`) properly gets rendered + * in the component's renderer. Normally all host listeners are evaluated with the + * parent component's renderer, but, in the case of animation @triggers, they need + * to be evaluated with the sub component's renderer (because that's where the + * animation triggers are defined). + * + * Do not use this instruction as a replacement for `listener`. This instruction + * only exists to ensure compatibility with the ViewEngine's host binding behavior. + * + * @param eventName Name of the event + * @param listenerFn The function to be called when event emits + * @param useCapture Whether or not to use capture in event listener + * @param eventTargetResolver Function that returns global target information in case this listener + * should be attached to a global object like window, document or body * * @codeGenApi -*/ + */ export function ɵɵcomponentHostSyntheticListener( eventName: string, listenerFn: (e?: any) => any, useCapture = false, eventTargetResolver?: GlobalTargetResolver): typeof ɵɵcomponentHostSyntheticListener { @@ -94,7 +95,7 @@ function findExistingListener( // We have found a matching event name on the same node but it might not have been // registered yet, so we must explicitly verify entries in the LView cleanup data // structures. - const lCleanup = lView[CLEANUP] !; + const lCleanup = lView[CLEANUP]!; const listenerIdxInLCleanup = tCleanup[i + 2]; return lCleanup.length > listenerIdxInLCleanup ? lCleanup[listenerIdxInLCleanup] : null; } @@ -124,8 +125,9 @@ function listenerInternal( // register a listener and store its cleanup function on LView. const lCleanup = getLCleanup(lView); - ngDevMode && assertNodeOfPossibleTypes( - tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer); + ngDevMode && + assertNodeOfPossibleTypes( + tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer); let processOutputs = true; @@ -207,8 +209,8 @@ function listenerInternal( const output = directiveInstance[minifiedName]; if (ngDevMode && !isObservable(output)) { - throw new Error( - `@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`); + throw new Error(`@Output ${minifiedName} not initialized in '${ + directiveInstance.constructor.name}'.`); } const subscription = output.subscribe(listenerFn); diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index a801bbe5e2aa5..fd3030084afce 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -7,7 +7,8 @@ */ import {AttributeMarker, ComponentTemplate} from '..'; -import {SchemaMetadata} from '../../core'; +import {Injector, SchemaMetadata} from '../../core'; +import {Sanitizer} from '../../sanitization/sanitizer'; import {KeyValueArray} from '../../util/array_utils'; import {assertDefined} from '../../util/assert'; import {createNamedArrayType} from '../../util/named_array_type'; @@ -17,10 +18,10 @@ import {DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/ import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, TIcu} from '../interfaces/i18n'; import {PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TViewNode} from '../interfaces/node'; import {SelectorFlags} from '../interfaces/projection'; -import {TQueries} from '../interfaces/query'; -import {RComment, RElement, RNode} from '../interfaces/renderer'; -import {TStylingKey, TStylingRange, getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate} from '../interfaces/styling'; -import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, HookData, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TData, TVIEW, TView as ITView, TView, TViewType, T_HOST} from '../interfaces/view'; +import {LQueries, TQueries} from '../interfaces/query'; +import {RComment, RElement, Renderer3, RendererFactory3, RNode} from '../interfaces/renderer'; +import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling'; +import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, DestroyHookData, ExpandoInstructions, FLAGS, HEADER_OFFSET, HookData, HOST, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, T_HOST, TData, TVIEW, TView as ITView, TView, TViewType} from '../interfaces/view'; import {attachDebugObject} from '../util/debug_utils'; import {getLContainerActiveIndex, getTNode, unwrapRNode} from '../util/view_utils'; @@ -55,9 +56,9 @@ const NG_DEV_MODE = ((typeof ngDevMode === 'undefined' || !!ngDevMode) && initNg * ``` */ -let LVIEW_COMPONENT_CACHE !: Map<string|null, Array<any>>; -let LVIEW_EMBEDDED_CACHE !: Map<string|null, Array<any>>; -let LVIEW_ROOT !: Array<any>; +let LVIEW_COMPONENT_CACHE!: Map<string|null, Array<any>>; +let LVIEW_EMBEDDED_CACHE!: Map<string|null, Array<any>>; +let LVIEW_ROOT!: Array<any>; interface TViewDebug extends ITView { type: TViewType; @@ -74,7 +75,7 @@ export function cloneToLViewFromTViewBlueprint(tView: TView): LView { return lView.concat(tView.blueprint) as any; } -function getLViewToClone(type: TViewType, name: string | null): Array<any> { +function getLViewToClone(type: TViewType, name: string|null): Array<any> { switch (type) { case TViewType.Root: if (LVIEW_ROOT === undefined) LVIEW_ROOT = new (createNamedArrayType('LRootView'))(); @@ -99,7 +100,7 @@ function getLViewToClone(type: TViewType, name: string | null): Array<any> { throw new Error('unreachable code'); } -function nameSuffix(text: string | null | undefined): string { +function nameSuffix(text: string|null|undefined): string { if (text == null) return ''; const index = text.lastIndexOf('_Template'); return '_' + (index === -1 ? text : text.substr(0, index)); @@ -133,7 +134,7 @@ export const TViewConstructor = class TView implements ITView { public contentCheckHooks: HookData|null, // public viewHooks: HookData|null, // public viewCheckHooks: HookData|null, // - public destroyHooks: HookData|null, // + public destroyHooks: DestroyHookData|null, // public cleanup: any[]|null, // public contentQueries: number[]|null, // public components: number[]|null, // @@ -142,7 +143,7 @@ export const TViewConstructor = class TView implements ITView { public firstChild: ITNode|null, // public schemas: SchemaMetadata[]|null, // public consts: TConstants|null, // - ) {} + ) {} get template_(): string { const buf: string[] = []; @@ -182,7 +183,7 @@ class TNode implements ITNode { public residualClasses: KeyValueArray<any>|undefined|null, // public classBindings: TStylingRange, // public styleBindings: TStylingRange, // - ) {} + ) {} get type_(): string { switch (this.type) { @@ -235,8 +236,12 @@ class TNode implements ITNode { return buf.join(''); } - get styleBindings_(): DebugStyleBindings { return toDebugStyleBinding(this, false); } - get classBindings_(): DebugStyleBindings { return toDebugStyleBinding(this, true); } + get styleBindings_(): DebugStyleBindings { + return toDebugStyleBinding(this, false); + } + get classBindings_(): DebugStyleBindings { + return toDebugStyleBinding(this, true); + } } export const TNodeDebug = TNode; export type TNodeDebug = TNode; @@ -280,17 +285,16 @@ function toDebugStyleBinding(tNode: TNode, isClassBased: boolean): DebugStyleBin return bindings; } -function processTNodeChildren(tNode: ITNode | null, buf: string[]) { +function processTNodeChildren(tNode: ITNode|null, buf: string[]) { while (tNode) { - buf.push((tNode as any as{template_: string}).template_); + buf.push((tNode as any as {template_: string}).template_); tNode = tNode.next; } } -const TViewData = NG_DEV_MODE && createNamedArrayType('TViewData') || null !as ArrayConstructor; -let TVIEWDATA_EMPTY: - unknown[]; // can't initialize here or it will not be tree shaken, because `LView` - // constructor could have side-effects. +const TViewData = NG_DEV_MODE && createNamedArrayType('TViewData') || null! as ArrayConstructor; +let TVIEWDATA_EMPTY: unknown[]; // can't initialize here or it will not be tree shaken, because + // `LView` constructor could have side-effects. /** * This function clones a blueprint and creates TData. * @@ -302,21 +306,21 @@ export function cloneToTViewData(list: any[]): TData { } export const LViewBlueprint = - NG_DEV_MODE && createNamedArrayType('LViewBlueprint') || null !as ArrayConstructor; + NG_DEV_MODE && createNamedArrayType('LViewBlueprint') || null! as ArrayConstructor; export const MatchesArray = - NG_DEV_MODE && createNamedArrayType('MatchesArray') || null !as ArrayConstructor; + NG_DEV_MODE && createNamedArrayType('MatchesArray') || null! as ArrayConstructor; export const TViewComponents = - NG_DEV_MODE && createNamedArrayType('TViewComponents') || null !as ArrayConstructor; + NG_DEV_MODE && createNamedArrayType('TViewComponents') || null! as ArrayConstructor; export const TNodeLocalNames = - NG_DEV_MODE && createNamedArrayType('TNodeLocalNames') || null !as ArrayConstructor; + NG_DEV_MODE && createNamedArrayType('TNodeLocalNames') || null! as ArrayConstructor; export const TNodeInitialInputs = - NG_DEV_MODE && createNamedArrayType('TNodeInitialInputs') || null !as ArrayConstructor; + NG_DEV_MODE && createNamedArrayType('TNodeInitialInputs') || null! as ArrayConstructor; export const TNodeInitialData = - NG_DEV_MODE && createNamedArrayType('TNodeInitialData') || null !as ArrayConstructor; + NG_DEV_MODE && createNamedArrayType('TNodeInitialData') || null! as ArrayConstructor; export const LCleanup = - NG_DEV_MODE && createNamedArrayType('LCleanup') || null !as ArrayConstructor; + NG_DEV_MODE && createNamedArrayType('LCleanup') || null! as ArrayConstructor; export const TCleanup = - NG_DEV_MODE && createNamedArrayType('TCleanup') || null !as ArrayConstructor; + NG_DEV_MODE && createNamedArrayType('TCleanup') || null! as ArrayConstructor; @@ -329,8 +333,8 @@ export function attachLContainerDebug(lContainer: LContainer) { } export function toDebug(obj: LView): LViewDebug; -export function toDebug(obj: LView | null): LViewDebug|null; -export function toDebug(obj: LView | LContainer | null): LViewDebug|LContainerDebug|null; +export function toDebug(obj: LView|null): LViewDebug|null; +export function toDebug(obj: LView|LContainer|null): LViewDebug|LContainerDebug|null; export function toDebug(obj: any): any { if (obj) { const debug = (obj as any).debug; @@ -389,10 +393,18 @@ export class LViewDebug { indexWithinInitPhase: flags >> LViewFlags.IndexWithinInitPhaseShift, }; } - get parent(): LViewDebug|LContainerDebug|null { return toDebug(this._raw_lView[PARENT]); } - get host(): string|null { return toHtml(this._raw_lView[HOST], true); } - get html(): string { return (this.nodes || []).map(node => toHtml(node.native, true)).join(''); } - get context(): {}|null { return this._raw_lView[CONTEXT]; } + get parent(): LViewDebug|LContainerDebug|null { + return toDebug(this._raw_lView[PARENT]); + } + get host(): string|null { + return toHtml(this._raw_lView[HOST], true); + } + get html(): string { + return (this.nodes || []).map(node => toHtml(node.native, true)).join(''); + } + get context(): {}|null { + return this._raw_lView[CONTEXT]; + } /** * The tree of nodes associated with the current `LView`. The nodes have been normalized into * a @@ -404,18 +416,42 @@ export class LViewDebug { return toDebugNodes(tNode, lView); } - get tView() { return this._raw_lView[TVIEW]; } - get cleanup() { return this._raw_lView[CLEANUP]; } - get injector() { return this._raw_lView[INJECTOR]; } - get rendererFactory() { return this._raw_lView[RENDERER_FACTORY]; } - get renderer() { return this._raw_lView[RENDERER]; } - get sanitizer() { return this._raw_lView[SANITIZER]; } - get childHead() { return toDebug(this._raw_lView[CHILD_HEAD]); } - get next() { return toDebug(this._raw_lView[NEXT]); } - get childTail() { return toDebug(this._raw_lView[CHILD_TAIL]); } - get declarationView() { return toDebug(this._raw_lView[DECLARATION_VIEW]); } - get queries() { return this._raw_lView[QUERIES]; } - get tHost() { return this._raw_lView[T_HOST]; } + get tView(): ITView { + return this._raw_lView[TVIEW]; + } + get cleanup(): any[]|null { + return this._raw_lView[CLEANUP]; + } + get injector(): Injector|null { + return this._raw_lView[INJECTOR]; + } + get rendererFactory(): RendererFactory3 { + return this._raw_lView[RENDERER_FACTORY]; + } + get renderer(): Renderer3 { + return this._raw_lView[RENDERER]; + } + get sanitizer(): Sanitizer|null { + return this._raw_lView[SANITIZER]; + } + get childHead(): LViewDebug|LContainerDebug|null { + return toDebug(this._raw_lView[CHILD_HEAD]); + } + get next(): LViewDebug|LContainerDebug|null { + return toDebug(this._raw_lView[NEXT]); + } + get childTail(): LViewDebug|LContainerDebug|null { + return toDebug(this._raw_lView[CHILD_TAIL]); + } + get declarationView(): LViewDebug|null { + return toDebug(this._raw_lView[DECLARATION_VIEW]); + } + get queries(): LQueries|null { + return this._raw_lView[QUERIES]; + } + get tHost(): TViewNode|TElementNode|null { + return this._raw_lView[T_HOST]; + } /** * Normalized view of child views (and containers) attached at this location. @@ -444,7 +480,7 @@ export interface DebugNode { * @param tNode * @param lView */ -export function toDebugNodes(tNode: ITNode | null, lView: LView): DebugNode[]|null { +export function toDebugNodes(tNode: ITNode|null, lView: LView): DebugNode[]|null { if (tNode) { const debugNodes: DebugNode[] = []; let tNodeCursor: ITNode|null = tNode; @@ -473,20 +509,32 @@ export function buildDebugNode(tNode: ITNode, lView: LView, nodeIndex: number): export class LContainerDebug { constructor(private readonly _raw_lContainer: LContainer) {} - get activeIndex(): number { return getLContainerActiveIndex(this._raw_lContainer); } + get activeIndex(): number { + return getLContainerActiveIndex(this._raw_lContainer); + } get hasTransplantedViews(): boolean { return (this._raw_lContainer[ACTIVE_INDEX] & ActiveIndexFlag.HAS_TRANSPLANTED_VIEWS) === ActiveIndexFlag.HAS_TRANSPLANTED_VIEWS; } get views(): LViewDebug[] { return this._raw_lContainer.slice(CONTAINER_HEADER_OFFSET) - .map(toDebug as(l: LView) => LViewDebug); + .map(toDebug as (l: LView) => LViewDebug); + } + get parent(): LViewDebug|LContainerDebug|null { + return toDebug(this._raw_lContainer[PARENT]); + } + get movedViews(): LView[]|null { + return this._raw_lContainer[MOVED_VIEWS]; + } + get host(): RElement|RComment|LView { + return this._raw_lContainer[HOST]; + } + get native(): RComment { + return this._raw_lContainer[NATIVE]; + } + get next() { + return toDebug(this._raw_lContainer[NEXT]); } - get parent(): LViewDebug|LContainerDebug|null { return toDebug(this._raw_lContainer[PARENT]); } - get movedViews(): LView[]|null { return this._raw_lContainer[MOVED_VIEWS]; } - get host(): RElement|RComment|LView { return this._raw_lContainer[HOST]; } - get native(): RComment { return this._raw_lContainer[NATIVE]; } - get next() { return toDebug(this._raw_lContainer[NEXT]); } } /** @@ -507,7 +555,9 @@ export function readLViewValue(value: any): LView|null { export class I18NDebugItem { [key: string]: any; - get tNode() { return getTNode(this._lView[TVIEW], this.nodeIndex); } + get tNode() { + return getTNode(this._lView[TVIEW], this.nodeIndex); + } constructor( public __raw_opCode: any, private _lView: LView, public nodeIndex: number, @@ -523,15 +573,16 @@ export class I18NDebugItem { * @param lView The view the opCodes are acting on */ export function attachI18nOpCodesDebug( - mutateOpCodes: I18nMutateOpCodes, updateOpCodes: I18nUpdateOpCodes, icus: TIcu[] | null, + mutateOpCodes: I18nMutateOpCodes, updateOpCodes: I18nUpdateOpCodes, icus: TIcu[]|null, lView: LView) { attachDebugObject(mutateOpCodes, new I18nMutateOpCodesDebug(mutateOpCodes, lView)); attachDebugObject(updateOpCodes, new I18nUpdateOpCodesDebug(updateOpCodes, icus, lView)); if (icus) { icus.forEach(icu => { - icu.create.forEach( - icuCase => { attachDebugObject(icuCase, new I18nMutateOpCodesDebug(icuCase, lView)); }); + icu.create.forEach(icuCase => { + attachDebugObject(icuCase, new I18nMutateOpCodesDebug(icuCase, lView)); + }); icu.update.forEach(icuCase => { attachDebugObject(icuCase, new I18nUpdateOpCodesDebug(icuCase, icus, lView)); }); @@ -644,7 +695,7 @@ export class I18nUpdateOpCodesDebug implements I18nOpCodesDebug { if (opCode < 0) { // It's a binding index whose value is negative // We cannot know the value of the binding so we only show the index - value += `�${-opCode - 1}�`; + value += `�${- opCode - 1}�`; } else { const nodeIndex = opCode >>> I18nUpdateOpCode.SHIFT_REF; let tIcuIndex: number; @@ -657,20 +708,23 @@ export class I18nUpdateOpCodesDebug implements I18nOpCodesDebug { __raw_opCode: opCode, checkBit, type: 'Attr', - attrValue: value, attrName, sanitizeFn, + attrValue: value, + attrName, + sanitizeFn, }); break; case I18nUpdateOpCode.Text: results.push({ __raw_opCode: opCode, checkBit, - type: 'Text', nodeIndex, + type: 'Text', + nodeIndex, text: value, }); break; case I18nUpdateOpCode.IcuSwitch: tIcuIndex = __raw_opCodes[++j] as number; - tIcu = icus ![tIcuIndex]; + tIcu = icus![tIcuIndex]; let result = new I18NDebugItem(opCode, __lView, nodeIndex, 'IcuSwitch'); result['tIcuIndex'] = tIcuIndex; result['checkBit'] = checkBit; @@ -680,7 +734,7 @@ export class I18nUpdateOpCodesDebug implements I18nOpCodesDebug { break; case I18nUpdateOpCode.IcuUpdate: tIcuIndex = __raw_opCodes[++j] as number; - tIcu = icus ![tIcuIndex]; + tIcu = icus![tIcuIndex]; result = new I18NDebugItem(opCode, __lView, nodeIndex, 'IcuUpdate'); result['tIcuIndex'] = tIcuIndex; result['checkBit'] = checkBit; @@ -697,4 +751,6 @@ export class I18nUpdateOpCodesDebug implements I18nOpCodesDebug { } } -export interface I18nOpCodesDebug { operations: any[]; } +export interface I18nOpCodesDebug { + operations: any[]; +} diff --git a/packages/core/src/render3/instructions/projection.ts b/packages/core/src/render3/instructions/projection.ts index b432a37d646d8..c6ded02f7a6bd 100644 --- a/packages/core/src/render3/instructions/projection.ts +++ b/packages/core/src/render3/instructions/projection.ts @@ -79,9 +79,9 @@ export function ɵɵprojectionDef(projectionSlots?: ProjectionSlots): void { // If no explicit projection slots are defined, fall back to a single // projection slot with the wildcard selector. const numProjectionSlots = projectionSlots ? projectionSlots.length : 1; - const projectionHeads: (TNode | null)[] = componentNode.projection = - newArray(numProjectionSlots, null !as TNode); - const tails: (TNode | null)[] = projectionHeads.slice(); + const projectionHeads: (TNode|null)[] = componentNode.projection = + newArray(numProjectionSlots, null! as TNode); + const tails: (TNode|null)[] = projectionHeads.slice(); let componentChild: TNode|null = componentNode.child; @@ -91,7 +91,7 @@ export function ɵɵprojectionDef(projectionSlots?: ProjectionSlots): void { if (slotIndex !== null) { if (tails[slotIndex]) { - tails[slotIndex] !.projectionNext = componentChild; + tails[slotIndex]!.projectionNext = componentChild; } else { projectionHeads[slotIndex] = componentChild; } @@ -119,7 +119,7 @@ export function setDelayProjection(value: boolean) { * - 1 based index of the selector from the {@link projectionDef} * * @codeGenApi -*/ + */ export function ɵɵprojection( nodeIndex: number, selectorIndex: number = 0, attrs?: TAttributes): void { const lView = getLView(); diff --git a/packages/core/src/render3/instructions/property.ts b/packages/core/src/render3/instructions/property.ts index 5867fc9eca8e0..ab79a35bcdd12 100644 --- a/packages/core/src/render3/instructions/property.ts +++ b/packages/core/src/render3/instructions/property.ts @@ -33,7 +33,7 @@ import {elementPropertyInternal, setInputsForProperty, storePropertyBindingMetad * @codeGenApi */ export function ɵɵproperty<T>( - propName: string, value: T, sanitizer?: SanitizerFn | null): typeof ɵɵproperty { + propName: string, value: T, sanitizer?: SanitizerFn|null): typeof ɵɵproperty { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, value)) { @@ -52,7 +52,7 @@ export function ɵɵproperty<T>( */ export function setDirectiveInputsWhichShadowsStyling( tView: TView, tNode: TNode, lView: LView, value: any, isClassBased: boolean) { - const inputs = tNode.inputs !; + const inputs = tNode.inputs!; const property = isClassBased ? 'class' : 'style'; // We support both 'class' and `className` hence the fallback. setInputsForProperty(tView, lView, inputs[property], property, value); diff --git a/packages/core/src/render3/instructions/property_interpolation.ts b/packages/core/src/render3/instructions/property_interpolation.ts index 373f5912116bd..6059d1985b003 100644 --- a/packages/core/src/render3/instructions/property_interpolation.ts +++ b/packages/core/src/render3/instructions/property_interpolation.ts @@ -88,8 +88,9 @@ export function ɵɵpropertyInterpolate1( const tNode = getSelectedTNode(); elementPropertyInternal( tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); - ngDevMode && storePropertyBindingMetadata( - tView.data, tNode, propName, getBindingIndex() - 1, prefix, suffix); + ngDevMode && + storePropertyBindingMetadata( + tView.data, tNode, propName, getBindingIndex() - 1, prefix, suffix); } return ɵɵpropertyInterpolate1; } @@ -134,8 +135,9 @@ export function ɵɵpropertyInterpolate2( const tNode = getSelectedTNode(); elementPropertyInternal( tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); - ngDevMode && storePropertyBindingMetadata( - tView.data, tNode, propName, getBindingIndex() - 2, prefix, i0, suffix); + ngDevMode && + storePropertyBindingMetadata( + tView.data, tNode, propName, getBindingIndex() - 2, prefix, i0, suffix); } return ɵɵpropertyInterpolate2; } @@ -183,8 +185,9 @@ export function ɵɵpropertyInterpolate3( const tNode = getSelectedTNode(); elementPropertyInternal( tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); - ngDevMode && storePropertyBindingMetadata( - tView.data, tNode, propName, getBindingIndex() - 3, prefix, i0, i1, suffix); + ngDevMode && + storePropertyBindingMetadata( + tView.data, tNode, propName, getBindingIndex() - 3, prefix, i0, i1, suffix); } return ɵɵpropertyInterpolate3; } @@ -408,9 +411,10 @@ export function ɵɵpropertyInterpolate7( const tNode = getSelectedTNode(); elementPropertyInternal( tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); - ngDevMode && storePropertyBindingMetadata( - tView.data, tNode, propName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, - i5, suffix); + ngDevMode && + storePropertyBindingMetadata( + tView.data, tNode, propName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, + suffix); } return ɵɵpropertyInterpolate7; } @@ -470,9 +474,10 @@ export function ɵɵpropertyInterpolate8( const tNode = getSelectedTNode(); elementPropertyInternal( tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); - ngDevMode && storePropertyBindingMetadata( - tView.data, tNode, propName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, - i5, i6, suffix); + ngDevMode && + storePropertyBindingMetadata( + tView.data, tNode, propName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, + suffix); } return ɵɵpropertyInterpolate8; } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 5c07128a5688a..828188fbd9435 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -24,13 +24,13 @@ import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags} fr import {ACTIVE_INDEX, ActiveIndexFlag, CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS} from '../interfaces/container'; import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition'; import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector'; -import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TConstants, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node'; -import {RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer'; +import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstants, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node'; +import {isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks'; -import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, TViewType, T_HOST} from '../interfaces/view'; +import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, InitPhaseState, INJECTOR, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, T_HOST, TData, TVIEW, TView, TViewType} from '../interfaces/view'; import {assertNodeOfPossibleTypes} from '../node_assert'; -import {isNodeMatchingSelectorList} from '../node_selector_matcher'; +import {isInlineTemplate, isNodeMatchingSelectorList} from '../node_selector_matcher'; import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, getTView, leaveView, setBindingIndex, setBindingRootForHostBindings, setCheckNoChangesMode, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state'; import {NO_CHANGE} from '../tokens'; import {isAnimationProp, mergeHostAttrs} from '../util/attrs_utils'; @@ -39,7 +39,7 @@ import {getLViewParent} from '../util/view_traversal_utils'; import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapLView, viewAttachedToChangeDetector} from '../util/view_utils'; import {selectIndexInternal} from './advance'; -import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeDebug, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData} from './lview_debug'; +import {attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData, LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeDebug, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor} from './lview_debug'; @@ -129,7 +129,7 @@ function refreshContentQueries(tView: TView, lView: LView): void { ngDevMode && assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined'); setCurrentQueryIndex(queryStartIdx); - directiveDef.contentQueries !(RenderFlags.Update, lView[directiveDefIdx], directiveDefIdx); + directiveDef.contentQueries!(RenderFlags.Update, lView[directiveDefIdx], directiveDefIdx); } } } @@ -155,8 +155,7 @@ function renderChildComponents(hostLView: LView, components: number[]): void { * @param renderer A renderer to use * @returns the element created */ -export function elementCreate( - name: string, renderer: Renderer3, namespace: string | null): RElement { +export function elementCreate(name: string, renderer: Renderer3, namespace: string|null): RElement { if (isProceduralRenderer(renderer)) { return renderer.createElement(name, namespace); } else { @@ -166,10 +165,9 @@ export function elementCreate( } export function createLView<T>( - parentLView: LView | null, tView: TView, context: T | null, flags: LViewFlags, - host: RElement | null, tHostNode: TViewNode | TElementNode | null, - rendererFactory?: RendererFactory3 | null, renderer?: Renderer3 | null, - sanitizer?: Sanitizer | null, injector?: Injector | null): LView { + parentLView: LView|null, tView: TView, context: T|null, flags: LViewFlags, host: RElement|null, + tHostNode: TViewNode|TElementNode|null, rendererFactory?: RendererFactory3|null, + renderer?: Renderer3|null, sanitizer?: Sanitizer|null, injector?: Injector|null): LView { const lView = ngDevMode ? cloneToLViewFromTViewBlueprint(tView) : tView.blueprint.slice() as LView; lView[HOST] = host; @@ -177,18 +175,19 @@ export function createLView<T>( resetPreOrderHookFlags(lView); lView[PARENT] = lView[DECLARATION_VIEW] = parentLView; lView[CONTEXT] = context; - lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]) !; + lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY])!; ngDevMode && assertDefined(lView[RENDERER_FACTORY], 'RendererFactory is required'); - lView[RENDERER] = (renderer || parentLView && parentLView[RENDERER]) !; + lView[RENDERER] = (renderer || parentLView && parentLView[RENDERER])!; ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required'); - lView[SANITIZER] = sanitizer || parentLView && parentLView[SANITIZER] || null !; + lView[SANITIZER] = sanitizer || parentLView && parentLView[SANITIZER] || null!; lView[INJECTOR as any] = injector || parentLView && parentLView[INJECTOR] || null; lView[T_HOST] = tHostNode; - ngDevMode && assertEqual( - tView.type == TViewType.Embedded ? parentLView !== null : true, true, - 'Embedded views must have parentLView'); + ngDevMode && + assertEqual( + tView.type == TViewType.Embedded ? parentLView !== null : true, true, + 'Embedded views must have parentLView'); lView[DECLARATION_COMPONENT_VIEW] = - tView.type == TViewType.Embedded ? parentLView ![DECLARATION_COMPONENT_VIEW] : lView; + tView.type == TViewType.Embedded ? parentLView![DECLARATION_COMPONENT_VIEW] : lView; ngDevMode && attachLViewDebug(lView); return lView; } @@ -208,23 +207,23 @@ export function createLView<T>( * @param attrs Any attrs for the native element, if applicable */ export function getOrCreateTNode( - tView: TView, tHostNode: TNode | null, index: number, type: TNodeType.Element, - name: string | null, attrs: TAttributes | null): TElementNode; + tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.Element, name: string|null, + attrs: TAttributes|null): TElementNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode | null, index: number, type: TNodeType.Container, - name: string | null, attrs: TAttributes | null): TContainerNode; + tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.Container, + name: string|null, attrs: TAttributes|null): TContainerNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode | null, index: number, type: TNodeType.Projection, name: null, - attrs: TAttributes | null): TProjectionNode; + tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.Projection, name: null, + attrs: TAttributes|null): TProjectionNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode | null, index: number, type: TNodeType.ElementContainer, - name: string | null, attrs: TAttributes | null): TElementContainerNode; + tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.ElementContainer, + name: string|null, attrs: TAttributes|null): TElementContainerNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode | null, index: number, type: TNodeType.IcuContainer, name: null, - attrs: TAttributes | null): TElementContainerNode; + tView: TView, tHostNode: TNode|null, index: number, type: TNodeType.IcuContainer, name: null, + attrs: TAttributes|null): TElementContainerNode; export function getOrCreateTNode( - tView: TView, tHostNode: TNode | null, index: number, type: TNodeType, name: string | null, - attrs: TAttributes | null): TElementNode&TContainerNode&TElementContainerNode&TProjectionNode& + tView: TView, tHostNode: TNode|null, index: number, type: TNodeType, name: string|null, + attrs: TAttributes|null): TElementNode&TContainerNode&TElementContainerNode&TProjectionNode& TIcuContainerNode { // Keep this function short, so that the VM will inline it. const adjustedIndex = index + HEADER_OFFSET; @@ -236,8 +235,8 @@ export function getOrCreateTNode( } function createTNodeAtIndex( - tView: TView, tHostNode: TNode | null, adjustedIndex: number, type: TNodeType, - name: string | null, attrs: TAttributes | null) { + tView: TView, tHostNode: TNode|null, adjustedIndex: number, type: TNodeType, name: string|null, + attrs: TAttributes|null) { const previousOrParentTNode = getPreviousOrParentTNode(); const isParent = getIsParent(); const parent = @@ -267,7 +266,7 @@ function createTNodeAtIndex( } export function assignTViewNodeToLView( - tView: TView, tParentNode: TNode | null, index: number, lView: LView): TViewNode { + tView: TView, tParentNode: TNode|null, index: number, lView: LView): TViewNode { // View nodes are not stored in data because they can be added / removed at runtime (which // would cause indices to change). Their TNodes are instead stored in tView.node. let tNode = tView.node; @@ -275,9 +274,9 @@ export function assignTViewNodeToLView( ngDevMode && tParentNode && assertNodeOfPossibleTypes(tParentNode, TNodeType.Element, TNodeType.Container); tView.node = tNode = createTNode( - tView, - tParentNode as TElementNode | TContainerNode | null, // - TNodeType.View, index, null, null) as TViewNode; + tView, + tParentNode as TElementNode | TContainerNode | null, // + TNodeType.View, index, null, null) as TViewNode; } return lView[T_HOST] = tNode as TViewNode; @@ -294,8 +293,9 @@ export function assignTViewNodeToLView( * @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0 */ export function allocExpando(tView: TView, lView: LView, numSlotsToAlloc: number) { - ngDevMode && assertGreaterThan( - numSlotsToAlloc, 0, 'The number of slots to alloc should be greater than 0'); + ngDevMode && + assertGreaterThan( + numSlotsToAlloc, 0, 'The number of slots to alloc should be greater than 0'); if (numSlotsToAlloc > 0) { if (tView.firstCreatePass) { for (let i = 0; i < numSlotsToAlloc; i++) { @@ -365,7 +365,7 @@ export function renderView<T>(tView: TView, lView: LView, context: T): void { // in case a child component has projected a container. The LContainer needs // to exist so the embedded views are properly attached by the container. if (tView.staticViewQueries) { - executeViewQueryFn(RenderFlags.Update, tView.viewQuery !, context); + executeViewQueryFn(RenderFlags.Update, tView.viewQuery!, context); } // Render child component views. @@ -389,7 +389,7 @@ export function renderView<T>(tView: TView, lView: LView, context: T): void { * - refreshing child (embedded and component) views. */ export function refreshView<T>( - tView: TView, lView: LView, templateFn: ComponentTemplate<{}>| null, context: T) { + tView: TView, lView: LView, templateFn: ComponentTemplate<{}>|null, context: T) { ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode'); const flags = lView[FLAGS]; if ((flags & LViewFlags.Destroyed) === LViewFlags.Destroyed) return; @@ -505,7 +505,7 @@ export function refreshView<T>( } export function renderComponentOrTemplate<T>( - tView: TView, lView: LView, templateFn: ComponentTemplate<{}>| null, context: T) { + tView: TView, lView: LView, templateFn: ComponentTemplate<{}>|null, context: T) { const rendererFactory = lView[RENDERER_FACTORY]; const normalExecutionPath = !getCheckNoChangesMode(); const creationModeIsActive = isCreationMode(lView); @@ -618,10 +618,10 @@ export function getOrCreateTComponentView(def: ComponentDef<any>): TView { * @param consts Constants for this view */ export function createTView( - type: TViewType, viewIndex: number, templateFn: ComponentTemplate<any>| null, decls: number, - vars: number, directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null, - viewQuery: ViewQueriesFunction<any>| null, schemas: SchemaMetadata[] | null, - consts: TConstants | null): TView { + type: TViewType, viewIndex: number, templateFn: ComponentTemplate<any>|null, decls: number, + vars: number, directives: DirectiveDefListOrFactory|null, pipes: PipeDefListOrFactory|null, + viewQuery: ViewQueriesFunction<any>|null, schemas: SchemaMetadata[]|null, + consts: TConstants|null): TView { ngDevMode && ngDevMode.tView++; const bindingStartIndex = HEADER_OFFSET + decls; // This length does not yet contain host bindings from child directives because at this point, @@ -637,7 +637,7 @@ export function createTView( templateFn, // template: ComponentTemplate<{}>|null, null, // queries: TQueries|null viewQuery, // viewQuery: ViewQueriesFunction<{}>|null, - null !, // node: TViewNode|TElementNode|null, + null!, // node: TViewNode|TElementNode|null, cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData, bindingStartIndex, // bindingStartIndex: number, initialViewLength, // expandoStartIndex: number, @@ -652,7 +652,7 @@ export function createTView( null, // contentCheckHooks: HookData|null, null, // viewHooks: HookData|null, null, // viewCheckHooks: HookData|null, - null, // destroyHooks: HookData|null, + null, // destroyHooks: DestroyHookData|null, null, // cleanup: any[]|null, null, // contentQueries: number[]|null, null, // components: number[]|null, @@ -670,7 +670,7 @@ export function createTView( template: templateFn, queries: null, viewQuery: viewQuery, - node: null !, + node: null!, data: blueprint.slice().fill(null, bindingStartIndex), bindingStartIndex: bindingStartIndex, expandoStartIndex: initialViewLength, @@ -711,7 +711,7 @@ function createError(text: string, token: any) { return new Error(`Renderer: ${text} [${stringifyForError(token)}]`); } -function assertHostNodeExists(rElement: RElement, elementOrSelector: RElement | string) { +function assertHostNodeExists(rElement: RElement, elementOrSelector: RElement|string) { if (!rElement) { if (typeof elementOrSelector === 'string') { throw createError('Host node with selector not found:', elementOrSelector); @@ -729,7 +729,7 @@ function assertHostNodeExists(rElement: RElement, elementOrSelector: RElement | * @param encapsulation View Encapsulation defined for component that requests host element. */ export function locateHostElement( - renderer: Renderer3, elementOrSelector: RElement | string, + renderer: Renderer3, elementOrSelector: RElement|string, encapsulation: ViewEncapsulation): RElement { if (isProceduralRenderer(renderer)) { // When using native Shadow DOM, do not clear host element to allow native slot projection @@ -738,7 +738,7 @@ export function locateHostElement( } let rElement = typeof elementOrSelector === 'string' ? - renderer.querySelector(elementOrSelector) ! : + renderer.querySelector(elementOrSelector)! : elementOrSelector; ngDevMode && assertHostNodeExists(rElement, elementOrSelector); @@ -780,7 +780,7 @@ export function storeCleanupFn(tView: TView, lView: LView, cleanupFn: Function): getLCleanup(lView).push(cleanupFn); if (tView.firstCreatePass) { - getTViewCleanup(tView).push(lView[CLEANUP] !.length - 1, null); + getTViewCleanup(tView).push(lView[CLEANUP]!.length - 1, null); } } @@ -796,8 +796,8 @@ export function storeCleanupFn(tView: TView, lView: LView, cleanupFn: Function): * @returns the TNode object */ export function createTNode( - tView: TView, tParent: TElementNode | TContainerNode | null, type: TNodeType, - adjustedIndex: number, tagName: string | null, attrs: TAttributes | null): TNode { + tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, adjustedIndex: number, + tagName: string|null, attrs: TAttributes|null): TNode { ngDevMode && ngDevMode.tNode++; let injectorIndex = tParent ? tParent.injectorIndex : -1; return ngDevMode ? new TNodeDebug( @@ -866,7 +866,7 @@ export function createTNode( function generatePropertyAliases( inputAliasMap: {[publicName: string]: string}, directiveDefIdx: number, - propStore: PropertyAliases | null): PropertyAliases|null { + propStore: PropertyAliases|null): PropertyAliases|null { for (let publicName in inputAliasMap) { if (inputAliasMap.hasOwnProperty(publicName)) { propStore = propStore === null ? {} : propStore; @@ -900,8 +900,14 @@ function initializeInputAndOutputAliases(tView: TView, tNode: TNode): void { for (let i = start; i < end; i++) { const directiveDef = defs[i] as DirectiveDef<any>; const directiveInputs = directiveDef.inputs; - inputsFromAttrs.push( - tNodeAttrs !== null ? generateInitialInputs(directiveInputs, tNodeAttrs) : null); + // Do not use unbound attributes as inputs to structural directives, since structural + // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`). + // TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which + // should be set for inline templates. + const initialInputs = (tNodeAttrs !== null && !isInlineTemplate(tNode)) ? + generateInitialInputs(directiveInputs, tNodeAttrs) : + null; + inputsFromAttrs.push(initialInputs); inputsStore = generatePropertyAliases(directiveInputs, i, inputsStore); outputsStore = generatePropertyAliases(directiveDef.outputs, i, outputsStore); } @@ -942,7 +948,7 @@ function mapPropName(name: string): string { export function elementPropertyInternal<T>( tView: TView, tNode: TNode, lView: LView, propName: string, value: T, renderer: Renderer3, - sanitizer: SanitizerFn | null | undefined, nativeOnly: boolean): void { + sanitizer: SanitizerFn|null|undefined, nativeOnly: boolean): void { ngDevMode && assertNotSame(value, NO_CHANGE as any, 'Incoming value should never be NO_CHANGE.'); const element = getNativeByTNode(tNode, lView) as RElement | RComment; let inputData = tNode.inputs; @@ -994,7 +1000,7 @@ function markDirtyIfOnPush(lView: LView, viewIndex: number): void { } function setNgReflectProperty( - lView: LView, element: RElement | RComment, type: TNodeType, attrName: string, value: any) { + lView: LView, element: RElement|RComment, type: TNodeType, attrName: string, value: any) { const renderer = lView[RENDERER]; attrName = normalizeDebugBindingName(attrName); const debugValue = normalizeDebugBindingValue(value); @@ -1018,7 +1024,7 @@ function setNgReflectProperty( } export function setNgReflectProperties( - lView: LView, element: RElement | RComment, type: TNodeType, dataValue: PropertyAliasValue, + lView: LView, element: RElement|RComment, type: TNodeType, dataValue: PropertyAliasValue, value: any) { if (type === TNodeType.Element || type === TNodeType.Container) { /** @@ -1036,8 +1042,14 @@ export function setNgReflectProperties( } function validateProperty( - tView: TView, lView: LView, element: RElement | RComment, propName: string, + tView: TView, lView: LView, element: RElement|RComment, propName: string, tNode: TNode): boolean { + // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT + // mode where this check happens at compile time. In JIT mode, `schemas` is always present and + // defined as an array (as an empty array in case `schemas` field is not defined) and we should + // execute the check below. + if (tView.schemas === null) return true; + // The property is considered valid if the element matches the schema, it exists on the element // or it is synthetic, and we are in a browser context (web worker nodes should be skipped). if (matchingSchemas(tView, lView, tNode.tagName) || propName in element || @@ -1050,7 +1062,7 @@ function validateProperty( return typeof Node === 'undefined' || Node === null || !(element instanceof Node); } -export function matchingSchemas(tView: TView, lView: LView, tagName: string | null): boolean { +export function matchingSchemas(tView: TView, lView: LView, tagName: string|null): boolean { const schemas = tView.schemas; if (schemas !== null) { @@ -1099,8 +1111,8 @@ export function instantiateRootComponent<T>(tView: TView, lView: LView, def: Com * Resolve the matched directives on a node. */ export function resolveDirectives( - tView: TView, lView: LView, tNode: TElementNode | TContainerNode | TElementContainerNode, - localRefs: string[] | null): boolean { + tView: TView, lView: LView, tNode: TElementNode|TContainerNode|TElementContainerNode, + localRefs: string[]|null): boolean { // Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in // tsickle. ngDevMode && assertFirstCreatePass(tView); @@ -1108,7 +1120,7 @@ export function resolveDirectives( let hasDirectives = false; if (getBindingsEnabled()) { const directiveDefs: DirectiveDef<any>[]|null = findDirectiveDefMatches(tView, lView, tNode); - const exportsMap: ({[key: string]: number} | null) = localRefs === null ? null : {'': -1}; + const exportsMap: ({[key: string]: number}|null) = localRefs === null ? null : {'': -1}; if (directiveDefs !== null) { let totalDirectiveHostVars = 0; @@ -1135,7 +1147,7 @@ export function resolveDirectives( baseResolveDirective(tView, lView, def); - saveNameToExportMap(tView.data !.length - 1, def, exportsMap); + saveNameToExportMap(tView.data!.length - 1, def, exportsMap); if (def.contentQueries !== null) tNode.flags |= TNodeFlags.hasContentQuery; if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0) @@ -1152,8 +1164,8 @@ export function resolveDirectives( } if (!preOrderCheckHooksFound && (def.onChanges || def.doCheck)) { - (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [ - ])).push(tNode.index - HEADER_OFFSET); + (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])) + .push(tNode.index - HEADER_OFFSET); preOrderCheckHooksFound = true; } @@ -1178,9 +1190,9 @@ export function resolveDirectives( * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add. */ export function addHostBindingsToExpandoInstructions( - tView: TView, def: ComponentDef<any>| DirectiveDef<any>): void { + tView: TView, def: ComponentDef<any>|DirectiveDef<any>): void { ngDevMode && assertFirstCreatePass(tView); - const expando = tView.expandoInstructions !; + const expando = tView.expandoInstructions!; // TODO(misko): PERF we are adding `hostBindings` even if there is nothing to add! This is // suboptimal for performance. `def.hostBindings` may be null, // but we still need to push null to the array as a placeholder @@ -1244,7 +1256,7 @@ function instantiateAllDirectives( attachPatchData(directive, lView); if (initialInputs !== null) { - setInputsFromAttrs(lView, i - start, directive, def, tNode, initialInputs !); + setInputsFromAttrs(lView, i - start, directive, def, tNode, initialInputs!); } if (isComponent) { @@ -1257,7 +1269,7 @@ function instantiateAllDirectives( function invokeDirectivesHostBindings(tView: TView, lView: LView, tNode: TNode) { const start = tNode.directiveStart; const end = tNode.directiveEnd; - const expando = tView.expandoInstructions !; + const expando = tView.expandoInstructions!; const firstCreatePass = tView.firstCreatePass; const elementIndex = tNode.index - HEADER_OFFSET; try { @@ -1284,7 +1296,7 @@ function invokeDirectivesHostBindings(tView: TView, lView: LView, tNode: TNode) */ export function invokeHostBindingsInCreationMode(def: DirectiveDef<any>, directive: any) { if (def.hostBindings !== null) { - def.hostBindings !(RenderFlags.Create, directive); + def.hostBindings!(RenderFlags.Create, directive); } } @@ -1296,9 +1308,10 @@ export function invokeHostBindingsInCreationMode(def: DirectiveDef<any>, directi */ export function generateExpandoInstructionBlock( tView: TView, tNode: TNode, directiveCount: number): void { - ngDevMode && assertEqual( - tView.firstCreatePass, true, - 'Expando block should only be generated on first create pass.'); + ngDevMode && + assertEqual( + tView.firstCreatePass, true, + 'Expando block should only be generated on first create pass.'); // Important: In JS `-x` and `0-x` is not the same! If `x===0` then `-x` will produce `-0` which // requires non standard math arithmetic and it can prevent VM optimizations. @@ -1306,26 +1319,27 @@ export function generateExpandoInstructionBlock( const elementIndex = HEADER_OFFSET - tNode.index; const providerStartIndex = tNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask; const providerCount = tView.data.length - providerStartIndex; - (tView.expandoInstructions || (tView.expandoInstructions = [ - ])).push(elementIndex, providerCount, directiveCount); + (tView.expandoInstructions || (tView.expandoInstructions = [])) + .push(elementIndex, providerCount, directiveCount); } /** -* Matches the current node against all available selectors. -* If a component is matched (at most one), it is returned in first position in the array. -*/ + * Matches the current node against all available selectors. + * If a component is matched (at most one), it is returned in first position in the array. + */ function findDirectiveDefMatches( tView: TView, viewData: LView, - tNode: TElementNode | TContainerNode | TElementContainerNode): DirectiveDef<any>[]|null { + tNode: TElementNode|TContainerNode|TElementContainerNode): DirectiveDef<any>[]|null { ngDevMode && assertFirstCreatePass(tView); - ngDevMode && assertNodeOfPossibleTypes( - tNode, TNodeType.Element, TNodeType.ElementContainer, TNodeType.Container); + ngDevMode && + assertNodeOfPossibleTypes( + tNode, TNodeType.Element, TNodeType.ElementContainer, TNodeType.Container); const registry = tView.directiveRegistry; let matches: any[]|null = null; if (registry) { for (let i = 0; i < registry.length; i++) { const def = registry[i] as ComponentDef<any>| DirectiveDef<any>; - if (isNodeMatchingSelectorList(tNode, def.selectors !, /* isProjectionMode */ false)) { + if (isNodeMatchingSelectorList(tNode, def.selectors!, /* isProjectionMode */ false)) { matches || (matches = ngDevMode ? new MatchesArray() : []); diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, viewData), tView, def.type); @@ -1347,21 +1361,20 @@ function findDirectiveDefMatches( * Marks a given TNode as a component's host. This consists of: * - setting appropriate TNode flags; * - storing index of component's host element so it will be queued for view refresh during CD. -*/ + */ export function markAsComponentHost(tView: TView, hostTNode: TNode): void { ngDevMode && assertFirstCreatePass(tView); hostTNode.flags |= TNodeFlags.isComponentHost; - (tView.components || (tView.components = ngDevMode ? new TViewComponents() : [ - ])).push(hostTNode.index); + (tView.components || (tView.components = ngDevMode ? new TViewComponents() : [])) + .push(hostTNode.index); } /** Caches local names and their matching directive indices for query and template lookups. */ function cacheMatchingLocalNames( - tNode: TNode, localRefs: string[] | null, exportsMap: {[key: string]: number}): void { + tNode: TNode, localRefs: string[]|null, exportsMap: {[key: string]: number}): void { if (localRefs) { - const localNames: (string | number)[] = tNode.localNames = - ngDevMode ? new TNodeLocalNames() : []; + const localNames: (string|number)[] = tNode.localNames = ngDevMode ? new TNodeLocalNames() : []; // Local names must be stored in tNode in the same order that localRefs are defined // in the template to ensure the data is loaded in the same slots as their refs @@ -1375,12 +1388,12 @@ function cacheMatchingLocalNames( } /** -* Builds up an export map as directives are created, so local refs can be quickly mapped -* to their directive instances. -*/ + * Builds up an export map as directives are created, so local refs can be quickly mapped + * to their directive instances. + */ function saveNameToExportMap( - index: number, def: DirectiveDef<any>| ComponentDef<any>, - exportsMap: {[key: string]: number} | null) { + index: number, def: DirectiveDef<any>|ComponentDef<any>, + exportsMap: {[key: string]: number}|null) { if (exportsMap) { if (def.exportAs) { for (let i = 0; i < def.exportAs.length; i++) { @@ -1397,9 +1410,10 @@ function saveNameToExportMap( * @param index the initial index */ export function initTNodeFlags(tNode: TNode, index: number, numberOfDirectives: number) { - ngDevMode && assertNotEqual( - numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, - 'Reached the max number of directives'); + ngDevMode && + assertNotEqual( + numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, + 'Reached the max number of directives'); tNode.flags |= TNodeFlags.isDirectiveHost; // When the first directive is created on a node, save the index tNode.directiveStart = index; @@ -1410,7 +1424,7 @@ export function initTNodeFlags(tNode: TNode, index: number, numberOfDirectives: function baseResolveDirective<T>(tView: TView, viewData: LView, def: DirectiveDef<T>) { tView.data.push(def); const directiveFactory = - def.factory || ((def as{factory: Function}).factory = getFactoryDef(def.type, true)); + def.factory || ((def as {factory: Function}).factory = getFactoryDef(def.type, true)); const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), null); tView.blueprint.push(nodeInjectorFactory); viewData.push(nodeInjectorFactory); @@ -1435,8 +1449,8 @@ function addComponentLogic<T>(lView: LView, hostTNode: TElementNode, def: Compon } export function elementAttributeInternal( - tNode: TNode, lView: LView, name: string, value: any, sanitizer: SanitizerFn | null | undefined, - namespace: string | null | undefined) { + tNode: TNode, lView: LView, name: string, value: any, sanitizer: SanitizerFn|null|undefined, + namespace: string|null|undefined) { ngDevMode && assertNotSame(value, NO_CHANGE as any, 'Incoming value should never be NO_CHANGE.'); ngDevMode && validateAgainstEventAttributes(name); const element = getNativeByTNode(tNode, lView) as RElement; @@ -1472,7 +1486,7 @@ export function elementAttributeInternal( function setInputsFromAttrs<T>( lView: LView, directiveIndex: number, instance: T, def: DirectiveDef<T>, tNode: TNode, initialInputData: InitialInputData): void { - const initialInputs: InitialInputs|null = initialInputData ![directiveIndex]; + const initialInputs: InitialInputs|null = initialInputData![directiveIndex]; if (initialInputs !== null) { const setInput = def.setInput; for (let i = 0; i < initialInputs.length;) { @@ -1480,7 +1494,7 @@ function setInputsFromAttrs<T>( const privateName = initialInputs[i++]; const value = initialInputs[i++]; if (setInput !== null) { - def.setInput !(instance, value, publicName, privateName); + def.setInput!(instance, value, publicName, privateName); } else { (instance as any)[privateName] = value; } @@ -1554,7 +1568,7 @@ const LContainerArray: any = ((typeof ngDevMode === 'undefined' || ngDevMode) && * @returns LContainer */ export function createLContainer( - hostNative: RElement | RComment | LView, currentView: LView, native: RComment, + hostNative: RElement|RComment|LView, currentView: LView, native: RComment, tNode: TNode): LContainer { ngDevMode && assertLView(currentView); ngDevMode && !isProceduralRenderer(currentView[RENDERER]) && assertDomNode(native); @@ -1569,7 +1583,7 @@ export function createLContainer( tNode, // t_host native, // native, null, // view refs - ); + ); ngDevMode && attachLContainerDebug(lContainer); return lContainer; } @@ -1594,14 +1608,14 @@ function refreshDynamicEmbeddedViews(lView: LView) { ngDevMode && assertDefined(embeddedTView, 'TView must be allocated'); if (viewAttachedToChangeDetector(embeddedLView)) { refreshView( - embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT] !); + embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]!); } } if ((activeIndexFlag & ActiveIndexFlag.HAS_TRANSPLANTED_VIEWS) !== 0) { // We should only CD moved views if the component where they were inserted does not match // the component where they were declared and insertion is on-push. Moved views also // contains intra component moves, or check-always which need to be skipped. - refreshTransplantedViews(viewOrContainer, lView[DECLARATION_COMPONENT_VIEW] !); + refreshTransplantedViews(viewOrContainer, lView[DECLARATION_COMPONENT_VIEW]!); } } viewOrContainer = viewOrContainer[NEXT]; @@ -1619,13 +1633,13 @@ function refreshDynamicEmbeddedViews(lView: LView) { * @param declaredComponentLView The `lContainer` parent component `LView`. */ function refreshTransplantedViews(lContainer: LContainer, declaredComponentLView: LView) { - const movedViews = lContainer[MOVED_VIEWS] !; + const movedViews = lContainer[MOVED_VIEWS]!; ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS'); for (let i = 0; i < movedViews.length; i++) { - const movedLView = movedViews[i] !; + const movedLView = movedViews[i]!; const insertionLContainer = movedLView[PARENT] as LContainer; ngDevMode && assertLContainer(insertionLContainer); - const insertedComponentLView = insertionLContainer[PARENT][DECLARATION_COMPONENT_VIEW] !; + const insertedComponentLView = insertionLContainer[PARENT][DECLARATION_COMPONENT_VIEW]!; ngDevMode && assertDefined(insertedComponentLView, 'Missing LView'); // Check if we have a transplanted view by compering declaration and insertion location. if (insertedComponentLView !== declaredComponentLView) { @@ -1643,7 +1657,7 @@ function refreshTransplantedViews(lContainer: LContainer, declaredComponentLView // point. const movedTView = movedLView[TVIEW]; ngDevMode && assertDefined(movedTView, 'TView must be allocated'); - refreshView(movedTView, movedLView, movedTView.template, movedLView[CONTEXT] !); + refreshView(movedTView, movedLView, movedTView.template, movedLView[CONTEXT]!); } } } @@ -1725,7 +1739,7 @@ export function addToViewTree<T extends LView|LContainer>(lView: LView, lViewOrL // of order, the change detection will run out of order, as the act of retrieving the the // LContainer from the RNode is what adds it to the queue. if (lView[CHILD_HEAD]) { - lView[CHILD_TAIL] ![NEXT] = lViewOrLContainer; + lView[CHILD_TAIL]![NEXT] = lViewOrLContainer; } else { lView[CHILD_HEAD] = lViewOrLContainer; } @@ -1758,7 +1772,7 @@ export function markViewDirty(lView: LView): LView|null { return lView; } // continue otherwise - lView = parent !; + lView = parent!; } return null; } @@ -1797,7 +1811,7 @@ export function scheduleTick(rootContext: RootContext, flags: RootContextFlags) } rootContext.clean = _CLEAN_PROMISE; - res !(null); + res!(null); }); } } @@ -1805,7 +1819,7 @@ export function scheduleTick(rootContext: RootContext, flags: RootContextFlags) export function tickRootContext(rootContext: RootContext) { for (let i = 0; i < rootContext.components.length; i++) { const rootComponent = rootContext.components[i]; - const lView = readPatchedLView(rootComponent) !; + const lView = readPatchedLView(rootComponent)!; const tView = lView[TVIEW]; renderComponentOrTemplate(tView, lView, tView.template, rootComponent); } @@ -1929,7 +1943,7 @@ function getTViewCleanup(tView: TView): any[] { * instead of the current renderer (see the componentSyntheticHost* instructions). */ export function loadComponentRenderer(tNode: TNode, lView: LView): Renderer3 { - const componentLView = unwrapLView(lView[tNode.index]) !; + const componentLView = unwrapLView(lView[tNode.index])!; return componentLView[RENDERER]; } @@ -1958,7 +1972,7 @@ export function setInputsForProperty( ngDevMode && assertDataInRange(lView, index); const def = tView.data[index] as DirectiveDef<any>; if (def.setInput !== null) { - def.setInput !(instance, value, publicName, privateName); + def.setInput!(instance, value, publicName, privateName); } else { instance[privateName] = value; } diff --git a/packages/core/src/render3/instructions/style_prop_interpolation.ts b/packages/core/src/render3/instructions/style_prop_interpolation.ts index cde0b3335b559..86eaff4cb5c8d 100644 --- a/packages/core/src/render3/instructions/style_prop_interpolation.ts +++ b/packages/core/src/render3/instructions/style_prop_interpolation.ts @@ -39,7 +39,7 @@ import {checkStylingProperty} from './styling'; */ export function ɵɵstylePropInterpolate1( prop: string, prefix: string, v0: any, suffix: string, - valueSuffix?: string | null): typeof ɵɵstylePropInterpolate1 { + valueSuffix?: string|null): typeof ɵɵstylePropInterpolate1 { const lView = getLView(); const interpolatedValue = interpolation1(lView, prefix, v0, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); @@ -76,7 +76,7 @@ export function ɵɵstylePropInterpolate1( */ export function ɵɵstylePropInterpolate2( prop: string, prefix: string, v0: any, i0: string, v1: any, suffix: string, - valueSuffix?: string | null): typeof ɵɵstylePropInterpolate2 { + valueSuffix?: string|null): typeof ɵɵstylePropInterpolate2 { const lView = getLView(); const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); @@ -115,7 +115,7 @@ export function ɵɵstylePropInterpolate2( */ export function ɵɵstylePropInterpolate3( prop: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string, - valueSuffix?: string | null): typeof ɵɵstylePropInterpolate3 { + valueSuffix?: string|null): typeof ɵɵstylePropInterpolate3 { const lView = getLView(); const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); @@ -156,7 +156,7 @@ export function ɵɵstylePropInterpolate3( */ export function ɵɵstylePropInterpolate4( prop: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, - v3: any, suffix: string, valueSuffix?: string | null): typeof ɵɵstylePropInterpolate4 { + v3: any, suffix: string, valueSuffix?: string|null): typeof ɵɵstylePropInterpolate4 { const lView = getLView(); const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); @@ -200,7 +200,7 @@ export function ɵɵstylePropInterpolate4( export function ɵɵstylePropInterpolate5( prop: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, suffix: string, - valueSuffix?: string | null): typeof ɵɵstylePropInterpolate5 { + valueSuffix?: string|null): typeof ɵɵstylePropInterpolate5 { const lView = getLView(); const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix); @@ -247,7 +247,7 @@ export function ɵɵstylePropInterpolate5( export function ɵɵstylePropInterpolate6( prop: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string, - valueSuffix?: string | null): typeof ɵɵstylePropInterpolate6 { + valueSuffix?: string|null): typeof ɵɵstylePropInterpolate6 { const lView = getLView(); const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix); @@ -297,7 +297,7 @@ export function ɵɵstylePropInterpolate6( export function ɵɵstylePropInterpolate7( prop: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string, - valueSuffix?: string | null): typeof ɵɵstylePropInterpolate7 { + valueSuffix?: string|null): typeof ɵɵstylePropInterpolate7 { const lView = getLView(); const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix); @@ -349,7 +349,7 @@ export function ɵɵstylePropInterpolate7( export function ɵɵstylePropInterpolate8( prop: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, - suffix: string, valueSuffix?: string | null): typeof ɵɵstylePropInterpolate8 { + suffix: string, valueSuffix?: string|null): typeof ɵɵstylePropInterpolate8 { const lView = getLView(); const interpolatedValue = interpolation8( lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix); @@ -388,7 +388,7 @@ export function ɵɵstylePropInterpolate8( * @codeGenApi */ export function ɵɵstylePropInterpolateV( - prop: string, values: any[], valueSuffix?: string | null): typeof ɵɵstylePropInterpolateV { + prop: string, values: any[], valueSuffix?: string|null): typeof ɵɵstylePropInterpolateV { const lView = getLView(); const interpolatedValue = interpolationV(lView, values); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); diff --git a/packages/core/src/render3/instructions/styling.ts b/packages/core/src/render3/instructions/styling.ts index 6370ebd952b3d..16c53d4f2796e 100644 --- a/packages/core/src/render3/instructions/styling.ts +++ b/packages/core/src/render3/instructions/styling.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 {SafeValue, unwrapSafeValue} from '../../sanitization/bypass'; import {stylePropNeedsSanitization, ɵɵsanitizeStyle} from '../../sanitization/sanitization'; @@ -19,7 +19,7 @@ import {DirectiveDef} from '../interfaces/definition'; import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node'; import {RElement, Renderer3} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; -import {TStylingKey, TStylingRange, getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate} from '../interfaces/styling'; +import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling'; import {HEADER_OFFSET, LView, RENDERER, TData, TView} from '../interfaces/view'; import {applyStyling} from '../node_manipulation'; import {getCurrentDirectiveIndex, getCurrentStyleSanitizer, getLView, getSelectedIndex, getTView, incrementBindingIndex, setCurrentStyleSanitizer} from '../state'; @@ -27,6 +27,7 @@ import {insertTStylingBinding} from '../styling/style_binding_list'; import {getLastParsedKey, getLastParsedValue, parseClassName, parseClassNameNext, parseStyle, parseStyleNext} from '../styling/styling_parser'; import {NO_CHANGE} from '../tokens'; import {getNativeByIndex} from '../util/view_utils'; + import {setDirectiveInputsWhichShadowsStyling} from './property'; @@ -46,7 +47,7 @@ import {setDirectiveInputsWhichShadowsStyling} from './property'; * * @codeGenApi */ -export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): void { +export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn|null): void { setCurrentStyleSanitizer(sanitizer); } @@ -72,8 +73,8 @@ export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): void { * @codeGenApi */ export function ɵɵstyleProp( - prop: string, value: string | number | SafeValue | undefined | null, - suffix?: string | null): typeof ɵɵstyleProp { + prop: string, value: string|number|SafeValue|undefined|null, + suffix?: string|null): typeof ɵɵstyleProp { checkStylingProperty(prop, value, suffix, false); return ɵɵstyleProp; } @@ -93,8 +94,7 @@ export function ɵɵstyleProp( * * @codeGenApi */ -export function ɵɵclassProp( - className: string, value: boolean | undefined | null): typeof ɵɵclassProp { +export function ɵɵclassProp(className: string, value: boolean|undefined|null): typeof ɵɵclassProp { checkStylingProperty(className, value, null, true); return ɵɵclassProp; } @@ -119,7 +119,7 @@ export function ɵɵclassProp( * * @codeGenApi */ -export function ɵɵstyleMap(styles: {[styleName: string]: any} | string | undefined | null): void { +export function ɵɵstyleMap(styles: {[styleName: string]: any}|string|undefined|null): void { checkStylingMap(styleKeyValueArraySet, styleStringParser, styles, false); } @@ -158,8 +158,8 @@ export function styleStringParser(keyValueArray: KeyValueArray<any>, text: strin * * @codeGenApi */ -export function ɵɵclassMap( - classes: {[className: string]: boolean | undefined | null} | string | undefined | null): void { +export function ɵɵclassMap(classes: {[className: string]: boolean|undefined|null}|string|undefined| + null): void { checkStylingMap(keyValueArraySet, classStringParser, classes, true); } @@ -187,8 +187,8 @@ export function classStringParser(keyValueArray: KeyValueArray<any>, text: strin * @param isClassBased `true` if `class` change (`false` if `style`) */ export function checkStylingProperty( - prop: string, value: any | NO_CHANGE, - suffixOrSanitizer: SanitizerFn | string | undefined | null, isClassBased: boolean): void { + prop: string, value: any|NO_CHANGE, suffixOrSanitizer: SanitizerFn|string|undefined|null, + isClassBased: boolean): void { const lView = getLView(); const tView = getTView(); // Styling instructions use 2 slots per binding. @@ -289,14 +289,14 @@ function isInHostBindings(tView: TView, bindingIndex: number): boolean { } /** -* Collects the necessary information to insert the binding into a linked list of style bindings -* using `insertTStylingBinding`. -* -* @param tView `TView` where the binding linked list will be stored. -* @param tStylingKey Property/key of the binding. -* @param bindingIndex Index of binding associated with the `prop` -* @param isClassBased `true` if `class` change (`false` if `style`) -*/ + * Collects the necessary information to insert the binding into a linked list of style bindings + * using `insertTStylingBinding`. + * + * @param tView `TView` where the binding linked list will be stored. + * @param tStylingKey Property/key of the binding. + * @param bindingIndex Index of binding associated with the `prop` + * @param isClassBased `true` if `class` change (`false` if `style`) + */ function stylingFirstUpdatePass( tView: TView, tStylingKey: TStylingKey, bindingIndex: number, isClassBased: boolean): void { ngDevMode && assertFirstUpdatePass(tView); @@ -477,9 +477,10 @@ function getTemplateHeadTStylingKey(tData: TData, tNode: TNode, isClassBased: bo function setTemplateHeadTStylingKey( tData: TData, tNode: TNode, isClassBased: boolean, tStylingKey: TStylingKey): void { const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings; - ngDevMode && assertNotEqual( - getTStylingRangeNext(bindings), 0, - 'Expecting to have at least one template styling binding.'); + ngDevMode && + assertNotEqual( + getTStylingRangeNext(bindings), 0, + 'Expecting to have at least one template styling binding.'); tData[getTStylingRangePrev(bindings)] = tStylingKey; } @@ -523,7 +524,7 @@ function collectResidual(tData: TData, tNode: TNode, isClassBased: boolean): Key * @param isClassBased `true` if `class` (`false` if `style`) */ function collectStylingFromDirectives( - hostDirectiveDef: DirectiveDef<any>| null, tData: TData, tNode: TNode, stylingKey: TStylingKey, + hostDirectiveDef: DirectiveDef<any>|null, tData: TData, tNode: TNode, stylingKey: TStylingKey, isClassBased: boolean): TStylingKey { // We need to loop because there can be directives which have `hostAttrs` but don't have // `hostBindings` so this loop catches up to the current directive.. @@ -559,7 +560,7 @@ function collectStylingFromDirectives( * @param isClassBased `true` if `class` (`false` if `style`) */ function collectStylingFromTAttrs( - stylingKey: TStylingKey | undefined, attrs: TAttributes | null, + stylingKey: TStylingKey|undefined, attrs: TAttributes|null, isClassBased: boolean): TStylingKey { const desiredMarker = isClassBased ? AttributeMarker.Classes : AttributeMarker.Styles; let currentMarker = AttributeMarker.ImplicitAttributes; @@ -711,7 +712,7 @@ function updateStylingMap( setKey = newKey; setValue = newValue; } - } else if (newKey === null || oldKey !== null && oldKey < newKey !) { + } else if (newKey === null || oldKey !== null && oldKey < newKey!) { // DELETE: oldKey key is missing or we did not find the oldKey in the newValue // (because the keyValueArray is sorted and `newKey` is found later alphabetically). // `"background" < "color"` so we need to delete `"background"` because it is not found in the @@ -754,7 +755,7 @@ function updateStylingMap( */ function updateStyling( tView: TView, tNode: TNode, lView: LView, renderer: Renderer3, prop: string, - value: string | undefined | null | boolean, isClassBased: boolean, bindingIndex: number) { + value: string|undefined|null|boolean, isClassBased: boolean, bindingIndex: number) { if (tNode.type !== TNodeType.Element) { // It is possible to have styling on non-elements (such as ng-container). // This is rare, but it does happen. In such a case, just ignore the binding. @@ -811,7 +812,7 @@ function updateStyling( * @param isClassBased `true` if `class` (`false` if `style`) */ function findStylingValue( - tData: TData, tNode: TNode | null, lView: LView, prop: string, index: number, + tData: TData, tNode: TNode|null, lView: LView, prop: string, index: number, isClassBased: boolean): any { // `TNode` to use for resolving static styling. Also controls search direction. // - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true. @@ -856,7 +857,7 @@ function findStylingValue( // consult residual styling let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles; if (residual != null /** OR residual !=== undefined */) { - value = keyValueArrayGet(residual !, prop); + value = keyValueArrayGet(residual!, prop); } } return value; @@ -884,7 +885,7 @@ function isStylingValuePresent(value: any): boolean { * @param suffixOrSanitizer */ function normalizeAndApplySuffixOrSanitizer( - value: any, suffixOrSanitizer: SanitizerFn | string | undefined | null): string|null|undefined| + value: any, suffixOrSanitizer: SanitizerFn|string|undefined|null): string|null|undefined| boolean { if (value == null /** || value === undefined */) { // do nothing diff --git a/packages/core/src/render3/instructions/text.ts b/packages/core/src/render3/instructions/text.ts index b7b1c909060bb..a6ab9338223b3 100644 --- a/packages/core/src/render3/instructions/text.ts +++ b/packages/core/src/render3/instructions/text.ts @@ -27,9 +27,10 @@ export function ɵɵtext(index: number, value: string = ''): void { const tView = getTView(); const adjustedIndex = index + HEADER_OFFSET; - ngDevMode && assertEqual( - getBindingIndex(), tView.bindingStartIndex, - 'text nodes should be created before any bindings'); + ngDevMode && + assertEqual( + getBindingIndex(), tView.bindingStartIndex, + 'text nodes should be created before any bindings'); ngDevMode && assertDataInRange(lView, adjustedIndex); const tNode = tView.firstCreatePass ? diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index 34f5538cb8899..31ea33b9da1fb 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -22,7 +22,7 @@ export type ComponentTemplate<T> = { // Note: the ctx parameter is typed as T|U, as using only U would prevent a template with // e.g. ctx: {} from being assigned to ComponentTemplate<any> as TypeScript won't infer U = any // in that scenario. By including T this incompatibility is resolved. - <U extends T>(rf: RenderFlags, ctx: T | U): void; + <U extends T>(rf: RenderFlags, ctx: T|U): void; }; /** @@ -72,7 +72,9 @@ export const enum RenderFlags { * A subclass of `Type` which has a static `ɵcmp`:`ComponentDef` field making it * consumable for rendering. */ -export interface ComponentType<T> extends Type<T> { ɵcmp: never; } +export interface ComponentType<T> extends Type<T> { + ɵcmp: never; +} /** * A subclass of `Type` which has a static `ɵdir`:`DirectiveDef` field making it @@ -87,14 +89,52 @@ export interface DirectiveType<T> extends Type<T> { * A subclass of `Type` which has a static `ɵpipe`:`PipeDef` field making it * consumable for rendering. */ -export interface PipeType<T> extends Type<T> { ɵpipe: never; } +export interface PipeType<T> extends Type<T> { + ɵpipe: never; +} + +/** + * An object literal of this type is used to represent the metadata of a constructor dependency. + * The type itself is never referred to from generated code. + */ +export type CtorDependency = { + /** + * If an `@Attribute` decorator is used, this represents the injected attribute's name. If the + * attribute name is a dynamic expression instead of a string literal, this will be the unknown + * type. + */ + attribute?: string|unknown; + + /** + * If `@Optional()` is used, this key is set to true. + */ + optional?: true; + + /** + * If `@Host` is used, this key is set to true. + */ + host?: true; + + /** + * If `@Self` is used, this key is set to true. + */ + self?: true; + + /** + * If `@SkipSelf` is used, this key is set to true. + */ + skipSelf?: true; +}|null; /** * @codeGenApi */ export type ɵɵDirectiveDefWithMeta< - T, Selector extends string, ExportAs extends string[], InputMap extends{[key: string]: string}, - OutputMap extends{[key: string]: string}, QueryFields extends string[]> = DirectiveDef<T>; + T, Selector extends string, ExportAs extends + string[], InputMap extends {[key: string]: string}, + OutputMap extends {[key: string]: string}, + QueryFields extends string[]> = + DirectiveDef<T>; /** * Runtime link information for Directives. @@ -235,13 +275,15 @@ export interface DirectiveDef<T> { * @codeGenApi */ export type ɵɵComponentDefWithMeta< - T, Selector extends String, ExportAs extends string[], InputMap extends{[key: string]: string}, - OutputMap extends{[key: string]: string}, QueryFields extends string[]> = ComponentDef<T>; + T, Selector extends String, ExportAs extends + string[], InputMap extends {[key: string]: string}, + OutputMap extends {[key: string]: string}, QueryFields extends + string[], NgContentSelectors extends string[]> = ComponentDef<T>; /** * @codeGenApi */ -export type ɵɵFactoryDef<T> = () => T; +export type ɵɵFactoryDef<T, CtorDependencies extends CtorDependency[]> = () => T; /** * Runtime link information for Components. @@ -433,14 +475,14 @@ export interface ComponentDefFeature { * * The function is necessary to be able to support forward declarations. */ -export type DirectiveDefListOrFactory = (() => DirectiveDefList) | DirectiveDefList; +export type DirectiveDefListOrFactory = (() => DirectiveDefList)|DirectiveDefList; -export type DirectiveDefList = (DirectiveDef<any>| ComponentDef<any>)[]; +export type DirectiveDefList = (DirectiveDef<any>|ComponentDef<any>)[]; -export type DirectiveTypesOrFactory = (() => DirectiveTypeList) | DirectiveTypeList; +export type DirectiveTypesOrFactory = (() => DirectiveTypeList)|DirectiveTypeList; export type DirectiveTypeList = - (DirectiveType<any>| ComponentType<any>| + (DirectiveType<any>|ComponentType<any>| Type<any>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */)[]; export type HostBindingsFunction<T> = <U extends T>(rf: RenderFlags, ctx: U) => void; @@ -450,14 +492,14 @@ export type HostBindingsFunction<T> = <U extends T>(rf: RenderFlags, ctx: U) => * * The function is necessary to be able to support forward declarations. */ -export type PipeDefListOrFactory = (() => PipeDefList) | PipeDefList; +export type PipeDefListOrFactory = (() => PipeDefList)|PipeDefList; export type PipeDefList = PipeDef<any>[]; -export type PipeTypesOrFactory = (() => PipeTypeList) | PipeTypeList; +export type PipeTypesOrFactory = (() => PipeTypeList)|PipeTypeList; export type PipeTypeList = - (PipeType<any>| Type<any>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */)[]; + (PipeType<any>|Type<any>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */)[]; // Note: This hack is necessary so we don't erroneously get a circular dependency diff --git a/packages/core/src/render3/interfaces/document.ts b/packages/core/src/render3/interfaces/document.ts index 3e630c38ee458..30ca7fbf0fab1 100644 --- a/packages/core/src/render3/interfaces/document.ts +++ b/packages/core/src/render3/interfaces/document.ts @@ -31,7 +31,7 @@ let DOCUMENT: Document|undefined = undefined; * * @param document The object representing the global `document` in this environment. */ -export function setDocument(document: Document | undefined): void { +export function setDocument(document: Document|undefined): void { DOCUMENT = document; } @@ -52,5 +52,5 @@ export function getDocument(): Document { // this should not happen in Angular apps. // Once we support running ivy outside of Angular we will need to publish `setDocument()` as a // public API. Meanwhile we just return `undefined` and let the application fail. - return undefined !; + return undefined!; } diff --git a/packages/core/src/render3/interfaces/i18n.ts b/packages/core/src/render3/interfaces/i18n.ts index 98da1ca9c4298..4143415f42f1c 100644 --- a/packages/core/src/render3/interfaces/i18n.ts +++ b/packages/core/src/render3/interfaces/i18n.ts @@ -66,7 +66,9 @@ export const enum I18nMutateOpCode { export const ELEMENT_MARKER: ELEMENT_MARKER = { marker: 'element' }; -export interface ELEMENT_MARKER { marker: 'element'; } +export interface ELEMENT_MARKER { + marker: 'element'; +} /** * Marks that the next string is for comment. @@ -77,7 +79,9 @@ export const COMMENT_MARKER: COMMENT_MARKER = { marker: 'comment' }; -export interface COMMENT_MARKER { marker: 'comment'; } +export interface COMMENT_MARKER { + marker: 'comment'; +} /** * Array storing OpCode for dynamically creating `i18n` blocks. diff --git a/packages/core/src/render3/interfaces/injector.ts b/packages/core/src/render3/interfaces/injector.ts index a4324225394ed..270c38b8160c5 100644 --- a/packages/core/src/render3/interfaces/injector.ts +++ b/packages/core/src/render3/interfaces/injector.ts @@ -23,7 +23,9 @@ export const INJECTOR_BLOOM_PARENT_SIZE = 9; * The interfaces encodes number of parents `LView`s to traverse and index in the `LView` * pointing to the parent injector. */ -export interface RelativeInjectorLocation { __brand__: 'RelativeInjectorLocationFlags'; } +export interface RelativeInjectorLocation { + __brand__: 'RelativeInjectorLocationFlags'; +} export const enum RelativeInjectorLocationFlags { InjectorIndexMask = 0b111111111111111, @@ -114,20 +116,20 @@ export const NO_PARENT_INJECTOR: RelativeInjectorLocation = -1 as any; */ /** -* Factory for creating instances of injectors in the NodeInjector. -* -* This factory is complicated by the fact that it can resolve `multi` factories as well. -* -* NOTE: Some of the fields are optional which means that this class has two hidden classes. -* - One without `multi` support (most common) -* - One with `multi` values, (rare). -* -* Since VMs can cache up to 4 inline hidden classes this is OK. -* -* - Single factory: Only `resolving` and `factory` is defined. -* - `providers` factory: `componentProviders` is a number and `index = -1`. -* - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`. -*/ + * Factory for creating instances of injectors in the NodeInjector. + * + * This factory is complicated by the fact that it can resolve `multi` factories as well. + * + * NOTE: Some of the fields are optional which means that this class has two hidden classes. + * - One without `multi` support (most common) + * - One with `multi` values, (rare). + * + * Since VMs can cache up to 4 inline hidden classes this is OK. + * + * - Single factory: Only `resolving` and `factory` is defined. + * - `providers` factory: `componentProviders` is a number and `index = -1`. + * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`. + */ export class NodeInjectorFactory { /** * The inject implementation to be activated when using the factory. @@ -234,7 +236,8 @@ export class NodeInjectorFactory { /** * Set to `true` if the token is declared in `viewProviders` (or if it is component). */ - isViewProvider: boolean, injectImplementation: null| + isViewProvider: boolean, + injectImplementation: null| (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T)) { this.canSeeViewProviders = isViewProvider; this.injectImpl = injectImplementation; diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts index e2d662f517c80..a60d7d5f80d42 100644 --- a/packages/core/src/render3/interfaces/node.ts +++ b/packages/core/src/render3/interfaces/node.ts @@ -90,8 +90,10 @@ export const enum TNodeProviderIndexes { /** The index of the first provider on this node is encoded on the least significant bits */ ProvidersStartIndexMask = 0b00000000000000001111111111111111, - /** The count of view providers from the component on this node is encoded on the 16 most - significant bits */ + /** + The count of view providers from the component on this node is encoded on the 16 most + significant bits + */ CptViewProvidersCountShift = 16, CptViewProvidersCountShifter = 0b00000000000000010000000000000000, } @@ -117,21 +119,21 @@ export const enum AttributeMarker { NamespaceURI = 0, /** - * Signals class declaration. - * - * Each value following `Classes` designates a class name to include on the element. - * ## Example: - * - * Given: - * ``` - * <div class="foo bar baz">...<d/vi> - * ``` - * - * the generated code is: - * ``` - * var _c1 = [AttributeMarker.Classes, 'foo', 'bar', 'baz']; - * ``` - */ + * Signals class declaration. + * + * Each value following `Classes` designates a class name to include on the element. + * ## Example: + * + * Given: + * ``` + * <div class="foo bar baz">...<d/vi> + * ``` + * + * the generated code is: + * ``` + * var _c1 = [AttributeMarker.Classes, 'foo', 'bar', 'baz']; + * ``` + */ Classes = 1, /** @@ -235,14 +237,14 @@ export const enum AttributeMarker { * - Special markers acting as flags to alter attributes processing. * - Parsed ngProjectAs selectors. */ -export type TAttributes = (string | AttributeMarker | CssSelector)[]; +export type TAttributes = (string|AttributeMarker|CssSelector)[]; /** * Constants that are associated with a view. Includes: * - Attribute arrays. * - Local definition arrays. */ -export type TConstants = (TAttributes | string)[]; +export type TConstants = (TAttributes|string)[]; /** * Binding data (flyweight) for a particular node that is shared between all templates @@ -700,7 +702,7 @@ export interface TProjectionNode extends TNode { /** * A union type representing all TNode types that can host a directive. */ -export type TDirectiveHostNode = TElementNode | TContainerNode | TElementContainerNode; +export type TDirectiveHostNode = TElementNode|TContainerNode|TElementContainerNode; /** * This mapping is necessary so we can set input properties and output listeners @@ -725,7 +727,7 @@ export type PropertyAliases = { * * e.g. [0, 'change-minified'] */ -export type PropertyAliasValue = (number | string)[]; +export type PropertyAliasValue = (number|string)[]; /** * This array contains information about input properties that @@ -745,7 +747,7 @@ export type PropertyAliasValue = (number | string)[]; * * e.g. [null, ['role-min', 'minified-input', 'button']] */ -export type InitialInputData = (InitialInputs | null)[]; +export type InitialInputData = (InitialInputs|null)[]; /** * Used by InitialInputData to store input properties @@ -766,7 +768,7 @@ export const unusedValueExportToPlacateAjd = 1; /** * Type representing a set of TNodes that can have local refs (`#foo`) placed on them. */ -export type TNodeWithLocalRefs = TContainerNode | TElementNode | TElementContainerNode; +export type TNodeWithLocalRefs = TContainerNode|TElementNode|TElementContainerNode; /** * Type for a function that extracts a value for a local refs. diff --git a/packages/core/src/render3/interfaces/player.ts b/packages/core/src/render3/interfaces/player.ts index 518aba8d28aca..37620de2118b4 100644 --- a/packages/core/src/render3/interfaces/player.ts +++ b/packages/core/src/render3/interfaces/player.ts @@ -25,7 +25,9 @@ export const enum BindingType { Style = 2, } -export interface BindingStore { setValue(prop: string, value: any): void; } +export interface BindingStore { + setValue(prop: string, value: any): void; +} /** * Defines the shape which produces the Player. @@ -50,7 +52,9 @@ export interface PlayerFactoryBuildFn { * `[style]`, `[style.prop]`, `[class]` and `[class.name]` template bindings * all accept a `PlayerFactory` as input and this player factories. */ -export interface PlayerFactory { '__brand__': 'Brand for PlayerFactory that nothing will match'; } +export interface PlayerFactory { + '__brand__': 'Brand for PlayerFactory that nothing will match'; +} export interface PlayerBuilder extends BindingStore { buildPlayer(currentPlayer: Player|null, isFirstRender: boolean): Player|undefined|null; @@ -63,7 +67,13 @@ export interface PlayerBuilder extends BindingStore { * code may compare state by checking if a number is higher or lower than * a certain numeric value. */ -export const enum PlayState {Pending = 0, Running = 1, Paused = 2, Finished = 100, Destroyed = 200} +export const enum PlayState { + Pending = 0, + Running = 1, + Paused = 2, + Finished = 100, + Destroyed = 200 +} /** * The context that stores all the active players and queued player factories present on an element. diff --git a/packages/core/src/render3/interfaces/projection.ts b/packages/core/src/render3/interfaces/projection.ts index 6657ab7ff9527..1ef2c6bca43cc 100644 --- a/packages/core/src/render3/interfaces/projection.ts +++ b/packages/core/src/render3/interfaces/projection.ts @@ -37,7 +37,7 @@ * * See more examples in node_selector_matcher_spec.ts */ -export type CssSelector = (string | SelectorFlags)[]; +export type CssSelector = (string|SelectorFlags)[]; /** * A list of CssSelectors. @@ -58,7 +58,7 @@ export type CssSelectorList = CssSelector[]; * using {@link ViewContainerRef#createComponent}. The last slot that specifies the * wildcard selector will retrieve all projectable nodes which do not match any selector. */ -export type ProjectionSlots = (CssSelectorList | '*')[]; +export type ProjectionSlots = (CssSelectorList|'*')[]; /** Flags used to build up CssSelectors */ export const enum SelectorFlags { diff --git a/packages/core/src/render3/interfaces/query.ts b/packages/core/src/render3/interfaces/query.ts index bb19958d30b3a..e184fa42566ff 100644 --- a/packages/core/src/render3/interfaces/query.ts +++ b/packages/core/src/render3/interfaces/query.ts @@ -7,7 +7,7 @@ */ import {Type} from '../../interface/type'; -import {QueryList} from '../../linker'; +import {QueryList} from '../../linker/query_list'; import {TNode} from './node'; import {TView} from './view'; @@ -145,7 +145,7 @@ export interface TQueries { template(tView: TView, tNode: TNode): void; /** - * A proxy method that iterates over all the TQueries in a given TView and calls the corresponding + * A proxy method that iterates over all the TQueries in a given TView and calls the corresponding * `embeddedTView` on each and every TQuery. * @param tNode */ diff --git a/packages/core/src/render3/interfaces/renderer.ts b/packages/core/src/render3/interfaces/renderer.ts index a61d7c4a90462..b34f5195eb297 100644 --- a/packages/core/src/render3/interfaces/renderer.ts +++ b/packages/core/src/render3/interfaces/renderer.ts @@ -24,9 +24,9 @@ export enum RendererStyleFlags3 { DashCase = 1 << 1 } -export type Renderer3 = ObjectOrientedRenderer3 | ProceduralRenderer3; +export type Renderer3 = ObjectOrientedRenderer3|ProceduralRenderer3; -export type GlobalTargetName = 'document' | 'window' | 'body'; +export type GlobalTargetName = 'document'|'window'|'body'; export type GlobalTargetResolver = (element: any) => { name: GlobalTargetName, target: EventTarget @@ -49,8 +49,8 @@ export interface ObjectOrientedRenderer3 { } /** Returns whether the `renderer` is a `ProceduralRenderer3` */ -export function isProceduralRenderer(renderer: ProceduralRenderer3 | ObjectOrientedRenderer3): - renderer is ProceduralRenderer3 { +export function isProceduralRenderer(renderer: ProceduralRenderer3| + ObjectOrientedRenderer3): renderer is ProceduralRenderer3 { return !!((renderer as any).listen); } @@ -104,8 +104,9 @@ export interface RendererFactory3 { } export const domRendererFactory3: RendererFactory3 = { - createRenderer: (hostElement: RElement | null, rendererType: RendererType2 | null): - Renderer3 => { return getDocument();} + createRenderer: (hostElement: RElement|null, rendererType: RendererType2|null): Renderer3 => { + return getDocument(); + } }; /** Subset of API needed for appending elements and text nodes. */ @@ -175,9 +176,13 @@ export interface RDomTokenList { remove(token: string): void; } -export interface RText extends RNode { textContent: string|null; } +export interface RText extends RNode { + textContent: string|null; +} -export interface RComment extends RNode { textContent: string|null; } +export interface RComment extends RNode { + textContent: string|null; +} // Note: This hack is necessary so we don't erroneously get a circular dependency // failure based on types. diff --git a/packages/core/src/render3/interfaces/styling.ts b/packages/core/src/render3/interfaces/styling.ts index e80ed1fbaee43..050b9756a321c 100644 --- a/packages/core/src/render3/interfaces/styling.ts +++ b/packages/core/src/render3/interfaces/styling.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 {KeyValueArray} from '../../util/array_utils'; import {assertNumber, assertNumberInRange} from '../../util/assert'; @@ -14,7 +14,7 @@ import {assertNumber, assertNumberInRange} from '../../util/assert'; * * See: `TStylingKeyPrimitive` and `TStylingStatic` */ -export type TStylingKey = TStylingKeyPrimitive | TStylingStatic; +export type TStylingKey = TStylingKeyPrimitive|TStylingStatic; /** @@ -27,7 +27,7 @@ export type TStylingKey = TStylingKeyPrimitive | TStylingStatic; * is combined with directive which shadows its input `@Input('class')`. That way the binding * should not participate in the styling resolution. */ -export type TStylingKeyPrimitive = string | null | false; +export type TStylingKeyPrimitive = string|null|false; /** * Store the static values for the styling binding. @@ -119,7 +119,9 @@ export interface TStylingStatic extends KeyValueArray<any> {} * * NOTE: `0` has special significance and represents `null` as in no additional pointer. */ -export interface TStylingRange { __brand__: 'TStylingRange'; } +export interface TStylingRange { + __brand__: 'TStylingRange'; +} /** * Shift and masks constants for encoding two numbers into and duplicate info into a single number. @@ -177,9 +179,8 @@ export function setTStylingRangePrev( tStylingRange: TStylingRange, previous: number): TStylingRange { ngDevMode && assertNumber(tStylingRange, 'expected number'); ngDevMode && assertNumberInRange(previous, 0, StylingRange.UNSIGNED_MASK); - return ( - ((tStylingRange as any as number) & ~StylingRange.PREV_MASK) | - (previous << StylingRange.PREV_SHIFT)) as any; + return (((tStylingRange as any as number) & ~StylingRange.PREV_MASK) | + (previous << StylingRange.PREV_SHIFT)) as any; } export function setTStylingRangePrevDuplicate(tStylingRange: TStylingRange): TStylingRange { @@ -195,9 +196,8 @@ export function getTStylingRangeNext(tStylingRange: TStylingRange): number { export function setTStylingRangeNext(tStylingRange: TStylingRange, next: number): TStylingRange { ngDevMode && assertNumber(tStylingRange, 'expected number'); ngDevMode && assertNumberInRange(next, 0, StylingRange.UNSIGNED_MASK); - return ( - ((tStylingRange as any as number) & ~StylingRange.NEXT_MASK) | // - next << StylingRange.NEXT_SHIFT) as any; + return (((tStylingRange as any as number) & ~StylingRange.NEXT_MASK) | // + next << StylingRange.NEXT_SHIFT) as any; } export function getTStylingRangeNextDuplicate(tStylingRange: TStylingRange): boolean { diff --git a/packages/core/src/render3/interfaces/type_checks.ts b/packages/core/src/render3/interfaces/type_checks.ts index eca8324340cb2..875c409466b49 100644 --- a/packages/core/src/render3/interfaces/type_checks.ts +++ b/packages/core/src/render3/interfaces/type_checks.ts @@ -15,10 +15,10 @@ import {FLAGS, LView, LViewFlags} from './view'; /** -* True if `value` is `LView`. -* @param value wrapped value of `RNode`, `LView`, `LContainer` -*/ -export function isLView(value: RNode | LView | LContainer | {} | null): value is LView { + * True if `value` is `LView`. + * @param value wrapped value of `RNode`, `LView`, `LContainer` + */ +export function isLView(value: RNode|LView|LContainer|{}|null): value is LView { return Array.isArray(value) && typeof value[TYPE] === 'object'; } @@ -26,7 +26,7 @@ export function isLView(value: RNode | LView | LContainer | {} | null): value is * True if `value` is `LContainer`. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ -export function isLContainer(value: RNode | LView | LContainer | {} | null): value is LContainer { +export function isLContainer(value: RNode|LView|LContainer|{}|null): value is LContainer { return Array.isArray(value) && value[TYPE] === true; } diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 9a7e4ee0f8034..eb943a97ffae8 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -365,8 +365,10 @@ export const enum InitPhaseState { /** More flags associated with an LView (saved in LView[PREORDER_HOOK_FLAGS]) */ export const enum PreOrderHookFlags { - /** The index of the next pre-order hook to be called in the hooks array, on the first 16 - bits */ + /** + The index of the next pre-order hook to be called in the hooks array, on the first 16 + bits + */ IndexOfTheNextPreOrderHookMaskMask = 0b01111111111111111, /** @@ -617,7 +619,7 @@ export interface TView { * Even indices: Directive index * Odd indices: Hook function */ - destroyHooks: HookData|null; + destroyHooks: DestroyHookData|null; /** * When a view is destroyed, listeners need to be released and outputs need to be @@ -684,7 +686,11 @@ export interface TView { consts: TConstants|null; } -export const enum RootContextFlags {Empty = 0b00, DetectChanges = 0b01, FlushPlayers = 0b10} +export const enum RootContextFlags { + Empty = 0b00, + DetectChanges = 0b01, + FlushPlayers = 0b10 +} /** @@ -722,6 +728,15 @@ export interface RootContext { flags: RootContextFlags; } +/** Single hook callback function. */ +export type HookFn = () => void; + +/** + * Information necessary to call a hook. E.g. the callback that + * needs to invoked and the index at which to find its context. + */ +export type HookEntry = number|HookFn; + /** * Array of hooks that should be executed for a view and their directive indices. * @@ -734,7 +749,27 @@ export interface RootContext { * Special cases: * - a negative directive index flags an init hook (ngOnInit, ngAfterContentInit, ngAfterViewInit) */ -export type HookData = (number | (() => void))[]; +export type HookData = HookEntry[]; + +/** + * Array of destroy hooks that should be executed for a view and their directive indices. + * + * The array is set up as a series of number/function or number/(number|function)[]: + * - Even indices represent the context with which hooks should be called. + * - Odd indices are the hook functions themselves. If a value at an odd index is an array, + * it represents the destroy hooks of a `multi` provider where: + * - Even indices represent the index of the provider for which we've registered a destroy hook, + * inside of the `multi` provider array. + * - Odd indices are the destroy hook functions. + * For example: + * LView: `[0, 1, 2, AService, 4, [BService, CService, DService]]` + * destroyHooks: `[3, AService.ngOnDestroy, 5, [0, BService.ngOnDestroy, 2, DService.ngOnDestroy]]` + * + * In the example above `AService` is a type provider with an `ngOnDestroy`, whereas `BService`, + * `CService` and `DService` are part of a `multi` provider where only `BService` and `DService` + * have an `ngOnDestroy` hook. + */ +export type DestroyHookData = (HookEntry|HookData)[]; /** * Static data that corresponds to the instance-specific data array on an LView. @@ -764,8 +799,8 @@ export type HookData = (number | (() => void))[]; * Injector bloom filters are also stored here. */ export type TData = - (TNode | PipeDef<any>| DirectiveDef<any>| ComponentDef<any>| number | TStylingRange | - TStylingKey | Type<any>| InjectionToken<any>| TI18n | I18nUpdateOpCodes | null | string)[]; + (TNode|PipeDef<any>|DirectiveDef<any>|ComponentDef<any>|number|TStylingRange|TStylingKey| + Type<any>|InjectionToken<any>|TI18n|I18nUpdateOpCodes|null|string)[]; // Note: This hack is necessary so we don't erroneously get a circular dependency // failure based on types. diff --git a/packages/core/src/render3/jit/directive.ts b/packages/core/src/render3/jit/directive.ts index e77baf8d46d11..0397485773e37 100644 --- a/packages/core/src/render3/jit/directive.ts +++ b/packages/core/src/render3/jit/directive.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {R3DirectiveMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade'; +import {getCompilerFacade, R3DirectiveMetadataFacade} from '../../compiler/compiler_facade'; import {R3ComponentMetadataFacade, R3QueryMetadataFacade} from '../../compiler/compiler_facade_interface'; import {resolveForwardRef} from '../../di/forward_ref'; import {getReflect, reflectDependencies} from '../../di/jit/util'; @@ -69,19 +69,23 @@ export function compileComponent(type: Type<any>, metadata: Component): void { throw new Error(error.join('\n')); } - const jitOptions = getJitOptions(); + // This const was called `jitOptions` previously but had to be renamed to `options` because + // of a bug with Terser that caused optimized JIT builds to throw a `ReferenceError`. + // This bug was investigated in https://github.com/angular/angular-cli/issues/17264. + // We should not rename it back until https://github.com/terser/terser/issues/615 is fixed. + const options = getJitOptions(); let preserveWhitespaces = metadata.preserveWhitespaces; if (preserveWhitespaces === undefined) { - if (jitOptions !== null && jitOptions.preserveWhitespaces !== undefined) { - preserveWhitespaces = jitOptions.preserveWhitespaces; + if (options !== null && options.preserveWhitespaces !== undefined) { + preserveWhitespaces = options.preserveWhitespaces; } else { preserveWhitespaces = false; } } let encapsulation = metadata.encapsulation; if (encapsulation === undefined) { - if (jitOptions !== null && jitOptions.defaultEncapsulation !== undefined) { - encapsulation = jitOptions.defaultEncapsulation; + if (options !== null && options.defaultEncapsulation !== undefined) { + encapsulation = options.defaultEncapsulation; } else { encapsulation = ViewEncapsulation.Emulated; } @@ -91,12 +95,14 @@ export function compileComponent(type: Type<any>, metadata: Component): void { const meta: R3ComponentMetadataFacade = { ...directiveMetadata(type, metadata), typeSourceSpan: compiler.createParseSourceSpan('Component', type.name, templateUrl), - template: metadata.template || '', preserveWhitespaces, + template: metadata.template || '', + preserveWhitespaces, styles: metadata.styles || EMPTY_ARRAY, animations: metadata.animations, directives: [], changeDetection: metadata.changeDetection, - pipes: new Map(), encapsulation, + pipes: new Map(), + encapsulation, interpolation: metadata.interpolation, viewProviders: metadata.viewProviders || null, }; @@ -131,7 +137,7 @@ export function compileComponent(type: Type<any>, metadata: Component): void { function hasSelectorScope<T>(component: Type<T>): component is Type<T>& {ngSelectorScope: Type<any>} { - return (component as{ngSelectorScope?: any}).ngSelectorScope !== undefined; + return (component as {ngSelectorScope?: any}).ngSelectorScope !== undefined; } /** @@ -141,7 +147,7 @@ function hasSelectorScope<T>(component: Type<T>): component is Type<T>& * In the event that compilation is not immediate, `compileDirective` will return a `Promise` which * will resolve when compilation completes and the directive becomes usable. */ -export function compileDirective(type: Type<any>, directive: Directive | null): void { +export function compileDirective(type: Type<any>, directive: Directive|null): void { let ngDirectiveDef: any = null; addDirectiveFactoryDef(type, directive || {}); @@ -175,7 +181,7 @@ function getDirectiveMetadata(type: Type<any>, metadata: Directive) { return {metadata: facade, sourceMapUrl}; } -function addDirectiveFactoryDef(type: Type<any>, metadata: Directive | Component) { +function addDirectiveFactoryDef(type: Type<any>, metadata: Directive|Component) { let ngFactoryDef: any = null; Object.defineProperty(type, NG_FACTORY_DEF, { @@ -221,7 +227,7 @@ export function directiveMetadata(type: Type<any>, metadata: Directive): R3Direc outputs: metadata.outputs || EMPTY_ARRAY, queries: extractQueriesMetadata(type, propMetadata, isContentQuery), lifecycle: {usesOnChanges: reflect.hasLifecycleHook(type, 'ngOnChanges')}, - typeSourceSpan: null !, + typeSourceSpan: null!, usesInheritance: !extendsDirectlyFromObject(type), exportAs: extractExportAs(metadata.exportAs), providers: metadata.providers || null, @@ -287,7 +293,7 @@ function extractQueriesMetadata( return queriesMeta; } -function extractExportAs(exportAs: string | undefined): string[]|null { +function extractExportAs(exportAs: string|undefined): string[]|null { return exportAs === undefined ? null : splitByComma(exportAs); } diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index 49b93eefd19f0..375c17322de5d 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {R3InjectorMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade'; +import {getCompilerFacade, R3InjectorMetadataFacade} from '../../compiler/compiler_facade'; import {resolveForwardRef} from '../../di/forward_ref'; import {NG_INJ_DEF} from '../../di/interface/defs'; import {reflectDependencies} from '../../di/jit/util'; @@ -70,7 +70,7 @@ export function flushModuleScopingQueueAsMuchAsPossible() { * an array of declarations, it will recurse to check each declaration in that array * (which may also be arrays). */ -function isResolvedDeclaration(declaration: any[] | Type<any>): boolean { +function isResolvedDeclaration(declaration: any[]|Type<any>): boolean { if (Array.isArray(declaration)) { return declaration.every(isResolvedDeclaration); } @@ -144,8 +144,9 @@ export function compileNgModuleDefs( Object.defineProperty(moduleType, NG_INJ_DEF, { get: () => { if (ngInjectorDef === null) { - ngDevMode && verifySemanticsOfNgModuleDef( - moduleType as any as NgModuleType, allowDuplicateDeclarationsInRoot); + ngDevMode && + verifySemanticsOfNgModuleDef( + moduleType as any as NgModuleType, allowDuplicateDeclarationsInRoot); const meta: R3InjectorMetadataFacade = { name: moduleType.name, type: moduleType, @@ -174,10 +175,10 @@ function verifySemanticsOfNgModuleDef( moduleType = resolveForwardRef(moduleType); let ngModuleDef: NgModuleDef<any>; if (importingModule) { - ngModuleDef = getNgModuleDef(moduleType) !; + ngModuleDef = getNgModuleDef(moduleType)!; if (!ngModuleDef) { - throw new Error( - `Unexpected value '${moduleType.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`); + throw new Error(`Unexpected value '${moduleType.name}' imported by the module '${ + importingModule.name}'. Please add an @NgModule annotation.`); } } else { ngModuleDef = getNgModuleDef(moduleType, true); @@ -222,8 +223,8 @@ function verifySemanticsOfNgModuleDef( type = resolveForwardRef(type); const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef(type); if (!def) { - errors.push( - `Unexpected value '${stringifyForError(type)}' declared by the module '${stringifyForError(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`); + errors.push(`Unexpected value '${stringifyForError(type)}' declared by the module '${ + stringifyForError(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`); } } @@ -244,8 +245,8 @@ function verifySemanticsOfNgModuleDef( // Modules don't need to be declared or imported. if (combinedDeclarations.lastIndexOf(type) === -1) { // We are exporting something which we don't explicitly declare or import. - errors.push( - `Can't export ${kind} ${stringifyForError(type)} from ${stringifyForError(moduleType)} as it was neither declared nor imported!`); + errors.push(`Can't export ${kind} ${stringifyForError(type)} from ${ + stringifyForError(moduleType)} as it was neither declared nor imported!`); } } } @@ -257,9 +258,13 @@ function verifySemanticsOfNgModuleDef( if (!suppressErrors) { const modules = [existingModule, moduleType].map(stringifyForError).sort(); errors.push( - `Type ${stringifyForError(type)} is part of the declarations of 2 modules: ${modules[0]} and ${modules[1]}! ` + - `Please consider moving ${stringifyForError(type)} to a higher module that imports ${modules[0]} and ${modules[1]}. ` + - `You can also create a new NgModule that exports and includes ${stringifyForError(type)} then import that NgModule in ${modules[0]} and ${modules[1]}.`); + `Type ${stringifyForError(type)} is part of the declarations of 2 modules: ${ + modules[0]} and ${modules[1]}! ` + + `Please consider moving ${stringifyForError(type)} to a higher module that imports ${ + modules[0]} and ${modules[1]}. ` + + `You can also create a new NgModule that exports and includes ${ + stringifyForError( + type)} then import that NgModule in ${modules[0]} and ${modules[1]}.`); } } else { // Mark type as having owner. @@ -271,8 +276,9 @@ function verifySemanticsOfNgModuleDef( type = resolveForwardRef(type); const existingModule = ownerNgModule.get(type); if (!existingModule) { - errors.push( - `Component ${stringifyForError(type)} is not part of any NgModule or the module has not been imported into your module.`); + errors.push(`Component ${ + stringifyForError( + type)} is not part of any NgModule or the module has not been imported into your module.`); } } @@ -298,19 +304,19 @@ function verifySemanticsOfNgModuleDef( type = resolveForwardRef(type); if (getComponentDef(type) || getDirectiveDef(type)) { - throw new Error( - `Unexpected directive '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`); + throw new Error(`Unexpected directive '${type.name}' imported by the module '${ + importingModule.name}'. Please add an @NgModule annotation.`); } if (getPipeDef(type)) { - throw new Error( - `Unexpected pipe '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`); + throw new Error(`Unexpected pipe '${type.name}' imported by the module '${ + importingModule.name}'. Please add an @NgModule annotation.`); } } } -function unwrapModuleWithProvidersImports( - typeOrWithProviders: NgModuleType<any>| {ngModule: NgModuleType<any>}): NgModuleType<any> { +function unwrapModuleWithProvidersImports(typeOrWithProviders: NgModuleType<any>| + {ngModule: NgModuleType<any>}): NgModuleType<any> { typeOrWithProviders = resolveForwardRef(typeOrWithProviders); return (typeOrWithProviders as any).ngModule || typeOrWithProviders; } @@ -321,7 +327,7 @@ function getAnnotation<T>(type: any, name: string): T|null { collect(type.decorators); return annotation; - function collect(annotations: any[] | null) { + function collect(annotations: any[]|null) { if (annotations) { annotations.forEach(readAnnotation); } @@ -391,7 +397,7 @@ function setScopeOnDeclaredComponents(moduleType: Type<any>, ngModule: NgModule) if (declaration.hasOwnProperty(NG_COMP_DEF)) { // A `ɵcmp` field exists - go ahead and patch the component directly. const component = declaration as Type<any>& {ɵcmp: ComponentDef<any>}; - const componentDef = getComponentDef(component) !; + const componentDef = getComponentDef(component)!; patchComponentDefWithScope(componentDef, transitiveScopes); } else if ( !declaration.hasOwnProperty(NG_DIR_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) { @@ -410,11 +416,11 @@ export function patchComponentDefWithScope<C>( componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives) .map( - dir => - dir.hasOwnProperty(NG_COMP_DEF) ? getComponentDef(dir) ! : getDirectiveDef(dir) !) + dir => dir.hasOwnProperty(NG_COMP_DEF) ? getComponentDef(dir)! : getDirectiveDef(dir)! + ) .filter(def => !!def); componentDef.pipeDefs = () => - Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef(pipe) !); + Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef(pipe)!); componentDef.schemas = transitiveScopes.schemas; // Since we avoid Components/Directives/Pipes recompiling in case there are no overrides, we @@ -437,7 +443,7 @@ export function transitiveScopesFor<T>(moduleType: Type<T>): NgModuleTransitiveS if (!isNgModule(moduleType)) { throw new Error(`${moduleType.name} does not have a module def (ɵmod property)`); } - const def = getNgModuleDef(moduleType) !; + const def = getNgModuleDef(moduleType)!; if (def.transitiveCompileScopes !== null) { return def.transitiveCompileScopes; @@ -473,7 +479,9 @@ export function transitiveScopesFor<T>(moduleType: Type<T>): NgModuleTransitiveS }); maybeUnwrapFn(def.declarations).forEach(declared => { - const declaredWithDefs = declared as Type<any>& { ɵpipe?: any; }; + const declaredWithDefs = declared as Type<any>& { + ɵpipe?: any; + }; if (getPipeDef(declaredWithDefs)) { scopes.compilation.pipes.add(declared); @@ -519,7 +527,7 @@ export function transitiveScopesFor<T>(moduleType: Type<T>): NgModuleTransitiveS return scopes; } -function expandModuleWithProviders(value: Type<any>| ModuleWithProviders<{}>): Type<any> { +function expandModuleWithProviders(value: Type<any>|ModuleWithProviders<{}>): Type<any> { if (isModuleWithProviders(value)) { return value.ngModule; } @@ -527,7 +535,7 @@ function expandModuleWithProviders(value: Type<any>| ModuleWithProviders<{}>): T } function isModuleWithProviders(value: any): value is ModuleWithProviders<{}> { - return (value as{ngModule?: any}).ngModule !== undefined; + return (value as {ngModule?: any}).ngModule !== undefined; } function isNgModule<T>(value: Type<T>): value is Type<T>&{ɵmod: NgModuleDef<T>} { diff --git a/packages/core/src/render3/jit/pipe.ts b/packages/core/src/render3/jit/pipe.ts index 2ecd57799f07d..46975ae2ba55a 100644 --- a/packages/core/src/render3/jit/pipe.ts +++ b/packages/core/src/render3/jit/pipe.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {R3PipeMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade'; +import {getCompilerFacade, R3PipeMetadataFacade} from '../../compiler/compiler_facade'; import {reflectDependencies} from '../../di/jit/util'; import {Type} from '../../interface/type'; import {Pipe} from '../../metadata/directives'; diff --git a/packages/core/src/render3/metadata.ts b/packages/core/src/render3/metadata.ts index 4927c671e0fab..919b4ed375ae4 100644 --- a/packages/core/src/render3/metadata.ts +++ b/packages/core/src/render3/metadata.ts @@ -25,43 +25,46 @@ interface TypeWithMetadata extends Type<any> { * tree-shaken away during production builds. */ export function setClassMetadata( - type: Type<any>, decorators: any[] | null, ctorParameters: (() => any[]) | null, - propDecorators: {[field: string]: any} | null): void { + type: Type<any>, decorators: any[]|null, ctorParameters: (() => any[])|null, + propDecorators: {[field: string]: any}|null): void { return noSideEffects(() => { - const clazz = type as TypeWithMetadata; + const clazz = type as TypeWithMetadata; - // We determine whether a class has its own metadata by taking the metadata from the parent - // constructor and checking whether it's the same as the subclass metadata below. We can't use - // `hasOwnProperty` here because it doesn't work correctly in IE10 for static fields that are - // defined by TS. See https://github.com/angular/angular/pull/28439#issuecomment-459349218. - const parentPrototype = clazz.prototype ? Object.getPrototypeOf(clazz.prototype) : null; - const parentConstructor: TypeWithMetadata|null = parentPrototype && parentPrototype.constructor; + // We determine whether a class has its own metadata by taking the metadata from the + // parent constructor and checking whether it's the same as the subclass metadata below. + // We can't use `hasOwnProperty` here because it doesn't work correctly in IE10 for + // static fields that are defined by TS. See + // https://github.com/angular/angular/pull/28439#issuecomment-459349218. + const parentPrototype = clazz.prototype ? Object.getPrototypeOf(clazz.prototype) : null; + const parentConstructor: TypeWithMetadata|null = + parentPrototype && parentPrototype.constructor; - if (decorators !== null) { - if (clazz.decorators !== undefined && - (!parentConstructor || parentConstructor.decorators !== clazz.decorators)) { - clazz.decorators.push(...decorators); - } else { - clazz.decorators = decorators; - } - } - if (ctorParameters !== null) { - // Rather than merging, clobber the existing parameters. If other projects exist which use - // tsickle-style annotations and reflect over them in the same way, this could cause issues, - // but that is vanishingly unlikely. - clazz.ctorParameters = ctorParameters; - } - if (propDecorators !== null) { - // The property decorator objects are merged as it is possible different fields have different - // decorator types. Decorators on individual fields are not merged, as it's also incredibly - // unlikely that a field will be decorated both with an Angular decorator and a non-Angular - // decorator that's also been downleveled. - if (clazz.propDecorators !== undefined && - (!parentConstructor || parentConstructor.propDecorators !== clazz.propDecorators)) { - clazz.propDecorators = {...clazz.propDecorators, ...propDecorators}; - } else { - clazz.propDecorators = propDecorators; - } - } - }) as never; + if (decorators !== null) { + if (clazz.decorators !== undefined && + (!parentConstructor || parentConstructor.decorators !== clazz.decorators)) { + clazz.decorators.push(...decorators); + } else { + clazz.decorators = decorators; + } + } + if (ctorParameters !== null) { + // Rather than merging, clobber the existing parameters. If other projects exist which + // use tsickle-style annotations and reflect over them in the same way, this could + // cause issues, but that is vanishingly unlikely. + clazz.ctorParameters = ctorParameters; + } + if (propDecorators !== null) { + // The property decorator objects are merged as it is possible different fields have + // different decorator types. Decorators on individual fields are not merged, as it's + // also incredibly unlikely that a field will be decorated both with an Angular + // decorator and a non-Angular decorator that's also been downleveled. + if (clazz.propDecorators !== undefined && + (!parentConstructor || + parentConstructor.propDecorators !== clazz.propDecorators)) { + clazz.propDecorators = {...clazz.propDecorators, ...propDecorators}; + } else { + clazz.propDecorators = propDecorators; + } + } + }) as never; } diff --git a/packages/core/src/render3/ng_module_ref.ts b/packages/core/src/render3/ng_module_ref.ts index e3c3326082f6a..5c6a8c8403ffe 100644 --- a/packages/core/src/render3/ng_module_ref.ts +++ b/packages/core/src/render3/ng_module_ref.ts @@ -9,7 +9,7 @@ import {Injector} from '../di/injector'; import {INJECTOR} from '../di/injector_compatibility'; import {InjectFlags} from '../di/interface/injector'; -import {R3Injector, createInjectorWithoutInjectorInstances} from '../di/r3_injector'; +import {createInjectorWithoutInjectorInstances, R3Injector} from '../di/r3_injector'; import {Type} from '../interface/type'; import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver'; import {InternalNgModuleRef, NgModuleFactory as viewEngine_NgModuleFactory, NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory'; @@ -23,7 +23,9 @@ import {getNgLocaleIdDef, getNgModuleDef} from './definition'; import {setLocaleId} from './i18n'; import {maybeUnwrapFn} from './util/misc_utils'; -export interface NgModuleType<T = any> extends Type<T> { ɵmod: NgModuleDef<T>; } +export interface NgModuleType<T = any> extends Type<T> { + ɵmod: NgModuleDef<T>; +} export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements InternalNgModuleRef<T> { // tslint:disable-next-line:require-internal-with-underscore @@ -45,20 +47,23 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna constructor(ngModuleType: Type<T>, public _parent: Injector|null) { super(); const ngModuleDef = getNgModuleDef(ngModuleType); - ngDevMode && assertDefined( - ngModuleDef, - `NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`); + ngDevMode && + assertDefined( + ngModuleDef, + `NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`); const ngLocaleIdDef = getNgLocaleIdDef(ngModuleType); ngLocaleIdDef && setLocaleId(ngLocaleIdDef); - this._bootstrapComponents = maybeUnwrapFn(ngModuleDef !.bootstrap); + this._bootstrapComponents = maybeUnwrapFn(ngModuleDef!.bootstrap); this._r3Injector = createInjectorWithoutInjectorInstances( - ngModuleType, _parent, - [ - {provide: viewEngine_NgModuleRef, useValue: this}, - {provide: viewEngine_ComponentFactoryResolver, useValue: this.componentFactoryResolver} - ], - stringify(ngModuleType)) as R3Injector; + ngModuleType, _parent, + [ + {provide: viewEngine_NgModuleRef, useValue: this}, { + provide: viewEngine_ComponentFactoryResolver, + useValue: this.componentFactoryResolver + } + ], + stringify(ngModuleType)) as R3Injector; // We need to resolve the injector types separately from the injector creation, because // the module might be trying to use this ref in its contructor for DI which will cause a @@ -79,12 +84,12 @@ export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements Interna ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed'); const injector = this._r3Injector; !injector.destroyed && injector.destroy(); - this.destroyCbs !.forEach(fn => fn()); + this.destroyCbs!.forEach(fn => fn()); this.destroyCbs = null; } onDestroy(callback: () => void): void { ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed'); - this.destroyCbs !.push(callback); + this.destroyCbs!.push(callback); } } diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 50c33997e45ed..ce5315440cf40 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -10,6 +10,7 @@ import {Renderer2} from '../core'; import {ViewEncapsulation} from '../metadata/view'; import {addToArray, removeFromArray} from '../util/array_utils'; import {assertDefined, assertDomNode, assertEqual, assertString} from '../util/assert'; + import {assertLContainer, assertLView, assertTNodeForLView} from './assert'; import {attachPatchData} from './context_discovery'; import {ACTIVE_INDEX, ActiveIndexFlag, CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS, NATIVE, unusedValueExportToPlacateAjd as unused1} from './interfaces/container'; @@ -17,9 +18,9 @@ import {ComponentDef} from './interfaces/definition'; import {NodeInjectorFactory} from './interfaces/injector'; import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; -import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; +import {isProceduralRenderer, ProceduralRenderer3, RElement, Renderer3, RNode, RText, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {isLContainer, isLView} from './interfaces/type_checks'; -import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, FLAGS, HOST, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, TView, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; +import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {getLViewParent} from './util/view_traversal_utils'; import {getNativeByTNode, unwrapRNode} from './util/view_utils'; @@ -74,8 +75,8 @@ const enum WalkTNodeTreeAction { * being passed as an argument. */ function applyToElementOrContainer( - action: WalkTNodeTreeAction, renderer: Renderer3, parent: RElement | null, - lNodeToHandle: RNode | LContainer | LView, beforeNode?: RNode | null) { + action: WalkTNodeTreeAction, renderer: Renderer3, parent: RElement|null, + lNodeToHandle: RNode|LContainer|LView, beforeNode?: RNode|null) { // If this slot was allocated for a text node dynamically created by i18n, the text node itself // won't be created until i18nApply() in the update block, so this node should be skipped. // For more info, see "ICU expressions should work inside an ngTemplateOutlet inside an ngFor" @@ -91,7 +92,7 @@ function applyToElementOrContainer( } else if (isLView(lNodeToHandle)) { isComponent = true; ngDevMode && assertDefined(lNodeToHandle[HOST], 'HOST must be defined for a component LView'); - lNodeToHandle = lNodeToHandle[HOST] !; + lNodeToHandle = lNodeToHandle[HOST]!; } const rNode: RNode = unwrapRNode(lNodeToHandle); ngDevMode && !isProceduralRenderer(renderer) && assertDomNode(rNode); @@ -108,7 +109,7 @@ function applyToElementOrContainer( nativeRemoveNode(renderer, rNode, isComponent); } else if (action === WalkTNodeTreeAction.Destroy) { ngDevMode && ngDevMode.rendererDestroyNode++; - (renderer as ProceduralRenderer3).destroyNode !(rNode); + (renderer as ProceduralRenderer3).destroyNode!(rNode); } if (lContainer != null) { applyContainer(renderer, action, lContainer, parent, beforeNode); @@ -136,11 +137,11 @@ export function createTextNode(value: string, renderer: Renderer3): RText { * @param beforeNode The node before which elements should be added, if insert mode */ export function addRemoveViewFromContainer( - tView: TView, lView: LView, insertMode: true, beforeNode: RNode | null): void; + tView: TView, lView: LView, insertMode: true, beforeNode: RNode|null): void; export function addRemoveViewFromContainer( tView: TView, lView: LView, insertMode: false, beforeNode: null): void; export function addRemoveViewFromContainer( - tView: TView, lView: LView, insertMode: boolean, beforeNode: RNode | null): void { + tView: TView, lView: LView, insertMode: boolean, beforeNode: RNode|null): void { const renderParent = getContainerRenderParent(tView.node as TViewNode, lView); ngDevMode && assertNodeType(tView.node as TNode, TNodeType.View); if (renderParent) { @@ -196,13 +197,13 @@ export function destroyViewTree(rootView: LView): void { if (!next) { // Only clean up view when moving to the side or up, as destroy hooks // should be called in order from the bottom up. - while (lViewOrLContainer && !lViewOrLContainer ![NEXT] && lViewOrLContainer !== rootView) { + while (lViewOrLContainer && !lViewOrLContainer![NEXT] && lViewOrLContainer !== rootView) { isLView(lViewOrLContainer) && cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer); lViewOrLContainer = getParentState(lViewOrLContainer, rootView); } if (lViewOrLContainer === null) lViewOrLContainer = rootView; isLView(lViewOrLContainer) && cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer); - next = lViewOrLContainer && lViewOrLContainer ![NEXT]; + next = lViewOrLContainer && lViewOrLContainer![NEXT]; } lViewOrLContainer = next; } @@ -267,7 +268,7 @@ function trackMovedView(declarationContainer: LContainer, lView: LView) { const movedViews = declarationContainer[MOVED_VIEWS]; const insertedLContainer = lView[PARENT] as LContainer; ngDevMode && assertLContainer(insertedLContainer); - const insertedComponentLView = insertedLContainer[PARENT] ![DECLARATION_COMPONENT_VIEW]; + const insertedComponentLView = insertedLContainer[PARENT]![DECLARATION_COMPONENT_VIEW]; ngDevMode && assertDefined(insertedComponentLView, 'Missing insertedComponentLView'); const insertedComponentIsOnPush = (insertedComponentLView[FLAGS] & LViewFlags.CheckAlways) !== LViewFlags.CheckAlways; @@ -291,10 +292,11 @@ function trackMovedView(declarationContainer: LContainer, lView: LView) { function detachMovedView(declarationContainer: LContainer, lView: LView) { ngDevMode && assertLContainer(declarationContainer); - ngDevMode && assertDefined( - declarationContainer[MOVED_VIEWS], - 'A projected view should belong to a non-empty projected views collection'); - const movedViews = declarationContainer[MOVED_VIEWS] !; + ngDevMode && + assertDefined( + declarationContainer[MOVED_VIEWS], + 'A projected view should belong to a non-empty projected views collection'); + const movedViews = declarationContainer[MOVED_VIEWS]!; const declaredViewIndex = movedViews.indexOf(lView); movedViews.splice(declaredViewIndex, 1); } @@ -383,7 +385,7 @@ export function destroyLView(tView: TView, lView: LView) { * @param rootView The rootView, so we don't propagate too far up the view tree * @returns The correct parent LViewOrLContainer */ -export function getParentState(lViewOrLContainer: LView | LContainer, rootView: LView): LView| +export function getParentState(lViewOrLContainer: LView|LContainer, rootView: LView): LView| LContainer|null { let tNode; if (isLView(lViewOrLContainer) && (tNode = lViewOrLContainer[T_HOST]) && @@ -449,7 +451,7 @@ function cleanUpView(tView: TView, lView: LView): void { function removeListeners(tView: TView, lView: LView): void { const tCleanup = tView.cleanup; if (tCleanup !== null) { - const lCleanup = lView[CLEANUP] !; + const lCleanup = lView[CLEANUP]!; for (let i = 0; i < tCleanup.length - 1; i += 2) { if (typeof tCleanup[i] === 'string') { // This is a native DOM listener @@ -484,7 +486,7 @@ function removeListeners(tView: TView, lView: LView): void { /** Calls onDestroy hooks for this view */ function executeOnDestroys(tView: TView, lView: LView): void { - let destroyHooks: HookData|null; + let destroyHooks: DestroyHookData|null; if (tView != null && (destroyHooks = tView.destroyHooks) != null) { for (let i = 0; i < destroyHooks.length; i += 2) { @@ -492,7 +494,15 @@ function executeOnDestroys(tView: TView, lView: LView): void { // Only call the destroy hook if the context has been requested. if (!(context instanceof NodeInjectorFactory)) { - (destroyHooks[i + 1] as() => void).call(context); + const toCall = destroyHooks[i + 1] as HookFn | HookData; + + if (Array.isArray(toCall)) { + for (let j = 0; j < toCall.length; j += 2) { + (toCall[j + 1] as HookFn).call(context[toCall[j] as number]); + } + } else { + toCall.call(context); + } } } } @@ -514,8 +524,9 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme // Skip over element and ICU containers as those are represented by a comment node and // can't be used as a render parent. let parentTNode = tNode.parent; - while (parentTNode != null && (parentTNode.type === TNodeType.ElementContainer || - parentTNode.type === TNodeType.IcuContainer)) { + while (parentTNode != null && + (parentTNode.type === TNodeType.ElementContainer || + parentTNode.type === TNodeType.IcuContainer)) { tNode = parentTNode; parentTNode = tNode.parent; } @@ -523,7 +534,7 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme // If the parent tNode is null, then we are inserting across views: either into an embedded view // or a component view. if (parentTNode == null) { - const hostTNode = currentView[T_HOST] !; + const hostTNode = currentView[T_HOST]!; if (hostTNode.type === TNodeType.View) { // We are inserting a root element of an embedded view We might delay insertion of children // for a given view if it is disconnected. This might happen for 2 main reasons: @@ -575,7 +586,7 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme * actual renderer being used. */ export function nativeInsertBefore( - renderer: Renderer3, parent: RElement, child: RNode, beforeNode: RNode | null): void { + renderer: Renderer3, parent: RElement, child: RNode, beforeNode: RNode|null): void { ngDevMode && ngDevMode.rendererInsertBefore++; if (isProceduralRenderer(renderer)) { renderer.insertBefore(parent, child, beforeNode); @@ -595,7 +606,7 @@ function nativeAppendChild(renderer: Renderer3, parent: RElement, child: RNode): } function nativeAppendOrInsertBefore( - renderer: Renderer3, parent: RElement, child: RNode, beforeNode: RNode | null) { + renderer: Renderer3, parent: RElement, child: RNode, beforeNode: RNode|null) { if (beforeNode !== null) { nativeInsertBefore(renderer, parent, child, beforeNode); } else { @@ -659,11 +670,11 @@ function getNativeAnchorNode(parentTNode: TNode, lView: LView): RNode|null { * @returns Whether or not the child was appended */ export function appendChild( - tView: TView, lView: LView, childEl: RNode | RNode[], childTNode: TNode): void { + tView: TView, lView: LView, childEl: RNode|RNode[], childTNode: TNode): void { const renderParent = getRenderParent(tView, childTNode, lView); if (renderParent != null) { const renderer = lView[RENDERER]; - const parentTNode: TNode = childTNode.parent || lView[T_HOST] !; + const parentTNode: TNode = childTNode.parent || lView[T_HOST]!; const anchorNode = getNativeAnchorNode(parentTNode, lView); if (Array.isArray(childEl)) { for (let i = 0; i < childEl.length; i++) { @@ -680,11 +691,12 @@ export function appendChild( * * Native nodes are returned in the order in which those appear in the native tree (DOM). */ -function getFirstNativeNode(lView: LView, tNode: TNode | null): RNode|null { +function getFirstNativeNode(lView: LView, tNode: TNode|null): RNode|null { if (tNode !== null) { - ngDevMode && assertNodeOfPossibleTypes( - tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer, - TNodeType.IcuContainer, TNodeType.Projection); + ngDevMode && + assertNodeOfPossibleTypes( + tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer, + TNodeType.IcuContainer, TNodeType.Projection); const tNodeType = tNode.type; if (tNodeType === TNodeType.Element) { @@ -708,10 +720,10 @@ function getFirstNativeNode(lView: LView, tNode: TNode | null): RNode|null { const componentHost = componentView[T_HOST] as TElementNode; const parentView = getLViewParent(componentView); const firstProjectedTNode: TNode|null = - (componentHost.projection as(TNode | null)[])[tNode.projection as number]; + (componentHost.projection as (TNode | null)[])[tNode.projection as number]; if (firstProjectedTNode != null) { - return getFirstNativeNode(parentView !, firstProjectedTNode); + return getFirstNativeNode(parentView!, firstProjectedTNode); } else { return getFirstNativeNode(lView, tNode.next); } @@ -757,13 +769,14 @@ export function nativeRemoveNode(renderer: Renderer3, rNode: RNode, isHostElemen * nodes on the LView or projection boundary. */ function applyNodes( - renderer: Renderer3, action: WalkTNodeTreeAction, tNode: TNode | null, lView: LView, - renderParent: RElement | null, beforeNode: RNode | null, isProjection: boolean) { + renderer: Renderer3, action: WalkTNodeTreeAction, tNode: TNode|null, lView: LView, + renderParent: RElement|null, beforeNode: RNode|null, isProjection: boolean) { while (tNode != null) { ngDevMode && assertTNodeForLView(tNode, lView); - ngDevMode && assertNodeOfPossibleTypes( - tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer, - TNodeType.Projection, TNodeType.Projection, TNodeType.IcuContainer); + ngDevMode && + assertNodeOfPossibleTypes( + tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer, + TNodeType.Projection, TNodeType.Projection, TNodeType.IcuContainer); const rawSlotValue = lView[tNode.index]; const tNodeType = tNode.type; if (isProjection) { @@ -814,9 +827,9 @@ function applyNodes( */ function applyView( tView: TView, lView: LView, renderer: Renderer3, action: WalkTNodeTreeAction, - renderParent: RElement | null, beforeNode: RNode | null) { - ngDevMode && assertNodeType(tView.node !, TNodeType.View); - const viewRootTNode: TNode|null = tView.node !.child; + renderParent: RElement|null, beforeNode: RNode|null) { + ngDevMode && assertNodeType(tView.node!, TNodeType.View); + const viewRootTNode: TNode|null = tView.node!.child; applyNodes(renderer, action, viewRootTNode, lView, renderParent, beforeNode, false); } @@ -833,7 +846,7 @@ function applyView( export function applyProjection(tView: TView, lView: LView, tProjectionNode: TProjectionNode) { const renderer = lView[RENDERER]; const renderParent = getRenderParent(tView, tProjectionNode, lView); - const parentTNode = tProjectionNode.parent || lView[T_HOST] !; + const parentTNode = tProjectionNode.parent || lView[T_HOST]!; let beforeNode = getNativeAnchorNode(parentTNode, lView); applyProjectionRecursive( renderer, WalkTNodeTreeAction.Create, lView, tProjectionNode, renderParent, beforeNode); @@ -855,12 +868,12 @@ export function applyProjection(tView: TView, lView: LView, tProjectionNode: TPr */ function applyProjectionRecursive( renderer: Renderer3, action: WalkTNodeTreeAction, lView: LView, - tProjectionNode: TProjectionNode, renderParent: RElement | null, beforeNode: RNode | null) { + tProjectionNode: TProjectionNode, renderParent: RElement|null, beforeNode: RNode|null) { const componentLView = lView[DECLARATION_COMPONENT_VIEW]; const componentNode = componentLView[T_HOST] as TElementNode; ngDevMode && assertEqual(typeof tProjectionNode.projection, 'number', 'expecting projection index'); - const nodeToProjectOrRNodes = componentNode.projection ![tProjectionNode.projection] !; + const nodeToProjectOrRNodes = componentNode.projection![tProjectionNode.projection]!; if (Array.isArray(nodeToProjectOrRNodes)) { // This should not exist, it is a bit of a hack. When we bootstrap a top level node and we // need to support passing projectable nodes, so we cheat and put them in the TNode @@ -895,7 +908,7 @@ function applyProjectionRecursive( */ function applyContainer( renderer: Renderer3, action: WalkTNodeTreeAction, lContainer: LContainer, - renderParent: RElement | null, beforeNode: RNode | null | undefined) { + renderParent: RElement|null, beforeNode: RNode|null|undefined) { ngDevMode && assertLContainer(lContainer); const anchor = lContainer[NATIVE]; // LContainer has its own before node. const native = unwrapRNode(lContainer); @@ -1016,4 +1029,4 @@ export function writeDirectClass(renderer: Renderer3, element: RElement, newValu element.className = newValue; } ngDevMode && ngDevMode.rendererSetClassName++; -} \ No newline at end of file +} diff --git a/packages/core/src/render3/node_selector_matcher.ts b/packages/core/src/render3/node_selector_matcher.ts index 8b539d5bbcb85..1cf74b409dd5a 100644 --- a/packages/core/src/render3/node_selector_matcher.ts +++ b/packages/core/src/render3/node_selector_matcher.ts @@ -46,7 +46,7 @@ function isCssClassMatching( } } else if (item === AttributeMarker.Classes) { // We found the classes section. Start searching for the class. - while (i < attrs.length && typeof(item = attrs[i++]) == 'string') { + while (i < attrs.length && typeof (item = attrs[i++]) == 'string') { // while we have strings if (item.toLowerCase() === cssClassToMatch) return true; } @@ -56,6 +56,15 @@ function isCssClassMatching( return false; } +/** + * Checks whether the `tNode` represents an inline template (e.g. `*ngFor`). + * + * @param tNode current TNode + */ +export function isInlineTemplate(tNode: TNode): boolean { + return tNode.type === TNodeType.Container && tNode.tagName !== NG_TEMPLATE_SELECTOR; +} + /** * Function that checks whether a given tNode matches tag-based selector and has a valid type. * @@ -134,11 +143,9 @@ export function isNodeMatchingSelector( continue; } - const isInlineTemplate = - tNode.type == TNodeType.Container && tNode.tagName !== NG_TEMPLATE_SELECTOR; const attrName = (mode & SelectorFlags.CLASS) ? 'class' : current; const attrIndexInNode = - findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate, isProjectionMode); + findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode); if (attrIndexInNode === -1) { if (isPositive(mode)) return false; @@ -151,9 +158,10 @@ export function isNodeMatchingSelector( if (attrIndexInNode > nameOnlyMarkerIdx) { nodeAttrValue = ''; } else { - ngDevMode && assertNotEqual( - nodeAttrs[attrIndexInNode], AttributeMarker.NamespaceURI, - 'We do not match directives on namespaced attributes'); + ngDevMode && + assertNotEqual( + nodeAttrs[attrIndexInNode], AttributeMarker.NamespaceURI, + 'We do not match directives on namespaced attributes'); // we lowercase the attribute value to be able to match // selectors without case-sensitivity // (selectors are already in lowercase when generated) @@ -208,7 +216,7 @@ function isPositive(mode: SelectorFlags): boolean { * matching against directives. */ function findAttrIndexInNode( - name: string, attrs: TAttributes | null, isInlineTemplate: boolean, + name: string, attrs: TAttributes|null, isInlineTemplate: boolean, isProjectionMode: boolean): number { if (attrs === null) return -1; @@ -289,7 +297,11 @@ function matchTemplateAttribute(attrs: TAttributes, name: string): number { if (i > -1) { i++; while (i < attrs.length) { - if (attrs[i] === name) return i; + const attr = attrs[i]; + // Return in case we checked all template attrs and are switching to the next section in the + // attrs array (that starts with a number that represents an attribute marker). + if (typeof attr === 'number') return -1; + if (attr === name) return i; i++; } } diff --git a/packages/core/src/render3/node_util.ts b/packages/core/src/render3/node_util.ts index 8f750ea399050..39865239908e8 100644 --- a/packages/core/src/render3/node_util.ts +++ b/packages/core/src/render3/node_util.ts @@ -43,7 +43,7 @@ export function getParentInjectorTNode( let parentTNode = startView[T_HOST] as TElementNode; // view offset is superior to 1 while (viewOffset > 1) { - parentView = parentView[DECLARATION_VIEW] !; + parentView = parentView[DECLARATION_VIEW]!; parentTNode = parentView[T_HOST] as TElementNode; viewOffset--; } diff --git a/packages/core/src/render3/pipe.ts b/packages/core/src/render3/pipe.ts index d7e79f0dbdb06..5685c39cfbfc8 100644 --- a/packages/core/src/render3/pipe.ts +++ b/packages/core/src/render3/pipe.ts @@ -11,6 +11,7 @@ import {PipeTransform} from '../change_detection/pipe_transform'; import {setInjectImplementation} from '../di/injector_compatibility'; import {getFactoryDef} from './definition'; +import {setIncludeViewProviders} from './di'; import {store, ɵɵdirectiveInject} from './instructions/all'; import {PipeDef, PipeDefList} from './interfaces/definition'; import {HEADER_OFFSET, LView, TVIEW} from './interfaces/view'; @@ -47,7 +48,12 @@ export function ɵɵpipe(index: number, pipeName: string): any { const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true)); const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject); + + // DI for pipes is supposed to behave like directives when placed on a component + // host node, which means that we have to disable access to `viewProviders`. + const previousIncludeViewProviders = setIncludeViewProviders(false); const pipeInstance = pipeFactory(); + setIncludeViewProviders(previousIncludeViewProviders); setInjectImplementation(previousInjectImplementation); store(tView, getLView(), index, pipeInstance); return pipeInstance; @@ -61,7 +67,7 @@ export function ɵɵpipe(index: number, pipeName: string): any { * @param registry Full list of available pipes * @returns Matching PipeDef */ -function getPipeDef(name: string, registry: PipeDefList | null): PipeDef<any> { +function getPipeDef(name: string, registry: PipeDefList|null): PipeDef<any> { if (registry) { for (let i = registry.length - 1; i >= 0; i--) { const pipeDef = registry[i]; @@ -89,7 +95,8 @@ export function ɵɵpipeBind1(index: number, slotOffset: number, v1: any): any { const lView = getLView(); const pipeInstance = load<PipeTransform>(lView, index); return unwrapValue( - lView, isPure(lView, index) ? + lView, + isPure(lView, index) ? pureFunction1Internal( lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance) : pipeInstance.transform(v1)); @@ -112,7 +119,8 @@ export function ɵɵpipeBind2(index: number, slotOffset: number, v1: any, v2: an const lView = getLView(); const pipeInstance = load<PipeTransform>(lView, index); return unwrapValue( - lView, isPure(lView, index) ? + lView, + isPure(lView, index) ? pureFunction2Internal( lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, pipeInstance) : pipeInstance.transform(v1, v2)); @@ -136,10 +144,11 @@ export function ɵɵpipeBind3(index: number, slotOffset: number, v1: any, v2: an const lView = getLView(); const pipeInstance = load<PipeTransform>(lView, index); return unwrapValue( - lView, isPure(lView, index) ? pureFunction3Internal( - lView, getBindingRoot(), slotOffset, pipeInstance.transform, - v1, v2, v3, pipeInstance) : - pipeInstance.transform(v1, v2, v3)); + lView, + isPure(lView, index) ? pureFunction3Internal( + lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, + v2, v3, pipeInstance) : + pipeInstance.transform(v1, v2, v3)); } /** @@ -162,10 +171,11 @@ export function ɵɵpipeBind4( const lView = getLView(); const pipeInstance = load<PipeTransform>(lView, index); return unwrapValue( - lView, isPure(lView, index) ? pureFunction4Internal( - lView, getBindingRoot(), slotOffset, pipeInstance.transform, - v1, v2, v3, v4, pipeInstance) : - pipeInstance.transform(v1, v2, v3, v4)); + lView, + isPure(lView, index) ? pureFunction4Internal( + lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, + v2, v3, v4, pipeInstance) : + pipeInstance.transform(v1, v2, v3, v4)); } /** @@ -184,7 +194,8 @@ export function ɵɵpipeBindV(index: number, slotOffset: number, values: [any, . const lView = getLView(); const pipeInstance = load<PipeTransform>(lView, index); return unwrapValue( - lView, isPure(lView, index) ? + lView, + isPure(lView, index) ? pureFunctionVInternal( lView, getBindingRoot(), slotOffset, pipeInstance.transform, values, pipeInstance) : pipeInstance.transform.apply(pipeInstance, values)); diff --git a/packages/core/src/render3/pure_function.ts b/packages/core/src/render3/pure_function.ts index 2594661166792..762837c866916 100644 --- a/packages/core/src/render3/pure_function.ts +++ b/packages/core/src/render3/pure_function.ts @@ -154,8 +154,9 @@ export function ɵɵpureFunction5( const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); return bindingUpdated(lView, bindingIndex + 4, exp5) || different ? updateBinding( - lView, bindingIndex + 5, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) : - pureFn(exp1, exp2, exp3, exp4, exp5)) : + lView, bindingIndex + 5, + thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) : + pureFn(exp1, exp2, exp3, exp4, exp5)) : getBinding(lView, bindingIndex + 5); } @@ -184,9 +185,9 @@ export function ɵɵpureFunction6( const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); return bindingUpdated2(lView, bindingIndex + 4, exp5, exp6) || different ? updateBinding( - lView, bindingIndex + 6, thisArg ? - pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) : - pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) : + lView, bindingIndex + 6, + thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) : + pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) : getBinding(lView, bindingIndex + 6); } @@ -217,9 +218,9 @@ export function ɵɵpureFunction7( let different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); return bindingUpdated3(lView, bindingIndex + 4, exp5, exp6, exp7) || different ? updateBinding( - lView, bindingIndex + 7, thisArg ? - pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) : - pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) : + lView, bindingIndex + 7, + thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) : + pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) : getBinding(lView, bindingIndex + 7); } @@ -252,9 +253,9 @@ export function ɵɵpureFunction8( const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); return bindingUpdated4(lView, bindingIndex + 4, exp5, exp6, exp7, exp8) || different ? updateBinding( - lView, bindingIndex + 8, thisArg ? - pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) : - pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) : + lView, bindingIndex + 8, + thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) : + pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) : getBinding(lView, bindingIndex + 8); } diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index ca1b736a1ba3a..3286f439b0436 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -36,8 +36,12 @@ const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4; class LQuery_<T> implements LQuery<T> { matches: (T|null)[]|null = null; constructor(public queryList: QueryList<T>) {} - clone(): LQuery<T> { return new LQuery_(this.queryList); } - setDirty(): void { this.queryList.setDirty(); } + clone(): LQuery<T> { + return new LQuery_(this.queryList); + } + setDirty(): void { + this.queryList.setDirty(); + } } class LQueries_ implements LQueries { @@ -66,9 +70,13 @@ class LQueries_ implements LQueries { return null; } - insertView(tView: TView): void { this.dirtyQueriesWithMatches(tView); } + insertView(tView: TView): void { + this.dirtyQueriesWithMatches(tView); + } - detachView(tView: TView): void { this.dirtyQueriesWithMatches(tView); } + detachView(tView: TView): void { + this.dirtyQueriesWithMatches(tView); + } private dirtyQueriesWithMatches(tView: TView) { for (let i = 0; i < this.queries.length; i++) { @@ -89,8 +97,9 @@ class TQueries_ implements TQueries { constructor(private queries: TQuery[] = []) {} elementStart(tView: TView, tNode: TNode): void { - ngDevMode && assertFirstCreatePass( - tView, 'Queries should collect results on the first template pass only'); + ngDevMode && + assertFirstCreatePass( + tView, 'Queries should collect results on the first template pass only'); for (let i = 0; i < this.queries.length; i++) { this.queries[i].elementStart(tView, tNode); } @@ -121,8 +130,9 @@ class TQueries_ implements TQueries { } template(tView: TView, tNode: TNode): void { - ngDevMode && assertFirstCreatePass( - tView, 'Queries should collect results on the first template pass only'); + ngDevMode && + assertFirstCreatePass( + tView, 'Queries should collect results on the first template pass only'); for (let i = 0; i < this.queries.length; i++) { this.queries[i].template(tView, tNode); } @@ -133,9 +143,13 @@ class TQueries_ implements TQueries { return this.queries[index]; } - get length(): number { return this.queries.length; } + get length(): number { + return this.queries.length; + } - track(tquery: TQuery): void { this.queries.push(tquery); } + track(tquery: TQuery): void { + this.queries.push(tquery); + } } class TQuery_ implements TQuery { @@ -173,7 +187,9 @@ class TQuery_ implements TQuery { } } - template(tView: TView, tNode: TNode): void { this.elementStart(tView, tNode); } + template(tView: TView, tNode: TNode): void { + this.elementStart(tView, tNode); + } embeddedTView(tNode: TNode, childQueryIndex: number): TQuery|null { if (this.isApplyingToNode(tNode)) { @@ -307,15 +323,17 @@ function createSpecialToken(lView: LView, tNode: TNode, read: any): any { } else if (read === ViewEngine_TemplateRef) { return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, lView); } else if (read === ViewContainerRef) { - ngDevMode && assertNodeOfPossibleTypes( - tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer); + ngDevMode && + assertNodeOfPossibleTypes( + tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer); return createContainerRef( ViewContainerRef, ViewEngine_ElementRef, tNode as TElementNode | TContainerNode | TElementContainerNode, lView); } else { ngDevMode && throwError( - `Special token to read should be one of ElementRef, TemplateRef or ViewContainerRef but got ${stringify(read)}.`); + `Special token to read should be one of ElementRef, TemplateRef or ViewContainerRef but got ${ + stringify(read)}.`); } } @@ -325,11 +343,11 @@ function createSpecialToken(lView: LView, tNode: TNode, read: any): any { * doesn't change). */ function materializeViewResults<T>( - tView: TView, lView: LView, tQuery: TQuery, queryIndex: number): (T | null)[] { - const lQuery = lView[QUERIES] !.queries ![queryIndex]; + tView: TView, lView: LView, tQuery: TQuery, queryIndex: number): (T|null)[] { + const lQuery = lView[QUERIES]!.queries![queryIndex]; if (lQuery.matches === null) { const tViewData = tView.data; - const tQueryMatches = tQuery.matches !; + const tQueryMatches = tQuery.matches!; const result: T|null[] = []; for (let i = 0; i < tQueryMatches.length; i += 2) { const matchedNodeIdx = tQueryMatches[i]; @@ -355,7 +373,7 @@ function materializeViewResults<T>( * starting with a provided LView. */ function collectQueryResults<T>(tView: TView, lView: LView, queryIndex: number, result: T[]): T[] { - const tQuery = tView.queries !.getByIndex(queryIndex); + const tQuery = tView.queries!.getByIndex(queryIndex); const tQueryMatches = tQuery.matches; if (tQueryMatches !== null) { const lViewResults = materializeViewResults<T>(tView, lView, tQuery, queryIndex); @@ -381,7 +399,7 @@ function collectQueryResults<T>(tView: TView, lView: LView, queryIndex: number, // collect matches for views created from this declaration container and inserted into // different containers if (declarationLContainer[MOVED_VIEWS] !== null) { - const embeddedLViews = declarationLContainer[MOVED_VIEWS] !; + const embeddedLViews = declarationLContainer[MOVED_VIEWS]!; for (let i = 0; i < embeddedLViews.length; i++) { const embeddedLView = embeddedLViews[i]; collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result); @@ -436,7 +454,7 @@ export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean { * @codeGenApi */ export function ɵɵstaticViewQuery<T>( - predicate: Type<any>| string[], descend: boolean, read?: any): void { + predicate: Type<any>|string[], descend: boolean, read?: any): void { viewQueryInternal(getTView(), getLView(), predicate, descend, read, true); } @@ -449,12 +467,12 @@ export function ɵɵstaticViewQuery<T>( * * @codeGenApi */ -export function ɵɵviewQuery<T>(predicate: Type<any>| string[], descend: boolean, read?: any): void { +export function ɵɵviewQuery<T>(predicate: Type<any>|string[], descend: boolean, read?: any): void { viewQueryInternal(getTView(), getLView(), predicate, descend, read, false); } function viewQueryInternal<T>( - tView: TView, lView: LView, predicate: Type<any>| string[], descend: boolean, read: any, + tView: TView, lView: LView, predicate: Type<any>|string[], descend: boolean, read: any, isStatic: boolean): void { if (tView.firstCreatePass) { createTQuery(tView, new TQueryMetadata_(predicate, descend, isStatic, read), -1); @@ -478,7 +496,7 @@ function viewQueryInternal<T>( * @codeGenApi */ export function ɵɵcontentQuery<T>( - directiveIndex: number, predicate: Type<any>| string[], descend: boolean, read?: any): void { + directiveIndex: number, predicate: Type<any>|string[], descend: boolean, read?: any): void { contentQueryInternal( getTView(), getLView(), predicate, descend, read, false, getPreviousOrParentTNode(), directiveIndex); @@ -497,14 +515,14 @@ export function ɵɵcontentQuery<T>( * @codeGenApi */ export function ɵɵstaticContentQuery<T>( - directiveIndex: number, predicate: Type<any>| string[], descend: boolean, read?: any): void { + directiveIndex: number, predicate: Type<any>|string[], descend: boolean, read?: any): void { contentQueryInternal( getTView(), getLView(), predicate, descend, read, true, getPreviousOrParentTNode(), directiveIndex); } function contentQueryInternal<T>( - tView: TView, lView: LView, predicate: Type<any>| string[], descend: boolean, read: any, + tView: TView, lView: LView, predicate: Type<any>|string[], descend: boolean, read: any, isStatic: boolean, tNode: TNode, directiveIndex: number): void { if (tView.firstCreatePass) { createTQuery(tView, new TQueryMetadata_(predicate, descend, isStatic, read), tNode.index); @@ -529,8 +547,8 @@ export function ɵɵloadQuery<T>(): QueryList<T> { function loadQueryInternal<T>(lView: LView, queryIndex: number): QueryList<T> { ngDevMode && assertDefined(lView[QUERIES], 'LQueries should be defined when trying to load a query'); - ngDevMode && assertDataInRange(lView[QUERIES] !.queries, queryIndex); - return lView[QUERIES] !.queries[queryIndex].queryList; + ngDevMode && assertDataInRange(lView[QUERIES]!.queries, queryIndex); + return lView[QUERIES]!.queries[queryIndex].queryList; } function createLQuery<T>(tView: TView, lView: LView) { @@ -538,7 +556,7 @@ function createLQuery<T>(tView: TView, lView: LView) { storeCleanupWithContext(tView, lView, queryList, queryList.destroy); if (lView[QUERIES] === null) lView[QUERIES] = new LQueries_(); - lView[QUERIES] !.queries.push(new LQuery_(queryList)); + lView[QUERIES]!.queries.push(new LQuery_(queryList)); } function createTQuery(tView: TView, metadata: TQueryMetadata, nodeIndex: number): void { @@ -551,11 +569,11 @@ function saveContentQueryAndDirectiveIndex(tView: TView, directiveIndex: number) const lastSavedDirectiveIndex = tView.contentQueries.length ? tViewContentQueries[tViewContentQueries.length - 1] : -1; if (directiveIndex !== lastSavedDirectiveIndex) { - tViewContentQueries.push(tView.queries !.length - 1, directiveIndex); + tViewContentQueries.push(tView.queries!.length - 1, directiveIndex); } } function getTQuery(tView: TView, index: number): TQuery { ngDevMode && assertDefined(tView.queries, 'TQueries must be defined to retrieve a TQuery'); - return tView.queries !.getByIndex(index); + return tView.queries!.getByIndex(index); } diff --git a/packages/core/src/render3/state.ts b/packages/core/src/render3/state.ts index 2a45f8c6019d9..79210bb34e1c1 100644 --- a/packages/core/src/render3/state.ts +++ b/packages/core/src/render3/state.ts @@ -373,7 +373,7 @@ export function enterDI(newView: LView, tNode: TNode) { ngDevMode && assertLViewOrUndefined(newView); const newLFrame = allocLFrame(); instructionState.lFrame = newLFrame; - newLFrame.previousOrParentTNode = tNode !; + newLFrame.previousOrParentTNode = tNode!; newLFrame.lView = newView; } @@ -389,7 +389,7 @@ export function enterDI(newView: LView, tNode: TNode) { * @param tNode Element to which the View is a child of * @returns the previously active lView; */ -export function enterView(newView: LView, tNode: TNode | null): void { +export function enterView(newView: LView, tNode: TNode|null): void { ngDevMode && assertLViewOrUndefined(newView); const newLFrame = allocLFrame(); if (ngDevMode) { @@ -406,10 +406,10 @@ export function enterView(newView: LView, tNode: TNode | null): void { } const tView = newView[TVIEW]; instructionState.lFrame = newLFrame; - newLFrame.previousOrParentTNode = tNode !; + newLFrame.previousOrParentTNode = tNode!; newLFrame.lView = newView; newLFrame.tView = tView; - newLFrame.contextLView = newView !; + newLFrame.contextLView = newView!; newLFrame.bindingIndex = tView.bindingStartIndex; } @@ -423,23 +423,23 @@ function allocLFrame() { return newLFrame; } -function createLFrame(parent: LFrame | null): LFrame { +function createLFrame(parent: LFrame|null): LFrame { const lFrame: LFrame = { - previousOrParentTNode: null !, // - isParent: true, // - lView: null !, // - tView: null !, // - selectedIndex: 0, // - contextLView: null !, // - elementDepthCount: 0, // - currentNamespace: null, // - currentSanitizer: null, // - currentDirectiveIndex: -1, // - bindingRootIndex: -1, // - bindingIndex: -1, // - currentQueryIndex: 0, // - parent: parent !, // - child: null, // + previousOrParentTNode: null!, // + isParent: true, // + lView: null!, // + tView: null!, // + selectedIndex: 0, // + contextLView: null!, // + elementDepthCount: 0, // + currentNamespace: null, // + currentSanitizer: null, // + currentDirectiveIndex: -1, // + bindingRootIndex: -1, // + bindingIndex: -1, // + currentQueryIndex: 0, // + parent: parent!, // + child: null, // }; parent !== null && (parent.child = lFrame); // link the new LFrame for reuse. return lFrame; @@ -457,8 +457,8 @@ function createLFrame(parent: LFrame | null): LFrame { function leaveViewLight(): LFrame { const oldLFrame = instructionState.lFrame; instructionState.lFrame = oldLFrame.parent; - oldLFrame.previousOrParentTNode = null !; - oldLFrame.lView = null !; + oldLFrame.previousOrParentTNode = null!; + oldLFrame.lView = null!; return oldLFrame; } @@ -481,9 +481,9 @@ export const leaveDI: () => void = leaveViewLight; export function leaveView() { const oldLFrame = leaveViewLight(); oldLFrame.isParent = true; - oldLFrame.tView = null !; + oldLFrame.tView = null!; oldLFrame.selectedIndex = 0; - oldLFrame.contextLView = null !; + oldLFrame.contextLView = null!; oldLFrame.elementDepthCount = 0; oldLFrame.currentDirectiveIndex = -1; oldLFrame.currentNamespace = null; @@ -495,16 +495,17 @@ export function leaveView() { export function nextContextImpl<T = any>(level: number): T { const contextLView = instructionState.lFrame.contextLView = - walkUpViews(level, instructionState.lFrame.contextLView !); + walkUpViews(level, instructionState.lFrame.contextLView!); return contextLView[CONTEXT] as T; } function walkUpViews(nestingLevel: number, currentView: LView): LView { while (nestingLevel > 0) { - ngDevMode && assertDefined( - currentView[DECLARATION_VIEW], - 'Declaration view should be defined if nesting level is greater than 0.'); - currentView = currentView[DECLARATION_VIEW] !; + ngDevMode && + assertDefined( + currentView[DECLARATION_VIEW], + 'Declaration view should be defined if nesting level is greater than 0.'); + currentView = currentView[DECLARATION_VIEW]!; nestingLevel--; } return currentView; @@ -581,7 +582,7 @@ export function getNamespace(): string|null { return instructionState.lFrame.currentNamespace; } -export function setCurrentStyleSanitizer(sanitizer: StyleSanitizeFn | null) { +export function setCurrentStyleSanitizer(sanitizer: StyleSanitizeFn|null) { instructionState.lFrame.currentSanitizer = sanitizer; } diff --git a/packages/core/src/render3/styling/class_differ.ts b/packages/core/src/render3/styling/class_differ.ts index 36b8f05c0b0f0..3f3f5e8f8fc5f 100644 --- a/packages/core/src/render3/styling/class_differ.ts +++ b/packages/core/src/render3/styling/class_differ.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 {assertNotEqual} from '../../util/assert'; import {CharCode} from '../../util/char_code'; diff --git a/packages/core/src/render3/styling/static_styling.ts b/packages/core/src/render3/styling/static_styling.ts index 9a54073ac9e8f..d27fa5519181a 100644 --- a/packages/core/src/render3/styling/static_styling.ts +++ b/packages/core/src/render3/styling/static_styling.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 {concatStringsWithSpace} from '../../util/stringify'; import {assertFirstCreatePass} from '../assert'; diff --git a/packages/core/src/render3/styling/style_binding_list.ts b/packages/core/src/render3/styling/style_binding_list.ts index 808e4e11eb98b..636cce8de520b 100644 --- a/packages/core/src/render3/styling/style_binding_list.ts +++ b/packages/core/src/render3/styling/style_binding_list.ts @@ -1,16 +1,16 @@ /** -* @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 -*/ + * @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 {KeyValueArray, keyValueArrayIndexOf} from '../../util/array_utils'; import {assertDataInRange, assertEqual, assertNotEqual} from '../../util/assert'; import {assertFirstUpdatePass} from '../assert'; import {TNode} from '../interfaces/node'; -import {TStylingKey, TStylingKeyPrimitive, TStylingRange, getTStylingRangeNext, getTStylingRangePrev, setTStylingRangeNext, setTStylingRangeNextDuplicate, setTStylingRangePrev, setTStylingRangePrevDuplicate, toTStylingRange} from '../interfaces/styling'; +import {getTStylingRangeNext, getTStylingRangePrev, setTStylingRangeNext, setTStylingRangeNextDuplicate, setTStylingRangePrev, setTStylingRangePrevDuplicate, toTStylingRange, TStylingKey, TStylingKeyPrimitive, TStylingRange} from '../interfaces/styling'; import {TData} from '../interfaces/view'; import {getTView} from '../state'; @@ -249,9 +249,10 @@ export function insertTStylingBinding( // We are inserting in template section. // We need to set this binding's "previous" to the current template tail tData[index + 1] = toTStylingRange(tmplTail, 0); - ngDevMode && assertEqual( - tmplHead !== 0 && tmplTail === 0, false, - 'Adding template bindings after hostBindings is not allowed.'); + ngDevMode && + assertEqual( + tmplHead !== 0 && tmplTail === 0, false, + 'Adding template bindings after hostBindings is not allowed.'); if (tmplHead === 0) { tmplHead = index; } else { @@ -409,13 +410,14 @@ function isStylingMatch(tStylingKeyCursor: TStylingKey, tStylingKey: TStylingKey ngDevMode && assertNotEqual( Array.isArray(tStylingKey), true, 'Expected that \'tStylingKey\' has been unwrapped'); - if (tStylingKeyCursor === null || // If the cursor is `null` it means that we have map at that + if ( + tStylingKeyCursor === null || // If the cursor is `null` it means that we have map at that // location so we must assume that we have a match. tStylingKey == null || // If `tStylingKey` is `null` then it is a map therefor assume that it // contains a match. (Array.isArray(tStylingKeyCursor) ? tStylingKeyCursor[1] : tStylingKeyCursor) === tStylingKey // If the keys match explicitly than we are a match. - ) { + ) { return true; } else if (Array.isArray(tStylingKeyCursor) && typeof tStylingKey === 'string') { // if we did not find a match, but `tStylingKeyCursor` is `KeyValueArray` that means cursor has diff --git a/packages/core/src/render3/styling/styling_parser.ts b/packages/core/src/render3/styling/styling_parser.ts index 89f24757a14db..1d97f51c8ff5b 100644 --- a/packages/core/src/render3/styling/styling_parser.ts +++ b/packages/core/src/render3/styling/styling_parser.ts @@ -244,7 +244,7 @@ export function consumeSeparator( * @param startIndex Starting index of character where the scan should start. * @param endIndex Ending index of character where the scan should end. * @returns Index after last style value character. -*/ + */ export function consumeStyleValue(text: string, startIndex: number, endIndex: number): number { let ch1 = -1; // 1st previous character let ch2 = -1; // 2nd previous character diff --git a/packages/core/src/render3/util/attrs_utils.ts b/packages/core/src/render3/util/attrs_utils.ts index 4b6201dfa45c0..e9b6939602db4 100644 --- a/packages/core/src/render3/util/attrs_utils.ts +++ b/packages/core/src/render3/util/attrs_utils.ts @@ -8,7 +8,7 @@ import {CharCode} from '../../util/char_code'; import {AttributeMarker, TAttributes} from '../interfaces/node'; import {CssSelector} from '../interfaces/projection'; -import {ProceduralRenderer3, RElement, Renderer3, isProceduralRenderer} from '../interfaces/renderer'; +import {isProceduralRenderer, ProceduralRenderer3, RElement, Renderer3} from '../interfaces/renderer'; @@ -96,7 +96,7 @@ export function setUpAttributes(renderer: Renderer3, native: RElement, attrs: TA * @param marker The attribute marker to test. * @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`). */ -export function isNameOnlyAttributeMarker(marker: string | AttributeMarker | CssSelector) { +export function isNameOnlyAttributeMarker(marker: string|AttributeMarker|CssSelector) { return marker === AttributeMarker.Bindings || marker === AttributeMarker.Template || marker === AttributeMarker.I18n; } @@ -116,7 +116,7 @@ export function isAnimationProp(name: string): boolean { * @param dst Location of where the merged `TAttributes` should end up. * @param src `TAttributes` which should be appended to `dst` */ -export function mergeHostAttrs(dst: TAttributes | null, src: TAttributes | null): TAttributes|null { +export function mergeHostAttrs(dst: TAttributes|null, src: TAttributes|null): TAttributes|null { if (src === null || src.length === 0) { // do nothing } else if (dst === null || dst.length === 0) { @@ -156,8 +156,8 @@ export function mergeHostAttrs(dst: TAttributes | null, src: TAttributes | null) * @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class. */ export function mergeHostAttribute( - dst: TAttributes, marker: AttributeMarker, key1: string, key2: string | null, - value: string | null): void { + dst: TAttributes, marker: AttributeMarker, key1: string, key2: string|null, + value: string|null): void { let i = 0; // Assume that new markers will be inserted at the end. let markerInsertPosition = dst.length; @@ -195,7 +195,7 @@ export function mergeHostAttribute( } return; } else if (key2 === dst[i + 1]) { - dst[i + 2] = value !; + dst[i + 2] = value!; return; } } diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts index 695d2899ff405..41b4ba81d193d 100644 --- a/packages/core/src/render3/util/discovery_utils.ts +++ b/packages/core/src/render3/util/discovery_utils.ts @@ -10,12 +10,13 @@ import {Injector} from '../../di/injector'; import {assertLView} from '../assert'; import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from '../context_discovery'; import {NodeInjector} from '../di'; -import {DebugNode, buildDebugNode} from '../instructions/lview_debug'; +import {buildDebugNode, DebugNode} from '../instructions/lview_debug'; import {LContext} from '../interfaces/context'; import {DirectiveDef} from '../interfaces/definition'; import {TElementNode, TNode, TNodeProviderIndexes} from '../interfaces/node'; import {isLView} from '../interfaces/type_checks'; -import {CLEANUP, CONTEXT, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, TVIEW, T_HOST} from '../interfaces/view'; +import {CLEANUP, CONTEXT, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, T_HOST, TVIEW} from '../interfaces/view'; + import {stringifyForError} from './misc_utils'; import {getLViewParent, getRootContext} from './view_traversal_utils'; import {getTNode, unwrapRNode} from './view_utils'; @@ -93,14 +94,14 @@ export function getContext<T>(element: Element): T|null { * @publicApi * @globalApi ng */ -export function getOwningComponent<T>(elementOrDir: Element | {}): T|null { +export function getOwningComponent<T>(elementOrDir: Element|{}): T|null { const context = loadLContext(elementOrDir, false); if (context === null) return null; let lView = context.lView; let parent: LView|null; ngDevMode && assertLView(lView); - while (lView[HOST] === null && (parent = getLViewParent(lView) !)) { + while (lView[HOST] === null && (parent = getLViewParent(lView)!)) { // As long as lView[HOST] is null we know we are part of sub-template such as `*ngIf` lView = parent; } @@ -118,7 +119,7 @@ export function getOwningComponent<T>(elementOrDir: Element | {}): T|null { * @publicApi * @globalApi ng */ -export function getRootComponents(elementOrDir: Element | {}): {}[] { +export function getRootComponents(elementOrDir: Element|{}): {}[] { return [...getRootContext(elementOrDir).components]; } @@ -132,7 +133,7 @@ export function getRootComponents(elementOrDir: Element | {}): {}[] { * @publicApi * @globalApi ng */ -export function getInjector(elementOrDir: Element | {}): Injector { +export function getInjector(elementOrDir: Element|{}): Injector { const context = loadLContext(elementOrDir, false); if (context === null) return Injector.NULL; @@ -192,7 +193,7 @@ export function getInjectionTokens(element: Element): any[] { * @globalApi ng */ export function getDirectives(element: Element): {}[] { - const context = loadLContext(element) !; + const context = loadLContext(element)!; if (context.directives === undefined) { context.directives = getDirectivesAtNodeIndex(context.nodeIndex, context.lView, false); @@ -250,7 +251,7 @@ export function getLocalRefs(target: {}): {[key: string]: any} { * @globalApi ng */ export function getHostElement(componentOrDirective: {}): Element { - return getLContext(componentOrDirective) !.native as never as Element; + return getLContext(componentOrDirective)!.native as never as Element; } /** @@ -270,7 +271,7 @@ export function getRenderedText(component: any): string { export function loadLContextFromNode(node: Node): LContext { if (!(node instanceof Node)) throw new Error('Expecting instance of DOM Element'); - return loadLContext(node) !; + return loadLContext(node)!; } /** diff --git a/packages/core/src/render3/util/injector_utils.ts b/packages/core/src/render3/util/injector_utils.ts index 0de9682a56404..4d77357745db9 100644 --- a/packages/core/src/render3/util/injector_utils.ts +++ b/packages/core/src/render3/util/injector_utils.ts @@ -37,7 +37,7 @@ export function getParentInjectorView(location: RelativeInjectorLocation, startV // <ng-template> tags or inline views, where the parent injector might live many views // above the child injector. while (viewOffset > 0) { - parentView = parentView[DECLARATION_VIEW] !; + parentView = parentView[DECLARATION_VIEW]!; viewOffset--; } return parentView; diff --git a/packages/core/src/render3/util/misc_utils.ts b/packages/core/src/render3/util/misc_utils.ts index 5d0b9ea648cec..d2805739e1546 100644 --- a/packages/core/src/render3/util/misc_utils.ts +++ b/packages/core/src/render3/util/misc_utils.ts @@ -37,16 +37,18 @@ export function stringifyForError(value: any): string { export const defaultScheduler = - (() => - (typeof requestAnimationFrame !== 'undefined' && requestAnimationFrame || // browser only - setTimeout // everything else - ).bind(global))(); + (() => ( + typeof requestAnimationFrame !== 'undefined' && + requestAnimationFrame || // browser only + setTimeout // everything else + ) + .bind(global))(); /** * * @codeGenApi */ -export function ɵɵresolveWindow(element: RElement & {ownerDocument: Document}) { +export function ɵɵresolveWindow(element: RElement&{ownerDocument: Document}) { return {name: 'window', target: element.ownerDocument.defaultView}; } @@ -54,7 +56,7 @@ export function ɵɵresolveWindow(element: RElement & {ownerDocument: Document}) * * @codeGenApi */ -export function ɵɵresolveDocument(element: RElement & {ownerDocument: Document}) { +export function ɵɵresolveDocument(element: RElement&{ownerDocument: Document}) { return {name: 'document', target: element.ownerDocument}; } @@ -62,7 +64,7 @@ export function ɵɵresolveDocument(element: RElement & {ownerDocument: Document * * @codeGenApi */ -export function ɵɵresolveBody(element: RElement & {ownerDocument: Document}) { +export function ɵɵresolveBody(element: RElement&{ownerDocument: Document}) { return {name: 'body', target: element.ownerDocument.body}; } @@ -85,7 +87,7 @@ export const INTERPOLATION_DELIMITER = `�`; /** * Unwrap a value which might be behind a closure (for forward declaration reasons). */ -export function maybeUnwrapFn<T>(value: T | (() => T)): T { +export function maybeUnwrapFn<T>(value: T|(() => T)): T { if (value instanceof Function) { return value(); } else { diff --git a/packages/core/src/render3/util/view_traversal_utils.ts b/packages/core/src/render3/util/view_traversal_utils.ts index 0be540c9f9ef1..7cda0ad9656f1 100644 --- a/packages/core/src/render3/util/view_traversal_utils.ts +++ b/packages/core/src/render3/util/view_traversal_utils.ts @@ -21,7 +21,7 @@ import {readPatchedLView} from './view_utils'; export function getLViewParent(lView: LView): LView|null { ngDevMode && assertLView(lView); const parent = lView[PARENT]; - return isLContainer(parent) ? parent[PARENT] ! : parent; + return isLContainer(parent) ? parent[PARENT]! : parent; } /** @@ -30,11 +30,11 @@ export function getLViewParent(lView: LView): LView|null { * * @param componentOrLView any component or `LView` */ -export function getRootView(componentOrLView: LView | {}): LView { +export function getRootView(componentOrLView: LView|{}): LView { ngDevMode && assertDefined(componentOrLView, 'component'); - let lView = isLView(componentOrLView) ? componentOrLView : readPatchedLView(componentOrLView) !; + let lView = isLView(componentOrLView) ? componentOrLView : readPatchedLView(componentOrLView)!; while (lView && !(lView[FLAGS] & LViewFlags.IsRoot)) { - lView = getLViewParent(lView) !; + lView = getLViewParent(lView)!; } ngDevMode && assertLView(lView); return lView; @@ -47,7 +47,7 @@ export function getRootView(componentOrLView: LView | {}): LView { * * @param viewOrComponent the `LView` or component to get the root context for. */ -export function getRootContext(viewOrComponent: LView | {}): RootContext { +export function getRootContext(viewOrComponent: LView|{}): RootContext { const rootView = getRootView(viewOrComponent); ngDevMode && assertDefined(rootView[CONTEXT], 'RootView has no context. Perhaps it is disconnected?'); diff --git a/packages/core/src/render3/util/view_utils.ts b/packages/core/src/render3/util/view_utils.ts index d98c91f825c69..54fe62be011fe 100644 --- a/packages/core/src/render3/util/view_utils.ts +++ b/packages/core/src/render3/util/view_utils.ts @@ -11,7 +11,7 @@ import {assertTNodeForLView} from '../assert'; import {ACTIVE_INDEX, ActiveIndexFlag, LContainer, TYPE} from '../interfaces/container'; import {LContext, MONKEY_PATCH_KEY_NAME} from '../interfaces/context'; import {TConstants, TNode} from '../interfaces/node'; -import {RNode, isProceduralRenderer} from '../interfaces/renderer'; +import {isProceduralRenderer, RNode} from '../interfaces/renderer'; import {isLContainer, isLView} from '../interfaces/type_checks'; import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, RENDERER, TData, TView} from '../interfaces/view'; @@ -38,7 +38,7 @@ import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLA * Returns `RNode`. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ -export function unwrapRNode(value: RNode | LView | LContainer): RNode { +export function unwrapRNode(value: RNode|LView|LContainer): RNode { while (Array.isArray(value)) { value = value[HOST] as any; } @@ -49,7 +49,7 @@ export function unwrapRNode(value: RNode | LView | LContainer): RNode { * Returns `LView` or `null` if not found. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ -export function unwrapLView(value: RNode | LView | LContainer): LView|null { +export function unwrapLView(value: RNode|LView|LContainer): LView|null { while (Array.isArray(value)) { // This check is same as `isLView()` but we don't call at as we don't want to call // `Array.isArray()` twice and give JITer more work for inlining. @@ -63,7 +63,7 @@ export function unwrapLView(value: RNode | LView | LContainer): LView|null { * Returns `LContainer` or `null` if not found. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ -export function unwrapLContainer(value: RNode | LView | LContainer): LContainer|null { +export function unwrapLContainer(value: RNode|LView|LContainer): LContainer|null { while (Array.isArray(value)) { // This check is same as `isLContainer()` but we don't call at as we don't want to call // `Array.isArray()` twice and give JITer more work for inlining. @@ -124,7 +124,7 @@ export function getTNode(tView: TView, index: number): TNode { } /** Retrieves a value from any `LView` or `TData`. */ -export function load<T>(view: LView | TData, index: number): T { +export function load<T>(view: LView|TData, index: number): T { ngDevMode && assertDataInRange(view, index + HEADER_OFFSET); return view[index + HEADER_OFFSET]; } @@ -176,8 +176,7 @@ export function viewAttachedToContainer(view: LView): boolean { } /** Returns a constant from `TConstants` instance. */ -export function getConstant<T>(consts: TConstants | null, index: number | null | undefined): T| - null { +export function getConstant<T>(consts: TConstants|null, index: number|null|undefined): T|null { return consts === null || index == null ? null : consts[index] as unknown as T; } diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts index 36c9aa2b75626..5727db3f30639 100644 --- a/packages/core/src/render3/view_engine_compatibility.ts +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -19,13 +19,13 @@ import {addToArray, removeFromArray} from '../util/array_utils'; import {assertDefined, assertEqual, assertGreaterThan, assertLessThan} from '../util/assert'; import {assertLContainer} from './assert'; -import {NodeInjector, getParentInjectorLocation} from './di'; +import {getParentInjectorLocation, NodeInjector} from './di'; import {addToViewTree, createLContainer, createLView, renderView} from './instructions/shared'; import {ActiveIndexFlag, CONTAINER_HEADER_OFFSET, LContainer, VIEW_REFS} from './interfaces/container'; import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node'; -import {RComment, RElement, isProceduralRenderer} from './interfaces/renderer'; +import {isProceduralRenderer, RComment, RElement} from './interfaces/renderer'; import {isComponentHost, isLContainer, isLView, isRootView} from './interfaces/type_checks'; -import {DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, TVIEW, TView, T_HOST} from './interfaces/view'; +import {DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; import {addRemoveViewFromContainer, appendChild, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode, removeView} from './node_manipulation'; import {getParentInjectorTNode} from './node_util'; @@ -46,7 +46,7 @@ export function injectElementRef(ElementRefToken: typeof ViewEngine_ElementRef): return createElementRef(ElementRefToken, getPreviousOrParentTNode(), getLView()); } -let R3ElementRef: {new (native: RElement | RComment): ViewEngine_ElementRef}; +let R3ElementRef: {new (native: RElement|RComment): ViewEngine_ElementRef}; /** * Creates an ElementRef given a node. @@ -95,7 +95,7 @@ export function createTemplateRef<T>( TemplateRefToken: typeof ViewEngine_TemplateRef, ElementRefToken: typeof ViewEngine_ElementRef, hostTNode: TNode, hostView: LView): ViewEngine_TemplateRef<T>|null { if (!R3TemplateRef) { - R3TemplateRef = class TemplateRef<T> extends TemplateRefToken<T> { + R3TemplateRef = class TemplateRef<T> extends TemplateRefToken<T>{ constructor( private _declarationView: LView, private _declarationTContainer: TContainerNode, readonly elementRef: ViewEngine_ElementRef) { @@ -138,7 +138,7 @@ export function createTemplateRef<T>( let R3ViewContainerRef: { new ( - lContainer: LContainer, hostTNode: TElementNode | TContainerNode | TElementContainerNode, + lContainer: LContainer, hostTNode: TElementNode|TContainerNode|TElementContainerNode, hostView: LView): ViewEngine_ViewContainerRef }; @@ -183,7 +183,9 @@ export function createContainerRef( return createElementRef(ElementRefToken, this._hostTNode, this._hostView); } - get injector(): Injector { return new NodeInjector(this._hostTNode, this._hostView); } + get injector(): Injector { + return new NodeInjector(this._hostTNode, this._hostView); + } /** @deprecated No replacement */ get parentInjector(): Injector { @@ -203,10 +205,12 @@ export function createContainerRef( } get(index: number): viewEngine_ViewRef|null { - return this._lContainer[VIEW_REFS] !== null && this._lContainer[VIEW_REFS] ![index] || null; + return this._lContainer[VIEW_REFS] !== null && this._lContainer[VIEW_REFS]![index] || null; } - get length(): number { return this._lContainer.length - CONTAINER_HEADER_OFFSET; } + get length(): number { + return this._lContainer.length - CONTAINER_HEADER_OFFSET; + } createEmbeddedView<C>(templateRef: ViewEngine_TemplateRef<C>, context?: C, index?: number): viewEngine_EmbeddedViewRef<C> { @@ -237,7 +241,7 @@ export function createContainerRef( } insert(viewRef: viewEngine_ViewRef, index?: number): viewEngine_ViewRef { - const lView = (viewRef as ViewRef<any>)._lView !; + const lView = (viewRef as ViewRef<any>)._lView!; const tView = lView[TVIEW]; if (viewRef.destroyed) { @@ -259,9 +263,10 @@ export function createContainerRef( this.detach(prevIdx); } else { const prevLContainer = lView[PARENT] as LContainer; - ngDevMode && assertEqual( - isLContainer(prevLContainer), true, - 'An attached view should have its PARENT point to a container.'); + ngDevMode && + assertEqual( + isLContainer(prevLContainer), true, + 'An attached view should have its PARENT point to a container.'); // We need to re-create a R3ViewContainerRef instance since those are not stored on @@ -281,7 +286,7 @@ export function createContainerRef( addRemoveViewFromContainer(tView, lView, true, beforeNode); (viewRef as ViewRef<any>).attachToViewContainerRef(this); - addToArray(this._lContainer[VIEW_REFS] !, adjustedIdx, viewRef); + addToArray(this._lContainer[VIEW_REFS]!, adjustedIdx, viewRef); return viewRef; } @@ -302,7 +307,7 @@ export function createContainerRef( this.allocateContainerIfNeeded(); const adjustedIdx = this._adjustIndex(index, -1); removeView(this._lContainer, adjustedIdx); - removeFromArray(this._lContainer[VIEW_REFS] !, adjustedIdx); + removeFromArray(this._lContainer[VIEW_REFS]!, adjustedIdx); } detach(index?: number): viewEngine_ViewRef|null { @@ -311,8 +316,8 @@ export function createContainerRef( const view = detachView(this._lContainer, adjustedIdx); const wasDetached = - view && removeFromArray(this._lContainer[VIEW_REFS] !, adjustedIdx) != null; - return wasDetached ? new ViewRef(view !) : null; + view && removeFromArray(this._lContainer[VIEW_REFS]!, adjustedIdx) != null; + return wasDetached ? new ViewRef(view!) : null; } private _adjustIndex(index?: number, shift: number = 0) { @@ -335,8 +340,9 @@ export function createContainerRef( }; } - ngDevMode && assertNodeOfPossibleTypes( - hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); + ngDevMode && + assertNodeOfPossibleTypes( + hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); let lContainer: LContainer; const slotValue = hostView[hostTNode.index]; @@ -363,10 +369,10 @@ export function createContainerRef( // node. if (isRootView(hostView)) { const renderer = hostView[RENDERER]; - const hostNative = getNativeByTNode(hostTNode, hostView) !; + const hostNative = getNativeByTNode(hostTNode, hostView)!; const parentOfHostNative = nativeParentNode(renderer, hostNative); nativeInsertBefore( - renderer, parentOfHostNative !, commentNode, nativeNextSibling(renderer, hostNative)); + renderer, parentOfHostNative!, commentNode, nativeNextSibling(renderer, hostNative)); } else { appendChild(hostView[TVIEW], hostView, commentNode, hostTNode); } @@ -412,7 +418,7 @@ function createViewRef(tNode: TNode, lView: LView, isPipe: boolean): ViewEngine_ const hostComponentView = lView[DECLARATION_COMPONENT_VIEW]; // look up return new ViewRef(hostComponentView, lView); } - return null !; + return null!; } /** Returns a Renderer2 (or throws when application was bootstrapped with Renderer3) */ diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index 722733dbb4940..10f67c6b66b31 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -10,11 +10,12 @@ import {ApplicationRef} from '../application_ref'; import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref'; import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref'; + import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions/shared'; import {CONTAINER_HEADER_OFFSET} from './interfaces/container'; import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node'; import {isLContainer} from './interfaces/type_checks'; -import {CONTEXT, DECLARATION_COMPONENT_VIEW, FLAGS, HOST, LView, LViewFlags, TVIEW, TView, T_HOST} from './interfaces/view'; +import {CONTEXT, DECLARATION_COMPONENT_VIEW, FLAGS, HOST, LView, LViewFlags, T_HOST, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; import {destroyLView, renderDetachView} from './node_manipulation'; import {getLViewParent} from './util/view_traversal_utils'; @@ -28,7 +29,7 @@ import {unwrapRNode} from './util/view_utils'; export interface viewEngine_ChangeDetectorRef_interface extends viewEngine_ChangeDetectorRef {} export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_InternalViewRef, - viewEngine_ChangeDetectorRef_interface { + viewEngine_ChangeDetectorRef_interface { private _appRef: ApplicationRef|null = null; private _viewContainerRef: viewEngine_ViewContainerRef|null = null; @@ -68,7 +69,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int */ private _cdRefInjectingView?: LView) {} - get context(): T { return this._lView[CONTEXT] as T; } + get context(): T { + return this._lView[CONTEXT] as T; + } get destroyed(): boolean { return (this._lView[FLAGS] & LViewFlags.Destroyed) === LViewFlags.Destroyed; @@ -89,7 +92,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int destroyLView(this._lView[TVIEW], this._lView); } - onDestroy(callback: Function) { storeCleanupFn(this._lView[TVIEW], this._lView, callback); } + onDestroy(callback: Function) { + storeCleanupFn(this._lView[TVIEW], this._lView, callback); + } /** * Marks a view and all of its ancestors dirty. @@ -125,7 +130,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * } * ``` */ - markForCheck(): void { markViewDirty(this._cdRefInjectingView || this._lView); } + markForCheck(): void { + markViewDirty(this._cdRefInjectingView || this._lView); + } /** * Detaches the view from the change detection tree. @@ -180,7 +187,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * } * ``` */ - detach(): void { this._lView[FLAGS] &= ~LViewFlags.Attached; } + detach(): void { + this._lView[FLAGS] &= ~LViewFlags.Attached; + } /** * Re-attaches a view to the change detection tree. @@ -238,7 +247,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * } * ``` */ - reattach(): void { this._lView[FLAGS] |= LViewFlags.Attached; } + reattach(): void { + this._lView[FLAGS] |= LViewFlags.Attached; + } /** * Checks the view and its children. @@ -261,7 +272,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * * See {@link ChangeDetectorRef#detach detach} for more information. */ - detectChanges(): void { detectChangesInternal(this._lView[TVIEW], this._lView, this.context); } + detectChanges(): void { + detectChangesInternal(this._lView[TVIEW], this._lView, this.context); + } /** * Checks the change detector and its children, and throws if any changes are detected. @@ -269,7 +282,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int * This is used in development mode to verify that running change detection doesn't * introduce other changes. */ - checkNoChanges(): void { checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context); } + checkNoChanges(): void { + checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context); + } attachToViewContainerRef(vcRef: viewEngine_ViewContainerRef) { if (this._appRef) { @@ -293,22 +308,31 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int /** @internal */ export class RootViewRef<T> extends ViewRef<T> { - constructor(public _view: LView) { super(_view); } + constructor(public _view: LView) { + super(_view); + } - detectChanges(): void { detectChangesInRootView(this._view); } + detectChanges(): void { + detectChangesInRootView(this._view); + } - checkNoChanges(): void { checkNoChangesInRootView(this._view); } + checkNoChanges(): void { + checkNoChangesInRootView(this._view); + } - get context(): T { return null !; } + get context(): T { + return null!; + } } function collectNativeNodes( - tView: TView, lView: LView, tNode: TNode | null, result: any[], + tView: TView, lView: LView, tNode: TNode|null, result: any[], isProjection: boolean = false): any[] { while (tNode !== null) { - ngDevMode && assertNodeOfPossibleTypes( - tNode, TNodeType.Element, TNodeType.Container, TNodeType.Projection, - TNodeType.ElementContainer, TNodeType.IcuContainer); + ngDevMode && + assertNodeOfPossibleTypes( + tNode, TNodeType.Element, TNodeType.Container, TNodeType.Projection, + TNodeType.ElementContainer, TNodeType.IcuContainer); const lNode = lView[tNode.index]; if (lNode !== null) { @@ -337,7 +361,7 @@ function collectNativeNodes( const componentHost = componentView[T_HOST] as TElementNode; const parentView = getLViewParent(componentView); let firstProjectedNode: TNode|null = - (componentHost.projection as(TNode | null)[])[tNode.projection as number]; + (componentHost.projection as (TNode | null)[])[tNode.projection as number]; if (firstProjectedNode !== null && parentView !== null) { collectNativeNodes(parentView[TVIEW], parentView, firstProjectedNode, result, true); } diff --git a/packages/core/src/sanitization/bypass.ts b/packages/core/src/sanitization/bypass.ts index 7a0272cd301cc..01287ad15caed 100644 --- a/packages/core/src/sanitization/bypass.ts +++ b/packages/core/src/sanitization/bypass.ts @@ -70,24 +70,34 @@ abstract class SafeValueImpl implements SafeValue { } class SafeHtmlImpl extends SafeValueImpl implements SafeHtml { - getTypeName() { return BypassType.Html; } + getTypeName() { + return BypassType.Html; + } } class SafeStyleImpl extends SafeValueImpl implements SafeStyle { - getTypeName() { return BypassType.Style; } + getTypeName() { + return BypassType.Style; + } } class SafeScriptImpl extends SafeValueImpl implements SafeScript { - getTypeName() { return BypassType.Script; } + getTypeName() { + return BypassType.Script; + } } class SafeUrlImpl extends SafeValueImpl implements SafeUrl { - getTypeName() { return BypassType.Url; } + getTypeName() { + return BypassType.Url; + } } class SafeResourceUrlImpl extends SafeValueImpl implements SafeResourceUrl { - getTypeName() { return BypassType.ResourceUrl; } + getTypeName() { + return BypassType.ResourceUrl; + } } export function unwrapSafeValue(value: SafeValue): string; export function unwrapSafeValue<T>(value: T): T; -export function unwrapSafeValue<T>(value: T | SafeValue): T { +export function unwrapSafeValue<T>(value: T|SafeValue): T { return value instanceof SafeValueImpl ? value.changingThisBreaksApplicationSecurity as any as T : value as any as T; } diff --git a/packages/core/src/sanitization/html_sanitizer.ts b/packages/core/src/sanitization/html_sanitizer.ts index faf6ce328786f..7a8e710eea068 100644 --- a/packages/core/src/sanitization/html_sanitizer.ts +++ b/packages/core/src/sanitization/html_sanitizer.ts @@ -114,19 +114,19 @@ class SanitizingHtmlSerializer { // This cannot use a TreeWalker, as it has to run on Angular's various DOM adapters. // However this code never accesses properties off of `document` before deleting its contents // again, so it shouldn't be vulnerable to DOM clobbering. - let current: Node = el.firstChild !; + let current: Node = el.firstChild!; let traverseContent = true; while (current) { if (current.nodeType === Node.ELEMENT_NODE) { traverseContent = this.startElement(current as Element); } else if (current.nodeType === Node.TEXT_NODE) { - this.chars(current.nodeValue !); + this.chars(current.nodeValue!); } else { // Strip non-element, non-text nodes. this.sanitizedSomething = true; } if (traverseContent && current.firstChild) { - current = current.firstChild !; + current = current.firstChild!; continue; } while (current) { @@ -135,14 +135,14 @@ class SanitizingHtmlSerializer { this.endElement(current as Element); } - let next = this.checkClobberedElement(current, current.nextSibling !); + let next = this.checkClobberedElement(current, current.nextSibling!); if (next) { current = next; break; } - current = this.checkClobberedElement(current, current.parentNode !); + current = this.checkClobberedElement(current, current.parentNode!); } } return this.buf.join(''); @@ -167,13 +167,13 @@ class SanitizingHtmlSerializer { const elAttrs = element.attributes; for (let i = 0; i < elAttrs.length; i++) { const elAttr = elAttrs.item(i); - const attrName = elAttr !.name; + const attrName = elAttr!.name; const lower = attrName.toLowerCase(); if (!VALID_ATTRS.hasOwnProperty(lower)) { this.sanitizedSomething = true; continue; } - let value = elAttr !.value; + let value = elAttr!.value; // TODO(martinprobst): Special case image URIs for data:image/... if (URI_ATTRS[lower]) value = _sanitizeUrl(value); if (SRCSET_ATTRS[lower]) value = sanitizeSrcset(value); @@ -192,14 +192,16 @@ class SanitizingHtmlSerializer { } } - private chars(chars: string) { this.buf.push(encodeEntities(chars)); } + private chars(chars: string) { + this.buf.push(encodeEntities(chars)); + } checkClobberedElement(node: Node, nextNode: Node): Node { if (nextNode && (node.compareDocumentPosition(nextNode) & Node.DOCUMENT_POSITION_CONTAINED_BY) === Node.DOCUMENT_POSITION_CONTAINED_BY) { - throw new Error( - `Failed to sanitize html because the element is clobbered: ${(node as Element).outerHTML}`); + throw new Error(`Failed to sanitize html because the element is clobbered: ${ + (node as Element).outerHTML}`); } return nextNode; } @@ -227,7 +229,9 @@ function encodeEntities(value: string) { }) .replace( NON_ALPHANUMERIC_REGEXP, - function(match: string) { return '&#' + match.charCodeAt(0) + ';'; }) + function(match: string) { + return '&#' + match.charCodeAt(0) + ';'; + }) .replace(/</g, '<') .replace(/>/g, '>'); } @@ -258,13 +262,13 @@ export function _sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string mXSSAttempts--; unsafeHtml = parsedHtml; - parsedHtml = inertBodyElement !.innerHTML; + parsedHtml = inertBodyElement!.innerHTML; inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml); } while (unsafeHtml !== parsedHtml); const sanitizer = new SanitizingHtmlSerializer(); const safeHtml = sanitizer.sanitizeChildren( - getTemplateContent(inertBodyElement !) as Element || inertBodyElement); + getTemplateContent(inertBodyElement!) as Element || inertBodyElement); if (isDevMode() && sanitizer.sanitizedSomething) { console.warn( 'WARNING: sanitizing HTML stripped some content, see http://g.co/ng/security#xss'); diff --git a/packages/core/src/sanitization/inert_body.ts b/packages/core/src/sanitization/inert_body.ts index 37fc574b66c55..e79311fd08145 100644 --- a/packages/core/src/sanitization/inert_body.ts +++ b/packages/core/src/sanitization/inert_body.ts @@ -80,7 +80,7 @@ export class InertBodyHelper { xhr.open('GET', 'data:text/html;charset=utf-8,' + html, false); xhr.send(undefined); const body: HTMLBodyElement = xhr.response.body; - body.removeChild(body.firstChild !); + body.removeChild(body.firstChild!); return body; } @@ -95,11 +95,9 @@ export class InertBodyHelper { // `<head>` tag. html = '<body><remove></remove>' + html + '</body>'; try { - const body = new (window as any) - .DOMParser() - .parseFromString(html, 'text/html') - .body as HTMLBodyElement; - body.removeChild(body.firstChild !); + const body = new (window as any).DOMParser().parseFromString(html, 'text/html').body as + HTMLBodyElement; + body.removeChild(body.firstChild!); return body; } catch { return null; @@ -152,7 +150,7 @@ export class InertBodyHelper { // loop backwards so that we can support removals. for (let i = elAttrs.length - 1; 0 < i; i--) { const attrib = elAttrs.item(i); - const attrName = attrib !.name; + const attrName = attrib!.name; if (attrName === 'xmlns:ns1' || attrName.indexOf('ns1:') === 0) { el.removeAttribute(attrName); } diff --git a/packages/core/src/sanitization/sanitization.ts b/packages/core/src/sanitization/sanitization.ts index 653f5a5b69ceb..cf9a3a4562579 100644 --- a/packages/core/src/sanitization/sanitization.ts +++ b/packages/core/src/sanitization/sanitization.ts @@ -11,11 +11,11 @@ import {SANITIZER} from '../render3/interfaces/view'; import {getLView} from '../render3/state'; import {renderStringify} from '../render3/util/misc_utils'; -import {BypassType, allowSanitizationBypassAndThrow, unwrapSafeValue} from './bypass'; +import {allowSanitizationBypassAndThrow, BypassType, unwrapSafeValue} from './bypass'; import {_sanitizeHtml as _sanitizeHtml} from './html_sanitizer'; import {Sanitizer} from './sanitizer'; import {SecurityContext} from './security'; -import {StyleSanitizeFn, StyleSanitizeMode, _sanitizeStyle} from './style_sanitizer'; +import {_sanitizeStyle, StyleSanitizeFn, StyleSanitizeMode} from './style_sanitizer'; import {_sanitizeUrl as _sanitizeUrl} from './url_sanitizer'; @@ -152,8 +152,9 @@ export function ɵɵsanitizeScript(unsafeScript: any): string { * If tag and prop names don't match Resource URL schema, use URL sanitizer. */ export function getUrlSanitizer(tag: string, prop: string) { - if ((prop === 'src' && (tag === 'embed' || tag === 'frame' || tag === 'iframe' || - tag === 'media' || tag === 'script')) || + if ((prop === 'src' && + (tag === 'embed' || tag === 'frame' || tag === 'iframe' || tag === 'media' || + tag === 'script')) || (prop === 'href' && (tag === 'base' || tag === 'link'))) { return ɵɵsanitizeResourceUrl; } @@ -186,7 +187,7 @@ export function ɵɵsanitizeUrlOrResourceUrl(unsafeUrl: any, tag: string, prop: * @publicApi */ export const ɵɵdefaultStyleSanitizer = - (function(prop: string, value: string|null, mode?: StyleSanitizeMode): string | boolean | null { + (function(prop: string, value: string|null, mode?: StyleSanitizeMode): string|boolean|null { if (value === undefined && mode === undefined) { // This is a workaround for the fact that `StyleSanitizeFn` should not exist once PR#34480 // lands. For now the `StyleSanitizeFn` and should act like `(value: any) => string` as a diff --git a/packages/core/src/testability/testability.ts b/packages/core/src/testability/testability.ts index 05cfc9c53644b..d4309947a17c7 100644 --- a/packages/core/src/testability/testability.ts +++ b/packages/core/src/testability/testability.ts @@ -133,7 +133,7 @@ export class Testability implements PublicTestability { // Schedules the call backs in a new frame so that it is always async. scheduleMicroTask(() => { while (this._callbacks.length !== 0) { - let cb = this._callbacks.pop() !; + let cb = this._callbacks.pop()!; clearTimeout(cb.timeoutId); cb.doneCb(this._didWork); } @@ -210,7 +210,9 @@ export class Testability implements PublicTestability { * Get the number of pending requests * @deprecated pending requests are now tracked with zones */ - getPendingRequestCount(): number { return this._pendingCount; } + getPendingRequestCount(): number { + return this._pendingCount; + } /** * Find providers by name @@ -233,7 +235,9 @@ export class TestabilityRegistry { /** @internal */ _applications = new Map<any, Testability>(); - constructor() { _testabilityGetter.addToWindow(this); } + constructor() { + _testabilityGetter.addToWindow(this); + } /** * Registers an application with a testability hook so that it can be tracked @@ -248,28 +252,38 @@ export class TestabilityRegistry { * Unregisters an application. * @param token token of application, root element */ - unregisterApplication(token: any) { this._applications.delete(token); } + unregisterApplication(token: any) { + this._applications.delete(token); + } /** * Unregisters all applications */ - unregisterAllApplications() { this._applications.clear(); } + unregisterAllApplications() { + this._applications.clear(); + } /** * Get a testability hook associated with the application * @param elem root element */ - getTestability(elem: any): Testability|null { return this._applications.get(elem) || null; } + getTestability(elem: any): Testability|null { + return this._applications.get(elem) || null; + } /** * Get all registered testabilities */ - getAllTestabilities(): Testability[] { return Array.from(this._applications.values()); } + getAllTestabilities(): Testability[] { + return Array.from(this._applications.values()); + } /** * Get all registered applications(root elements) */ - getAllRootElements(): any[] { return Array.from(this._applications.keys()); } + getAllRootElements(): any[] { + return Array.from(this._applications.keys()); + } /** * Find testability of a node in the Tree diff --git a/packages/core/src/util/WrappedValue.ts b/packages/core/src/util/WrappedValue.ts index 48e5e3f76b33d..a19003474f3ed 100644 --- a/packages/core/src/util/WrappedValue.ts +++ b/packages/core/src/util/WrappedValue.ts @@ -30,17 +30,25 @@ export class WrappedValue { /** @deprecated from 5.3, use `unwrap()` instead - will switch to protected */ wrapped: any; - constructor(value: any) { this.wrapped = value; } + constructor(value: any) { + this.wrapped = value; + } /** Creates a wrapped value. */ - static wrap(value: any): WrappedValue { return new WrappedValue(value); } + static wrap(value: any): WrappedValue { + return new WrappedValue(value); + } /** * Returns the underlying value of a wrapped value. * Returns the given `value` when it is not wrapped. **/ - static unwrap(value: any): any { return WrappedValue.isWrapped(value) ? value.wrapped : value; } + static unwrap(value: any): any { + return WrappedValue.isWrapped(value) ? value.wrapped : value; + } /** Returns true if `value` is a wrapped value. */ - static isWrapped(value: any): value is WrappedValue { return value instanceof WrappedValue; } + static isWrapped(value: any): value is WrappedValue { + return value instanceof WrappedValue; + } } diff --git a/packages/core/src/util/array_utils.ts b/packages/core/src/util/array_utils.ts index 78b1723350b23..08bf5e8b87223 100644 --- a/packages/core/src/util/array_utils.ts +++ b/packages/core/src/util/array_utils.ts @@ -9,11 +9,11 @@ import {assertEqual, assertLessThanOrEqual} from './assert'; /** -* Equivalent to ES6 spread, add each item to an array. -* -* @param items The items to add -* @param arr The array to which you want to add the items -*/ + * Equivalent to ES6 spread, add each item to an array. + * + * @param items The items to add + * @param arr The array to which you want to add the items + */ export function addAllToArray(items: any[], arr: any[]) { for (let i = 0; i < items.length; i++) { arr.push(items[i]); @@ -42,7 +42,7 @@ export function flatten(list: any[], dst?: any[]): any[] { return dst; } -export function deepForEach<T>(input: (T | any[])[], fn: (value: T) => void): void { +export function deepForEach<T>(input: (T|any[])[], fn: (value: T) => void): void { input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value)); } @@ -69,7 +69,7 @@ export function newArray<T>(size: number, value: T): T[]; export function newArray<T>(size: number, value?: T): T[] { const list: T[] = []; for (let i = 0; i < size; i++) { - list.push(value !); + list.push(value!); } return list; } @@ -228,7 +228,9 @@ export function arrayIndexOfSorted(array: string[], value: string): number { * * See: `keyValueArraySet`, `keyValueArrayGet`, `keyValueArrayIndexOf`, `keyValueArrayDelete`. */ -export interface KeyValueArray<VALUE> extends Array<VALUE|string> { __brand__: 'array-map'; } +export interface KeyValueArray<VALUE> extends Array<VALUE|string> { + __brand__: 'array-map'; +} /** * Set a `value` for a `key`. @@ -253,7 +255,7 @@ export function keyValueArraySet<V>( /** * Retrieve a `value` for a `key` (on `undefined` if not found.) - * + * * @param keyValueArray to search. * @param key The key to locate. * @return The `value` stored at the `key` location or `undefined if not found. diff --git a/packages/core/src/util/char_code.ts b/packages/core/src/util/char_code.ts index 18a023dec43c4..5523acd44f558 100644 --- a/packages/core/src/util/char_code.ts +++ b/packages/core/src/util/char_code.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 + */ /** * List ASCII char codes to be used with `String.charCodeAt` diff --git a/packages/core/src/util/decorators.ts b/packages/core/src/util/decorators.ts index 7ffb8fe56539d..757b6e81d6e90 100644 --- a/packages/core/src/util/decorators.ts +++ b/packages/core/src/util/decorators.ts @@ -52,7 +52,7 @@ export function makeDecorator<T>( const metaCtor = makeMetadataCtor(props); function DecoratorFactory( - this: unknown | typeof DecoratorFactory, ...args: any[]): (cls: Type<T>) => any { + this: unknown|typeof DecoratorFactory, ...args: any[]): (cls: Type<T>) => any { if (this instanceof DecoratorFactory) { metaCtor.call(this, ...args); return this as typeof DecoratorFactory; @@ -101,7 +101,7 @@ export function makeParamDecorator( return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function ParamDecoratorFactory( - this: unknown | typeof ParamDecoratorFactory, ...args: any[]): any { + this: unknown|typeof ParamDecoratorFactory, ...args: any[]): any { if (this instanceof ParamDecoratorFactory) { metaCtor.apply(this, args); return this; @@ -143,8 +143,7 @@ export function makePropDecorator( return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); - function PropDecoratorFactory( - this: unknown | typeof PropDecoratorFactory, ...args: any[]): any { + function PropDecoratorFactory(this: unknown|typeof PropDecoratorFactory, ...args: any[]): any { if (this instanceof PropDecoratorFactory) { metaCtor.apply(this, args); return this; diff --git a/packages/core/src/util/empty.ts b/packages/core/src/util/empty.ts index d30b12e7720a8..0db2000237238 100644 --- a/packages/core/src/util/empty.ts +++ b/packages/core/src/util/empty.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 {initNgDevMode} from './ng_dev_mode'; /** diff --git a/packages/core/src/util/errors.ts b/packages/core/src/util/errors.ts index 7f895c4420573..6e0eb9d1247b2 100644 --- a/packages/core/src/util/errors.ts +++ b/packages/core/src/util/errors.ts @@ -13,8 +13,8 @@ export const ERROR_LOGGER = 'ngErrorLogger'; export function wrappedError(message: string, originalError: any): Error { - const msg = - `${message} caused by: ${originalError instanceof Error ? originalError.message: originalError }`; + const msg = `${message} caused by: ${ + originalError instanceof Error ? originalError.message : originalError}`; const error = Error(msg); (error as any)[ERROR_ORIGINAL_ERROR] = originalError; return error; diff --git a/packages/core/src/util/lang.ts b/packages/core/src/util/lang.ts index d69c39a1465ec..056a61b4cfb9a 100644 --- a/packages/core/src/util/lang.ts +++ b/packages/core/src/util/lang.ts @@ -20,7 +20,7 @@ export function isPromise<T = any>(obj: any): obj is Promise<T> { /** * Determine if the argument is an Observable */ -export function isObservable(obj: any | Observable<any>): obj is Observable<any> { +export function isObservable(obj: any|Observable<any>): obj is Observable<any> { // TODO: use isObservable once we update pass rxjs 6.1 // https://github.com/ReactiveX/rxjs/blob/master/CHANGELOG.md#610-2018-05-03 return !!obj && typeof obj.subscribe === 'function'; diff --git a/packages/core/src/util/microtask.ts b/packages/core/src/util/microtask.ts index fdc8a2b4f6443..4cda89fac18b8 100644 --- a/packages/core/src/util/microtask.ts +++ b/packages/core/src/util/microtask.ts @@ -13,7 +13,9 @@ declare const Zone: any; export function scheduleMicroTask(fn: Function) { if (typeof Zone === 'undefined') { // use promise to schedule microTask instead of use Zone - promise.then(() => { fn && fn.apply(null, null); }); + promise.then(() => { + fn && fn.apply(null, null); + }); } else { Zone.current.scheduleMicroTask('scheduleMicrotask', fn); } diff --git a/packages/core/src/util/stringify.ts b/packages/core/src/util/stringify.ts index 11c5f331f858a..519be71f5e2c3 100644 --- a/packages/core/src/util/stringify.ts +++ b/packages/core/src/util/stringify.ts @@ -45,7 +45,7 @@ export function stringify(token: any): string { * @param after after string. * @returns concatenated string. */ -export function concatStringsWithSpace(before: string | null, after: string | null): string { +export function concatStringsWithSpace(before: string|null, after: string|null): string { return (before == null || before === '') ? (after === null ? '' : after) : ((after == null || after === '') ? before : before + ' ' + after); diff --git a/packages/core/src/view/element.ts b/packages/core/src/view/element.ts index ed4b9f71c03d5..d31d5b43f41d3 100644 --- a/packages/core/src/view/element.ts +++ b/packages/core/src/view/element.ts @@ -10,12 +10,12 @@ import {ViewEncapsulation} from '../metadata/view'; import {RendererType2} from '../render/api'; import {SecurityContext} from '../sanitization/security'; -import {BindingDef, BindingFlags, ElementData, ElementHandleEventFn, NodeDef, NodeFlags, OutputDef, OutputType, QueryValueType, ViewData, ViewDefinitionFactory, asElementData} from './types'; -import {NOOP, calcBindingFlags, checkAndUpdateBinding, dispatchEvent, elementEventFullName, getParentRenderElement, resolveDefinition, resolveRendererType2, splitMatchedQueriesDsl, splitNamespace} from './util'; +import {asElementData, BindingDef, BindingFlags, ElementData, ElementHandleEventFn, NodeDef, NodeFlags, OutputDef, OutputType, QueryValueType, ViewData, ViewDefinitionFactory} from './types'; +import {calcBindingFlags, checkAndUpdateBinding, dispatchEvent, elementEventFullName, getParentRenderElement, NOOP, resolveDefinition, resolveRendererType2, splitMatchedQueriesDsl, splitNamespace} from './util'; export function anchorDef( - flags: NodeFlags, matchedQueriesDsl: null | [string | number, QueryValueType][], - ngContentIndex: null | number, childCount: number, handleEvent?: null | ElementHandleEventFn, + flags: NodeFlags, matchedQueriesDsl: null|[string | number, QueryValueType][], + ngContentIndex: null|number, childCount: number, handleEvent?: null|ElementHandleEventFn, templateFactory?: ViewDefinitionFactory): NodeDef { flags |= NodeFlags.TypeElement; const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl); @@ -33,14 +33,20 @@ export function anchorDef( checkIndex: -1, childFlags: 0, directChildFlags: 0, - childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount, + childMatchedQueries: 0, + matchedQueries, + matchedQueryIds, + references, + ngContentIndex, + childCount, bindings: [], bindingFlags: 0, outputs: [], element: { ns: null, name: null, - attrs: null, template, + attrs: null, + template, componentProvider: null, componentView: null, componentRendererType: null, @@ -57,18 +63,18 @@ export function anchorDef( export function elementDef( checkIndex: number, flags: NodeFlags, - matchedQueriesDsl: null | [string | number, QueryValueType][], ngContentIndex: null | number, - childCount: number, namespaceAndName: string | null, fixedAttrs: null | [string, string][] = [], - bindings?: null | [BindingFlags, string, string | SecurityContext | null][], - outputs?: null | ([string, string])[], handleEvent?: null | ElementHandleEventFn, - componentView?: null | ViewDefinitionFactory, - componentRendererType?: RendererType2 | null): NodeDef { + matchedQueriesDsl: null|[string | number, QueryValueType][], ngContentIndex: null|number, + childCount: number, namespaceAndName: string|null, fixedAttrs: null|[string, string][] = [], + bindings?: null|[BindingFlags, string, string | SecurityContext | null][], + outputs?: null|([string, string])[], handleEvent?: null|ElementHandleEventFn, + componentView?: null|ViewDefinitionFactory, + componentRendererType?: RendererType2|null): NodeDef { if (!handleEvent) { handleEvent = NOOP; } const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl); - let ns: string = null !; - let name: string = null !; + let ns: string = null!; + let name: string = null!; if (namespaceAndName) { [ns, name] = splitNamespace(namespaceAndName); } @@ -78,8 +84,8 @@ export function elementDef( const [bindingFlags, namespaceAndName, suffixOrSecurityContext] = bindings[i]; const [ns, name] = splitNamespace(namespaceAndName); - let securityContext: SecurityContext = undefined !; - let suffix: string = undefined !; + let securityContext: SecurityContext = undefined!; + let suffix: string = undefined!; switch (bindingFlags & BindingFlags.Types) { case BindingFlags.TypeElementStyle: suffix = <string>suffixOrSecurityContext; @@ -96,11 +102,8 @@ export function elementDef( const outputDefs: OutputDef[] = []; for (let i = 0; i < outputs.length; i++) { const [target, eventName] = outputs[i]; - outputDefs[i] = { - type: OutputType.ElementOutput, - target: <any>target, eventName, - propName: null - }; + outputDefs[i] = + {type: OutputType.ElementOutput, target: <any>target, eventName, propName: null}; } fixedAttrs = fixedAttrs || []; const attrs = <[string, string, string][]>fixedAttrs.map(([namespaceAndName, value]) => { @@ -124,7 +127,12 @@ export function elementDef( flags, childFlags: 0, directChildFlags: 0, - childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount, + childMatchedQueries: 0, + matchedQueries, + matchedQueryIds, + references, + ngContentIndex, + childCount, bindings: bindingDefs, bindingFlags: calcBindingFlags(bindingDefs), outputs: outputDefs, @@ -149,7 +157,7 @@ export function elementDef( } export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData { - const elDef = def.element !; + const elDef = def.element!; const rootSelectorOrNode = view.root.selectorOrNode; const renderer = view.renderer; let el: any; @@ -192,7 +200,7 @@ export function listenToElementOutputs(view: ViewData, compView: ViewData, def: } const disposable = <any>listenerView.renderer.listen(listenTarget || el, output.eventName, handleEventClosure); - view.disposables ![def.outputIndex + i] = disposable; + view.disposables![def.outputIndex + i] = disposable; } } @@ -234,7 +242,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu const binding = def.bindings[bindingIdx]; const elData = asElementData(view, def.nodeIndex); const renderNode = elData.renderElement; - const name = binding.name !; + const name = binding.name!; switch (binding.flags & BindingFlags.Types) { case BindingFlags.TypeElementAttribute: setElementAttribute(view, binding, renderNode, binding.ns, name, value); @@ -257,7 +265,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu } function setElementAttribute( - view: ViewData, binding: BindingDef, renderNode: any, ns: string | null, name: string, + view: ViewData, binding: BindingDef, renderNode: any, ns: string|null, name: string, value: any) { const securityContext = binding.securityContext; let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value; @@ -282,7 +290,7 @@ function setElementClass(view: ViewData, renderNode: any, name: string, value: b function setElementStyle( view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) { let renderValue: string|null = - view.root.sanitizer.sanitize(SecurityContext.STYLE, value as{} | string); + view.root.sanitizer.sanitize(SecurityContext.STYLE, value as {} | string); if (renderValue != null) { renderValue = renderValue.toString(); const unit = binding.suffix; diff --git a/packages/core/src/view/entrypoint.ts b/packages/core/src/view/entrypoint.ts index ae1cb7004eb7d..db14d4d7f1e86 100644 --- a/packages/core/src/view/entrypoint.ts +++ b/packages/core/src/view/entrypoint.ts @@ -48,7 +48,10 @@ function cloneNgModuleDefinition(def: NgModuleDefinition): NgModuleDefinition { return { factory: def.factory, - scope: def.scope, providers, modules, providersByKey, + scope: def.scope, + providers, + modules, + providersByKey, }; } diff --git a/packages/core/src/view/errors.ts b/packages/core/src/view/errors.ts index d7ded67a01add..1e78713982eeb 100644 --- a/packages/core/src/view/errors.ts +++ b/packages/core/src/view/errors.ts @@ -14,7 +14,8 @@ import {DebugContext} from './types'; export function expressionChangedAfterItHasBeenCheckedError( context: DebugContext, oldValue: any, currValue: any, isFirstCheck: boolean): Error { let msg = - `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`; + `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '${ + oldValue}'. Current value: '${currValue}'.`; if (isFirstCheck) { msg += ` It seems like the view has been created after its parent and its children have been dirty checked.` + diff --git a/packages/core/src/view/index.ts b/packages/core/src/view/index.ts index 28b0947d26ab6..563f307784083 100644 --- a/packages/core/src/view/index.ts +++ b/packages/core/src/view/index.ts @@ -13,10 +13,10 @@ export {moduleDef, moduleProvideDef} from './ng_module'; export {directiveDef, pipeDef, providerDef} from './provider'; export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression'; export {queryDef} from './query'; -export {ViewRef_, createComponentFactory, getComponentViewDefinitionFactory, nodeValue} from './refs'; +export {createComponentFactory, getComponentViewDefinitionFactory, nodeValue, ViewRef_} from './refs'; export {initServicesIfNeeded} from './services'; export {textDef} from './text'; -export {EMPTY_ARRAY, EMPTY_MAP, createRendererType2, elementEventFullName, inlineInterpolate, interpolate, rootRenderNodes, tokenKey, unwrapValue} from './util'; +export {createRendererType2, elementEventFullName, EMPTY_ARRAY, EMPTY_MAP, inlineInterpolate, interpolate, rootRenderNodes, tokenKey, unwrapValue} from './util'; export {viewDef} from './view'; export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach'; diff --git a/packages/core/src/view/ng_content.ts b/packages/core/src/view/ng_content.ts index fd7ce7e9fcedc..a566c8f25a40a 100644 --- a/packages/core/src/view/ng_content.ts +++ b/packages/core/src/view/ng_content.ts @@ -7,9 +7,9 @@ */ import {NodeDef, NodeFlags, ViewData} from './types'; -import {RenderNodeAction, getParentRenderElement, visitProjectedRenderNodes} from './util'; +import {getParentRenderElement, RenderNodeAction, visitProjectedRenderNodes} from './util'; -export function ngContentDef(ngContentIndex: null | number, index: number): NodeDef { +export function ngContentDef(ngContentIndex: null|number, index: number): NodeDef { return { // will bet set by the view definition nodeIndex: -1, @@ -25,7 +25,8 @@ export function ngContentDef(ngContentIndex: null | number, index: number): Node childMatchedQueries: 0, matchedQueries: {}, matchedQueryIds: 0, - references: {}, ngContentIndex, + references: {}, + ngContentIndex, childCount: 0, bindings: [], bindingFlags: 0, @@ -44,7 +45,7 @@ export function appendNgContent(view: ViewData, renderHost: any, def: NodeDef) { // Nothing to do if there is no parent element. return; } - const ngContentIndex = def.ngContent !.index; + const ngContentIndex = def.ngContent!.index; visitProjectedRenderNodes( view, ngContentIndex, RenderNodeAction.AppendChild, parentEl, null, undefined); } diff --git a/packages/core/src/view/ng_module.ts b/packages/core/src/view/ng_module.ts index 4700396465509..cc44b4b1ea476 100644 --- a/packages/core/src/view/ng_module.ts +++ b/packages/core/src/view/ng_module.ts @@ -25,8 +25,7 @@ const INJECTORRefTokenKey = tokenKey(INJECTOR); const NgModuleRefTokenKey = tokenKey(NgModuleRef); export function moduleProvideDef( - flags: NodeFlags, token: any, value: any, - deps: ([DepFlags, any] | any)[]): NgModuleProviderDef { + flags: NodeFlags, token: any, value: any, deps: ([DepFlags, any]|any)[]): NgModuleProviderDef { // Need to resolve forwardRefs as e.g. for `useValue` we // lowered the expression and then stopped evaluating it, // i.e. also didn't unwrap it. @@ -35,7 +34,10 @@ export function moduleProvideDef( return { // will bet set by the module definition index: -1, - deps: depDefs, flags, token, value + deps: depDefs, + flags, + token, + value }; } @@ -113,7 +115,8 @@ export function resolveNgModuleDep( data._def.providers[index] = data._def.providersByKey[depDef.tokenKey] = { flags: NodeFlags.TypeFactoryProvider | NodeFlags.LazyProvider, value: injectableDef.factory, - deps: [], index, + deps: [], + index, token: depDef.token, }; data._providers[index] = UNDEFINED_VALUE; @@ -135,8 +138,9 @@ function moduleTransitivelyPresent(ngModule: NgModuleData, scope: any): boolean function targetsModule(ngModule: NgModuleData, def: ɵɵInjectableDef<any>): boolean { const providedIn = def.providedIn; - return providedIn != null && (providedIn === 'any' || providedIn === ngModule._def.scope || - moduleTransitivelyPresent(ngModule, providedIn)); + return providedIn != null && + (providedIn === 'any' || providedIn === ngModule._def.scope || + moduleTransitivelyPresent(ngModule, providedIn)); } function _createProviderInstance(ngModule: NgModuleData, providerDef: NgModuleProviderDef): any { diff --git a/packages/core/src/view/provider.ts b/packages/core/src/view/provider.ts index f8421a03bc821..338af6969ef72 100644 --- a/packages/core/src/view/provider.ts +++ b/packages/core/src/view/provider.ts @@ -16,7 +16,7 @@ import {isObservable} from '../util/lang'; import {stringify} from '../util/stringify'; import {createChangeDetectorRef, createInjector} from './refs'; -import {BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, ViewData, ViewFlags, ViewState, asElementData, asProviderData, shouldCallLifecycleInitHook} from './types'; +import {asElementData, asProviderData, BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, shouldCallLifecycleInitHook, ViewData, ViewFlags, ViewState} from './types'; import {calcBindingFlags, checkBinding, dispatchEvent, isComponentView, splitDepsDsl, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util'; const Renderer2TokenKey = tokenKey(Renderer2); @@ -28,17 +28,18 @@ const InjectorRefTokenKey = tokenKey(Injector); const INJECTORRefTokenKey = tokenKey(INJECTOR); export function directiveDef( - checkIndex: number, flags: NodeFlags, - matchedQueries: null | [string | number, QueryValueType][], childCount: number, ctor: any, - deps: ([DepFlags, any] | any)[], props?: null | {[name: string]: [number, string]}, - outputs?: null | {[name: string]: string}): NodeDef { + checkIndex: number, flags: NodeFlags, matchedQueries: null|[string | number, QueryValueType][], + childCount: number, ctor: any, deps: ([DepFlags, any]|any)[], + props?: null|{[name: string]: [number, string]}, + outputs?: null|{[name: string]: string}): NodeDef { const bindings: BindingDef[] = []; if (props) { for (let prop in props) { const [bindingIndex, nonMinifiedName] = props[prop]; bindings[bindingIndex] = { flags: BindingFlags.TypeProperty, - name: prop, nonMinifiedName, + name: prop, + nonMinifiedName, ns: null, securityContext: null, suffix: null @@ -57,22 +58,21 @@ export function directiveDef( checkIndex, flags, matchedQueries, childCount, ctor, ctor, deps, bindings, outputDefs); } -export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef { +export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any]|any)[]): NodeDef { flags |= NodeFlags.TypePipe; return _def(-1, flags, null, 0, ctor, ctor, deps); } export function providerDef( - flags: NodeFlags, matchedQueries: null | [string | number, QueryValueType][], token: any, - value: any, deps: ([DepFlags, any] | any)[]): NodeDef { + flags: NodeFlags, matchedQueries: null|[string | number, QueryValueType][], token: any, + value: any, deps: ([DepFlags, any]|any)[]): NodeDef { return _def(-1, flags, matchedQueries, 0, token, value, deps); } export function _def( - checkIndex: number, flags: NodeFlags, - matchedQueriesDsl: [string | number, QueryValueType][] | null, childCount: number, token: any, - value: any, deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], - outputs?: OutputDef[]): NodeDef { + checkIndex: number, flags: NodeFlags, matchedQueriesDsl: [string|number, QueryValueType][]|null, + childCount: number, token: any, value: any, deps: ([DepFlags, any]|any)[], + bindings?: BindingDef[], outputs?: OutputDef[]): NodeDef { const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl); if (!outputs) { outputs = []; @@ -99,9 +99,15 @@ export function _def( flags, childFlags: 0, directChildFlags: 0, - childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, - ngContentIndex: -1, childCount, bindings, - bindingFlags: calcBindingFlags(bindings), outputs, + childMatchedQueries: 0, + matchedQueries, + matchedQueryIds, + references, + ngContentIndex: -1, + childCount, + bindings, + bindingFlags: calcBindingFlags(bindings), + outputs, element: null, provider: {token, value, deps: depDefs}, text: null, @@ -124,24 +130,24 @@ export function createPipeInstance(view: ViewData, def: NodeDef): any { const allowPrivateServices = true; // pipes are always eager and classes! return createClass( - compView.parent !, viewParentEl(compView) !, allowPrivateServices, def.provider !.value, - def.provider !.deps); + compView.parent!, viewParentEl(compView)!, allowPrivateServices, def.provider!.value, + def.provider!.deps); } export function createDirectiveInstance(view: ViewData, def: NodeDef): any { // components can see other private services, other directives can't. const allowPrivateServices = (def.flags & NodeFlags.Component) > 0; // directives are always eager and classes! - const instance = createClass( - view, def.parent !, allowPrivateServices, def.provider !.value, def.provider !.deps); + const instance = + createClass(view, def.parent!, allowPrivateServices, def.provider!.value, def.provider!.deps); if (def.outputs.length) { for (let i = 0; i < def.outputs.length; i++) { const output = def.outputs[i]; - const outputObservable = instance[output.propName !]; + const outputObservable = instance[output.propName!]; if (isObservable(outputObservable)) { const subscription = outputObservable.subscribe( - eventHandlerClosure(view, def.parent !.nodeIndex, output.eventName)); - view.disposables ![def.outputIndex + i] = subscription.unsubscribe.bind(subscription); + eventHandlerClosure(view, def.parent!.nodeIndex, output.eventName)); + view.disposables![def.outputIndex + i] = subscription.unsubscribe.bind(subscription); } else { throw new Error( `@Output ${output.propName} not initialized in '${instance.constructor.name}'.`); @@ -161,7 +167,7 @@ export function checkAndUpdateDirectiveInline( const providerData = asProviderData(view, def.nodeIndex); const directive = providerData.instance; let changed = false; - let changes: SimpleChanges = undefined !; + let changes: SimpleChanges = undefined!; const bindLen = def.bindings.length; if (bindLen > 0 && checkBinding(view, def, 0, v0)) { changed = true; @@ -221,7 +227,7 @@ export function checkAndUpdateDirectiveDynamic( const providerData = asProviderData(view, def.nodeIndex); const directive = providerData.instance; let changed = false; - let changes: SimpleChanges = undefined !; + let changes: SimpleChanges = undefined!; for (let i = 0; i < values.length; i++) { if (checkBinding(view, def, i, values[i])) { changed = true; @@ -248,14 +254,14 @@ function _createProviderInstance(view: ViewData, def: NodeDef): any { switch (def.flags & NodeFlags.Types) { case NodeFlags.TypeClassProvider: return createClass( - view, def.parent !, allowPrivateServices, providerDef !.value, providerDef !.deps); + view, def.parent!, allowPrivateServices, providerDef!.value, providerDef!.deps); case NodeFlags.TypeFactoryProvider: return callFactory( - view, def.parent !, allowPrivateServices, providerDef !.value, providerDef !.deps); + view, def.parent!, allowPrivateServices, providerDef!.value, providerDef!.deps); case NodeFlags.TypeUseExistingProvider: - return resolveDep(view, def.parent !, allowPrivateServices, providerDef !.deps[0]); + return resolveDep(view, def.parent!, allowPrivateServices, providerDef!.deps[0]); case NodeFlags.TypeValueProvider: - return providerDef !.value; + return providerDef!.value; } } @@ -346,12 +352,12 @@ export function resolveDep( if (tokenKey === ChangeDetectorRefTokenKey) { // directives on the same element as a component should be able to control the change detector // of that component as well. - allowPrivateServices = !!(elDef && elDef.element !.componentView); + allowPrivateServices = !!(elDef && elDef.element!.componentView); } if (elDef && (depDef.flags & DepFlags.SkipSelf)) { allowPrivateServices = false; - elDef = elDef.parent !; + elDef = elDef.parent!; } let searchView: ViewData|null = view; @@ -367,7 +373,7 @@ export function resolveDep( case ViewContainerRefTokenKey: return asElementData(searchView, elDef.nodeIndex).viewContainer; case TemplateRefTokenKey: { - if (elDef.element !.template) { + if (elDef.element!.template) { return asElementData(searchView, elDef.nodeIndex).template; } break; @@ -381,8 +387,8 @@ export function resolveDep( return createInjector(searchView, elDef); default: const providerDef = - (allowPrivateServices ? elDef.element !.allProviders : - elDef.element !.publicProviders) ![tokenKey]; + (allowPrivateServices ? elDef.element!.allProviders : + elDef.element!.publicProviders)![tokenKey]; if (providerDef) { let providerData = asProviderData(searchView, providerDef.nodeIndex); if (!providerData) { @@ -395,8 +401,8 @@ export function resolveDep( } allowPrivateServices = isComponentView(searchView); - elDef = viewParentEl(searchView) !; - searchView = searchView.parent !; + elDef = viewParentEl(searchView)!; + searchView = searchView.parent!; if (depDef.flags & DepFlags.Self) { searchView = null; @@ -435,13 +441,13 @@ function updateProp( view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any, changes: SimpleChanges): SimpleChanges { if (def.flags & NodeFlags.Component) { - const compView = asElementData(view, def.parent !.nodeIndex).componentView; + const compView = asElementData(view, def.parent!.nodeIndex).componentView; if (compView.def.flags & ViewFlags.OnPush) { compView.state |= ViewState.ChecksEnabled; } } const binding = def.bindings[bindingIdx]; - const propName = binding.name !; + const propName = binding.name!; // Note: This is still safe with Closure Compiler as // the user passed in the property name as an object has to `providerDef`, // so Closure Compiler will have renamed the property correctly already. @@ -450,7 +456,7 @@ function updateProp( changes = changes || {}; const oldValue = WrappedValue.unwrap(view.oldValues[def.bindingIndex + bindingIdx]); const binding = def.bindings[bindingIdx]; - changes[binding.nonMinifiedName !] = + changes[binding.nonMinifiedName!] = new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0); } view.oldValues[def.bindingIndex + bindingIdx] = value; diff --git a/packages/core/src/view/pure_expression.ts b/packages/core/src/view/pure_expression.ts index 1cbcae3558d16..c8b300913a639 100644 --- a/packages/core/src/view/pure_expression.ts +++ b/packages/core/src/view/pure_expression.ts @@ -8,7 +8,7 @@ import {newArray} from '../util/array_utils'; -import {BindingDef, BindingFlags, NodeDef, NodeFlags, PureExpressionData, ViewData, asPureExpressionData} from './types'; +import {asPureExpressionData, BindingDef, BindingFlags, NodeDef, NodeFlags, PureExpressionData, ViewData} from './types'; import {calcBindingFlags, checkAndUpdateBinding} from './util'; export function purePipeDef(checkIndex: number, argCount: number): NodeDef { @@ -64,7 +64,8 @@ function _pureExpressionDef( matchedQueryIds: 0, references: {}, ngContentIndex: -1, - childCount: 0, bindings, + childCount: 0, + bindings, bindingFlags: calcBindingFlags(bindings), outputs: [], element: null, @@ -115,16 +116,16 @@ export function checkAndUpdatePureExpressionInline( break; case NodeFlags.TypePureObject: value = {}; - if (bindLen > 0) value[bindings[0].name !] = v0; - if (bindLen > 1) value[bindings[1].name !] = v1; - if (bindLen > 2) value[bindings[2].name !] = v2; - if (bindLen > 3) value[bindings[3].name !] = v3; - if (bindLen > 4) value[bindings[4].name !] = v4; - if (bindLen > 5) value[bindings[5].name !] = v5; - if (bindLen > 6) value[bindings[6].name !] = v6; - if (bindLen > 7) value[bindings[7].name !] = v7; - if (bindLen > 8) value[bindings[8].name !] = v8; - if (bindLen > 9) value[bindings[9].name !] = v9; + if (bindLen > 0) value[bindings[0].name!] = v0; + if (bindLen > 1) value[bindings[1].name!] = v1; + if (bindLen > 2) value[bindings[2].name!] = v2; + if (bindLen > 3) value[bindings[3].name!] = v3; + if (bindLen > 4) value[bindings[4].name!] = v4; + if (bindLen > 5) value[bindings[5].name!] = v5; + if (bindLen > 6) value[bindings[6].name!] = v6; + if (bindLen > 7) value[bindings[7].name!] = v7; + if (bindLen > 8) value[bindings[8].name!] = v8; + if (bindLen > 9) value[bindings[9].name!] = v9; break; case NodeFlags.TypePurePipe: const pipe = v0; @@ -188,7 +189,7 @@ export function checkAndUpdatePureExpressionDynamic( case NodeFlags.TypePureObject: value = {}; for (let i = 0; i < values.length; i++) { - value[bindings[i].name !] = values[i]; + value[bindings[i].name!] = values[i]; } break; case NodeFlags.TypePurePipe: diff --git a/packages/core/src/view/query.ts b/packages/core/src/view/query.ts index 9e734b4be33d8..d462885d56994 100644 --- a/packages/core/src/view/query.ts +++ b/packages/core/src/view/query.ts @@ -9,7 +9,7 @@ import {ElementRef} from '../linker/element_ref'; import {QueryList} from '../linker/query_list'; -import {NodeDef, NodeFlags, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, ViewData, asElementData, asProviderData, asQueryList} from './types'; +import {asElementData, asProviderData, asQueryList, NodeDef, NodeFlags, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, ViewData} from './types'; import {declaredViewContainer, filterQueryId, isEmbeddedView} from './util'; export function queryDef( @@ -29,7 +29,8 @@ export function queryDef( outputIndex: -1, // regular values // TODO(vicb): check - checkIndex: -1, flags, + checkIndex: -1, + flags, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, @@ -56,7 +57,7 @@ export function createQuery(): QueryList<any> { export function dirtyParentQueries(view: ViewData) { const queryIds = view.def.nodeMatchedQueries; while (view.parent && isEmbeddedView(view)) { - let tplDef = view.parentNodeDef !; + let tplDef = view.parentNodeDef!; view = view.parent; // content queries const end = tplDef.nodeIndex + tplDef.childCount; @@ -64,7 +65,7 @@ export function dirtyParentQueries(view: ViewData) { const nodeDef = view.def.nodes[i]; if ((nodeDef.flags & NodeFlags.TypeContentQuery) && (nodeDef.flags & NodeFlags.DynamicQuery) && - (nodeDef.query !.filterId & queryIds) === nodeDef.query !.filterId) { + (nodeDef.query!.filterId & queryIds) === nodeDef.query!.filterId) { asQueryList(view, i).setDirty(); } if ((nodeDef.flags & NodeFlags.TypeElement && i + nodeDef.childCount < tplDef.nodeIndex) || @@ -95,19 +96,19 @@ export function checkAndUpdateQuery(view: ViewData, nodeDef: NodeDef) { return; } let directiveInstance: any; - let newValues: any[] = undefined !; + let newValues: any[] = undefined!; if (nodeDef.flags & NodeFlags.TypeContentQuery) { - const elementDef = nodeDef.parent !.parent !; + const elementDef = nodeDef.parent!.parent!; newValues = calcQueryValues( - view, elementDef.nodeIndex, elementDef.nodeIndex + elementDef.childCount, nodeDef.query !, + view, elementDef.nodeIndex, elementDef.nodeIndex + elementDef.childCount, nodeDef.query!, []); - directiveInstance = asProviderData(view, nodeDef.parent !.nodeIndex).instance; + directiveInstance = asProviderData(view, nodeDef.parent!.nodeIndex).instance; } else if (nodeDef.flags & NodeFlags.TypeViewQuery) { - newValues = calcQueryValues(view, 0, view.def.nodes.length - 1, nodeDef.query !, []); + newValues = calcQueryValues(view, 0, view.def.nodes.length - 1, nodeDef.query!, []); directiveInstance = view.component; } queryList.reset(newValues); - const bindings = nodeDef.query !.bindings; + const bindings = nodeDef.query!.bindings; let notify = false; for (let i = 0; i < bindings.length; i++) { const binding = bindings[i]; @@ -137,8 +138,8 @@ function calcQueryValues( if (valueType != null) { values.push(getQueryValue(view, nodeDef, valueType)); } - if (nodeDef.flags & NodeFlags.TypeElement && nodeDef.element !.template && - (nodeDef.element !.template !.nodeMatchedQueries & queryDef.filterId) === + if (nodeDef.flags & NodeFlags.TypeElement && nodeDef.element!.template && + (nodeDef.element!.template !.nodeMatchedQueries & queryDef.filterId) === queryDef.filterId) { const elementData = asElementData(view, i); // check embedded views that were attached at the place of their template, @@ -148,7 +149,7 @@ function calcQueryValues( i += nodeDef.childCount; } if (nodeDef.flags & NodeFlags.EmbeddedViews) { - const embeddedViews = elementData.viewContainer !._embeddedViews; + const embeddedViews = elementData.viewContainer!._embeddedViews; for (let k = 0; k < embeddedViews.length; k++) { const embeddedView = embeddedViews[k]; const dvc = declaredViewContainer(embeddedView); diff --git a/packages/core/src/view/refs.ts b/packages/core/src/view/refs.ts index 4fe9a5d731fff..36fe5cfa8616e 100644 --- a/packages/core/src/view/refs.ts +++ b/packages/core/src/view/refs.ts @@ -22,7 +22,7 @@ import {stringify} from '../util/stringify'; import {VERSION} from '../version'; import {callNgModuleLifecycle, initNgModule, resolveNgModuleDep} from './ng_module'; -import {DepFlags, ElementData, NgModuleData, NgModuleDefinition, NodeDef, NodeFlags, Services, TemplateData, ViewContainerData, ViewData, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types'; +import {asElementData, asProviderData, asTextData, DepFlags, ElementData, NgModuleData, NgModuleDefinition, NodeDef, NodeFlags, Services, TemplateData, ViewContainerData, ViewData, ViewDefinitionFactory, ViewState} from './types'; import {markParentViewsForCheck, resolveDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util'; import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachView} from './view_attach'; @@ -32,7 +32,7 @@ const EMPTY_CONTEXT = {}; // Putting any logic in here will destroy closure tree shaking! export function createComponentFactory( selector: string, componentType: Type<any>, viewDefFactory: ViewDefinitionFactory, - inputs: {[propName: string]: string} | null, outputs: {[propName: string]: string}, + inputs: {[propName: string]: string}|null, outputs: {[propName: string]: string}, ngContentSelectors: string[]): ComponentFactory<any> { return new ComponentFactory_( selector, componentType, viewDefFactory, inputs, outputs, ngContentSelectors); @@ -61,7 +61,7 @@ class ComponentFactory_ extends ComponentFactory<any> { get inputs() { const inputsArr: {propName: string, templateName: string}[] = []; - const inputs = this._inputs !; + const inputs = this._inputs!; for (let propName in inputs) { const templateName = inputs[propName]; inputsArr.push({propName, templateName}); @@ -88,7 +88,7 @@ class ComponentFactory_ extends ComponentFactory<any> { throw new Error('ngModule should be provided'); } const viewDef = resolveDefinition(this.viewDefFactory); - const componentNodeIndex = viewDef.nodes[0].element !.componentProvider !.nodeIndex; + const componentNodeIndex = viewDef.nodes[0].element!.componentProvider!.nodeIndex; const view = Services.createRootView( injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT); const component = asProviderData(view, componentNodeIndex).instance; @@ -115,11 +115,19 @@ class ComponentRef_ extends ComponentRef<any> { get location(): ElementRef { return new ElementRef(asElementData(this._view, this._elDef.nodeIndex).renderElement); } - get injector(): Injector { return new Injector_(this._view, this._elDef); } - get componentType(): Type<any> { return <any>this._component.constructor; } + get injector(): Injector { + return new Injector_(this._view, this._elDef); + } + get componentType(): Type<any> { + return <any>this._component.constructor; + } - destroy(): void { this._viewRef.destroy(); } - onDestroy(callback: Function): void { this._viewRef.onDestroy(callback); } + destroy(): void { + this._viewRef.destroy(); + } + onDestroy(callback: Function): void { + this._viewRef.onDestroy(callback); + } } export function createViewContainerData( @@ -134,9 +142,13 @@ class ViewContainerRef_ implements ViewContainerData { _embeddedViews: ViewData[] = []; constructor(private _view: ViewData, private _elDef: NodeDef, private _data: ElementData) {} - get element(): ElementRef { return new ElementRef(this._data.renderElement); } + get element(): ElementRef { + return new ElementRef(this._data.renderElement); + } - get injector(): Injector { return new Injector_(this._view, this._elDef); } + get injector(): Injector { + return new Injector_(this._view, this._elDef); + } /** @deprecated No replacement */ get parentInjector(): Injector { @@ -144,7 +156,7 @@ class ViewContainerRef_ implements ViewContainerData { let elDef = this._elDef.parent; while (!elDef && view) { elDef = viewParentEl(view); - view = view.parent !; + view = view.parent!; } return view ? new Injector_(view, elDef) : new Injector_(this._view, null); @@ -153,7 +165,7 @@ class ViewContainerRef_ implements ViewContainerData { clear(): void { const len = this._embeddedViews.length; for (let i = len - 1; i >= 0; i--) { - const view = detachEmbeddedView(this._data, i) !; + const view = detachEmbeddedView(this._data, i)!; Services.destroyView(view); } } @@ -168,7 +180,9 @@ class ViewContainerRef_ implements ViewContainerData { return null; } - get length(): number { return this._embeddedViews.length; } + get length(): number { + return this._embeddedViews.length; + } createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C> { @@ -243,14 +257,24 @@ export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef { this._appRef = null; } - get rootNodes(): any[] { return rootRenderNodes(this._view); } + get rootNodes(): any[] { + return rootRenderNodes(this._view); + } - get context() { return this._view.context; } + get context() { + return this._view.context; + } - get destroyed(): boolean { return (this._view.state & ViewState.Destroyed) !== 0; } + get destroyed(): boolean { + return (this._view.state & ViewState.Destroyed) !== 0; + } - markForCheck(): void { markParentViewsForCheck(this._view); } - detach(): void { this._view.state &= ~ViewState.Attached; } + markForCheck(): void { + markParentViewsForCheck(this._view); + } + detach(): void { + this._view.state &= ~ViewState.Attached; + } detectChanges(): void { const fs = this._view.root.rendererFactory; if (fs.begin) { @@ -264,9 +288,13 @@ export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef { } } } - checkNoChanges(): void { Services.checkNoChangesView(this._view); } + checkNoChanges(): void { + Services.checkNoChangesView(this._view); + } - reattach(): void { this._view.state |= ViewState.Attached; } + reattach(): void { + this._view.state |= ViewState.Attached; + } onDestroy(callback: Function) { if (!this._view.disposables) { this._view.disposables = []; @@ -313,13 +341,15 @@ class TemplateRef_ extends TemplateRef<any> implements TemplateData { * @internal */ // TODO(issue/24571): remove '!'. - _projectedViews !: ViewData[]; + _projectedViews!: ViewData[]; - constructor(private _parentView: ViewData, private _def: NodeDef) { super(); } + constructor(private _parentView: ViewData, private _def: NodeDef) { + super(); + } createEmbeddedView(context: any): EmbeddedViewRef<any> { return new ViewRef_(Services.createEmbeddedView( - this._parentView, this._def, this._def.element !.template !, context)); + this._parentView, this._def, this._def.element!.template !, context)); } get elementRef(): ElementRef { @@ -346,7 +376,7 @@ export function nodeValue(view: ViewData, index: number): any { const def = view.def.nodes[index]; if (def.flags & NodeFlags.TypeElement) { const elData = asElementData(view, def.nodeIndex); - return def.element !.template ? elData.template : elData.renderElement; + return def.element!.template ? elData.template : elData.renderElement; } else if (def.flags & NodeFlags.TypeText) { return asTextData(view, def.nodeIndex).renderText; } else if (def.flags & (NodeFlags.CatProvider | NodeFlags.TypePipe)) { @@ -366,10 +396,10 @@ class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> { private _destroyed: boolean = false; /** @internal */ // TODO(issue/24571): remove '!'. - _providers !: any[]; + _providers!: any[]; /** @internal */ // TODO(issue/24571): remove '!'. - _modules !: any[]; + _modules!: any[]; readonly injector: Injector = this; @@ -391,9 +421,13 @@ class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> { this, {token: token, tokenKey: tokenKey(token), flags: flags}, notFoundValue); } - get instance() { return this.get(this._moduleType); } + get instance() { + return this.get(this._moduleType); + } - get componentFactoryResolver() { return this.get(ComponentFactoryResolver); } + get componentFactoryResolver() { + return this.get(ComponentFactoryResolver); + } destroy(): void { if (this._destroyed) { @@ -405,5 +439,7 @@ class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> { this._destroyListeners.forEach((listener) => listener()); } - onDestroy(callback: () => void): void { this._destroyListeners.push(callback); } + onDestroy(callback: () => void): void { + this._destroyListeners.push(callback); + } } diff --git a/packages/core/src/view/services.ts b/packages/core/src/view/services.ts index 82caa09c75a4c..97b9f2bf9208d 100644 --- a/packages/core/src/view/services.ts +++ b/packages/core/src/view/services.ts @@ -8,7 +8,7 @@ import {DebugElement__PRE_R3__, DebugEventListener, DebugNode__PRE_R3__, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node'; import {Injector} from '../di'; -import {InjectableType, getInjectableDef, ɵɵInjectableDef} from '../di/interface/defs'; +import {getInjectableDef, InjectableType, ɵɵInjectableDef} from '../di/interface/defs'; import {ErrorHandler} from '../error_handler'; import {Type} from '../interface/type'; import {ComponentFactory} from '../linker/component_factory'; @@ -22,8 +22,8 @@ import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './err import {resolveDep} from './provider'; import {dirtyParentQueries, getQueryValue} from './query'; import {createInjector, createNgModuleRef, getComponentViewDefinitionFactory} from './refs'; -import {ArgumentType, BindingFlags, CheckType, DebugContext, ElementData, NgModuleDefinition, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types'; -import {NOOP, isComponentView, renderNode, resolveDefinition, splitDepsDsl, tokenKey, viewParentEl} from './util'; +import {ArgumentType, asElementData, asPureExpressionData, BindingFlags, CheckType, DebugContext, ElementData, NgModuleDefinition, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState} from './types'; +import {isComponentView, NOOP, renderNode, resolveDefinition, splitDepsDsl, tokenKey, viewParentEl} from './util'; import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view'; @@ -69,15 +69,13 @@ function createProdServices() { destroyView: destroyView, createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex), handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) => - view.def.handleEvent(view, nodeIndex, eventName, event), + view.def.handleEvent(view, nodeIndex, eventName, event), updateDirectives: (view: ViewData, checkType: CheckType) => view.def.updateDirectives( - checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode : - prodCheckNoChangesNode, - view), + checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode : prodCheckNoChangesNode, + view), updateRenderer: (view: ViewData, checkType: CheckType) => view.def.updateRenderer( - checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode : - prodCheckNoChangesNode, - view), + checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode : prodCheckNoChangesNode, + view), }; } @@ -102,7 +100,7 @@ function createDebugServices() { } function createProdRootView( - elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any, + elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string|any, def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData { const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2); return createRootView( @@ -111,7 +109,7 @@ function createProdRootView( } function debugCreateRootView( - elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any, + elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string|any, def: ViewDefinition, ngModule: NgModuleRef<any>, context?: any): ViewData { const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2); const root = createRootData( @@ -130,8 +128,13 @@ function createRootData( const renderer = rendererFactory.createRenderer(null, null); return { ngModule, - injector: elInjector, projectableNodes, - selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer, errorHandler + injector: elInjector, + projectableNodes, + selectorOrNode: rootSelectorOrNode, + sanitizer, + rendererFactory, + renderer, + errorHandler }; } @@ -146,7 +149,7 @@ function debugCreateEmbeddedView( function debugCreateComponentView( parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData { const overrideComponentView = - viewDefOverrides.get(nodeDef.element !.componentProvider !.provider !.token); + viewDefOverrides.get(nodeDef.element!.componentProvider!.provider!.token); if (overrideComponentView) { viewDef = overrideComponentView; } else { @@ -178,7 +181,7 @@ function debugOverrideProvider(override: ProviderOverride) { function debugOverrideComponentView(comp: any, compFactory: ComponentFactory<any>) { const hostViewDef = resolveDefinition(getComponentViewDefinitionFactory(compFactory)); - const compViewDef = resolveDefinition(hostViewDef.nodes[0].element !.componentView !); + const compViewDef = resolveDefinition(hostViewDef.nodes[0].element!.componentView!); viewDefOverrides.set(comp, compViewDef); } @@ -204,7 +207,7 @@ function applyProviderOverridesToView(def: ViewDefinition): ViewDefinition { } // clone the whole view definition, // as it maintains references between the nodes that are hard to update. - def = def.factory !(() => NOOP); + def = def.factory!(() => NOOP); for (let i = 0; i < elementIndicesWithOverwrittenProviders.length; i++) { applyProviderOverridesToElement(def, elementIndicesWithOverwrittenProviders[i]); } @@ -219,8 +222,8 @@ function applyProviderOverridesToView(def: ViewDefinition): ViewDefinition { lastElementDef = nodeDef; } if (lastElementDef && nodeDef.flags & NodeFlags.CatProviderNoDirective && - providerOverrides.has(nodeDef.provider !.token)) { - elIndicesWithOverwrittenProviders.push(lastElementDef !.nodeIndex); + providerOverrides.has(nodeDef.provider!.token)) { + elIndicesWithOverwrittenProviders.push(lastElementDef!.nodeIndex); lastElementDef = null; } } @@ -235,7 +238,7 @@ function applyProviderOverridesToView(def: ViewDefinition): ViewDefinition { return; } if (nodeDef.flags & NodeFlags.CatProviderNoDirective) { - const provider = nodeDef.provider !; + const provider = nodeDef.provider!; const override = providerOverrides.get(provider.token); if (override) { nodeDef.flags = (nodeDef.flags & ~NodeFlags.CatProviderNoDirective) | override.flags; @@ -257,7 +260,7 @@ function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefi } // clone the whole view definition, // as it maintains references between the nodes that are hard to update. - def = def.factory !(() => NOOP); + def = def.factory!(() => NOOP); applyProviderOverrides(def); return def; @@ -277,7 +280,7 @@ function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefi }); def.modules.forEach(module => { providerOverridesWithScope.forEach((override, token) => { - if (getInjectableDef(token) !.providedIn === module) { + if (getInjectableDef(token)!.providedIn === module) { hasOverrides = true; hasDeprecatedOverrides = hasDeprecatedOverrides || override.deprecatedBehavior; } @@ -305,7 +308,7 @@ function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefi if (providerOverridesWithScope.size > 0) { let moduleSet = new Set<any>(def.modules); providerOverridesWithScope.forEach((override, token) => { - if (moduleSet.has(getInjectableDef(token) !.providedIn)) { + if (moduleSet.has(getInjectableDef(token)!.providedIn)) { let provider = { token: token, flags: @@ -366,7 +369,7 @@ let _currentAction: DebugAction; let _currentView: ViewData; let _currentNodeIndex: number|null; -function debugSetCurrentNode(view: ViewData, nodeIndex: number | null) { +function debugSetCurrentNode(view: ViewData, nodeIndex: number|null) { _currentView = view; _currentNodeIndex = nodeIndex; } @@ -436,13 +439,13 @@ function debugCheckAndUpdateNode( const binding = nodeDef.bindings[i]; const value = values[i]; if (binding.flags & BindingFlags.TypeProperty) { - bindingValues[normalizeDebugBindingName(binding.nonMinifiedName !)] = + bindingValues[normalizeDebugBindingName(binding.nonMinifiedName!)] = normalizeDebugBindingValue(value); } } - const elDef = nodeDef.parent !; + const elDef = nodeDef.parent!; const el = asElementData(view, elDef.nodeIndex).renderElement; - if (!elDef.element !.name) { + if (!elDef.element!.name) { // a comment. view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`); } else { @@ -498,12 +501,12 @@ class DebugContext_ implements DebugContext { let elDef = this.nodeDef; let elView = view; while (elDef && (elDef.flags & NodeFlags.TypeElement) === 0) { - elDef = elDef.parent !; + elDef = elDef.parent!; } if (!elDef) { while (!elDef && elView) { - elDef = viewParentEl(elView) !; - elView = elView.parent !; + elDef = viewParentEl(elView)!; + elView = elView.parent!; } } this.elDef = elDef; @@ -515,11 +518,17 @@ class DebugContext_ implements DebugContext { return asElementData(this.elView, this.elDef.nodeIndex).componentView || this.view; } - get injector(): Injector { return createInjector(this.elView, this.elDef); } + get injector(): Injector { + return createInjector(this.elView, this.elDef); + } - get component(): any { return this.elOrCompView.component; } + get component(): any { + return this.elOrCompView.component; + } - get context(): any { return this.elOrCompView.context; } + get context(): any { + return this.elOrCompView.context; + } get providerTokens(): any[] { const tokens: any[] = []; @@ -528,7 +537,7 @@ class DebugContext_ implements DebugContext { i++) { const childDef = this.elView.def.nodes[i]; if (childDef.flags & NodeFlags.CatProvider) { - tokens.push(childDef.provider !.token); + tokens.push(childDef.provider!.token); } i += childDef.childCount; } @@ -585,7 +594,7 @@ class DebugContext_ implements DebugContext { return NOOP; } }; - logViewDef.factory !(nodeLogger); + logViewDef.factory!(nodeLogger); if (currRenderNodeIndex < renderNodeIndex) { console.error('Illegal state: the ViewDefinitionFactory did not call the logger!'); (<any>console.error)(...values); @@ -606,10 +615,10 @@ function getRenderNodeIndex(viewDef: ViewDefinition, nodeIndex: number): number function findHostElement(view: ViewData): ElementData|null { while (view && !isComponentView(view)) { - view = view.parent !; + view = view.parent!; } if (view.parent) { - return asElementData(view.parent, viewParentEl(view) !.nodeIndex); + return asElementData(view.parent, viewParentEl(view)!.nodeIndex); } return null; } @@ -635,7 +644,7 @@ function callWithDebugContext(action: DebugAction, fn: any, self: any, args: any if (isViewDebugError(e) || !_currentView) { throw e; } - throw viewWrappedDebugError(e, getCurrentDebugContext() !); + throw viewWrappedDebugError(e, getCurrentDebugContext()!); } } @@ -672,7 +681,9 @@ export class DebugRendererFactory2 implements RendererFactory2 { export class DebugRenderer2 implements Renderer2 { readonly data: {[key: string]: any}; - private createDebugContext(nativeElement: any) { return this.debugContextFactory(nativeElement); } + private createDebugContext(nativeElement: any) { + return this.debugContextFactory(nativeElement); + } /** * Factory function used to create a `DebugContext` when a node is created. @@ -684,10 +695,12 @@ export class DebugRenderer2 implements Renderer2 { */ debugContextFactory: (nativeElement?: any) => DebugContext | null = getCurrentDebugContext; - constructor(private delegate: Renderer2) { this.data = this.delegate.data; } + constructor(private delegate: Renderer2) { + this.data = this.delegate.data; + } destroyNode(node: any) { - const debugNode = getDebugNode(node) !; + const debugNode = getDebugNode(node)!; removeDebugNodeFromIndex(debugNode); if (debugNode instanceof DebugNode__PRE_R3__) { debugNode.listeners.length = 0; @@ -697,14 +710,16 @@ export class DebugRenderer2 implements Renderer2 { } } - destroy() { this.delegate.destroy(); } + destroy() { + this.delegate.destroy(); + } createElement(name: string, namespace?: string): any { const el = this.delegate.createElement(name, namespace); const debugCtx = this.createDebugContext(el); if (debugCtx) { const debugEl = new DebugElement__PRE_R3__(el, null, debugCtx); - (debugEl as{name: string}).name = name; + (debugEl as {name: string}).name = name; indexDebugNode(debugEl); } return el; @@ -740,7 +755,7 @@ export class DebugRenderer2 implements Renderer2 { insertBefore(parent: any, newChild: any, refChild: any): void { const debugEl = getDebugNode(parent); const debugChildEl = getDebugNode(newChild); - const debugRefEl = getDebugNode(refChild) !; + const debugRefEl = getDebugNode(refChild)!; if (debugEl && debugChildEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.insertBefore(debugRefEl, debugChildEl); } @@ -837,7 +852,13 @@ export class DebugRenderer2 implements Renderer2 { return this.delegate.listen(target, eventName, callback); } - parentNode(node: any): any { return this.delegate.parentNode(node); } - nextSibling(node: any): any { return this.delegate.nextSibling(node); } - setValue(node: any, value: string): void { return this.delegate.setValue(node, value); } + parentNode(node: any): any { + return this.delegate.parentNode(node); + } + nextSibling(node: any): any { + return this.delegate.nextSibling(node); + } + setValue(node: any, value: string): void { + return this.delegate.setValue(node, value); + } } diff --git a/packages/core/src/view/text.ts b/packages/core/src/view/text.ts index 7067d7fe43de2..51f31e25c7e83 100644 --- a/packages/core/src/view/text.ts +++ b/packages/core/src/view/text.ts @@ -6,11 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {BindingDef, BindingFlags, NodeDef, NodeFlags, TextData, ViewData, asTextData} from './types'; +import {asTextData, BindingDef, BindingFlags, NodeDef, NodeFlags, TextData, ViewData} from './types'; import {checkAndUpdateBinding, getParentRenderElement} from './util'; export function textDef( - checkIndex: number, ngContentIndex: number | null, staticText: string[]): NodeDef { + checkIndex: number, ngContentIndex: number|null, staticText: string[]): NodeDef { const bindings: BindingDef[] = []; for (let i = 1; i < staticText.length; i++) { bindings[i - 1] = { @@ -38,8 +38,10 @@ export function textDef( childMatchedQueries: 0, matchedQueries: {}, matchedQueryIds: 0, - references: {}, ngContentIndex, - childCount: 0, bindings, + references: {}, + ngContentIndex, + childCount: 0, + bindings, bindingFlags: BindingFlags.TypeProperty, outputs: [], element: null, @@ -53,7 +55,7 @@ export function textDef( export function createText(view: ViewData, renderHost: any, def: NodeDef): TextData { let renderNode: any; const renderer = view.renderer; - renderNode = renderer.createText(def.text !.prefix); + renderNode = renderer.createText(def.text!.prefix); const parentEl = getParentRenderElement(view, renderHost, def); if (parentEl) { renderer.appendChild(parentEl, renderNode); @@ -79,7 +81,7 @@ export function checkAndUpdateTextInline( if (bindLen > 9 && checkAndUpdateBinding(view, def, 9, v9)) changed = true; if (changed) { - let value = def.text !.prefix; + let value = def.text!.prefix; if (bindLen > 0) value += _addInterpolationPart(v0, bindings[0]); if (bindLen > 1) value += _addInterpolationPart(v1, bindings[1]); if (bindLen > 2) value += _addInterpolationPart(v2, bindings[2]); @@ -111,7 +113,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values: for (let i = 0; i < values.length; i++) { value = value + _addInterpolationPart(values[i], bindings[i]); } - value = def.text !.prefix + value; + value = def.text!.prefix + value; const renderNode = asTextData(view, def.nodeIndex).renderText; view.renderer.setValue(renderNode, value); } diff --git a/packages/core/src/view/types.ts b/packages/core/src/view/types.ts index 3172afa33bc79..da23eb3675d6a 100644 --- a/packages/core/src/view/types.ts +++ b/packages/core/src/view/types.ts @@ -30,16 +30,22 @@ import {SecurityContext} from '../sanitization/security'; * function to log the error from the definition of the node, which is shown in all browser * logs. */ -export interface DefinitionFactory<D extends Definition<any>> { (logger: NodeLogger): D; } +export interface DefinitionFactory<D extends Definition<any>> { + (logger: NodeLogger): D; +} /** * Function to call console.error at the right source location. This is an indirection * via another function as browser will log the location that actually called * `console.error`. */ -export interface NodeLogger { (): () => void; } +export interface NodeLogger { + (): () => void; +} -export interface Definition<DF extends DefinitionFactory<any>> { factory: DF|null; } +export interface Definition<DF extends DefinitionFactory<any>> { + factory: DF|null; +} export interface NgModuleDefinition extends Definition<NgModuleDefinitionFactory> { providers: NgModuleProviderDef[]; @@ -77,7 +83,9 @@ export interface ViewDefinition extends Definition<ViewDefinitionFactory> { export interface ViewDefinitionFactory extends DefinitionFactory<ViewDefinition> {} -export interface ViewUpdateFn { (check: NodeCheckFn, view: ViewData): void; } +export interface ViewUpdateFn { + (check: NodeCheckFn, view: ViewData): void; +} // helper functions to create an overloaded function type. export interface NodeCheckFn { @@ -87,7 +95,10 @@ export interface NodeCheckFn { v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any; } -export const enum ArgumentType {Inline = 0, Dynamic = 1} +export const enum ArgumentType { + Inline = 0, + Dynamic = 1 +} export interface ViewHandleEventFn { (view: ViewData, nodeIndex: number, eventName: string, event: any): boolean; @@ -232,7 +243,10 @@ export interface OutputDef { propName: string|null; } -export const enum OutputType {ElementOutput, DirectiveOutput} +export const enum OutputType { + ElementOutput, + DirectiveOutput +} export const enum QueryValueType { ElementRef = 0, @@ -266,7 +280,9 @@ export interface ElementDef { handleEvent: ElementHandleEventFn|null; } -export interface ElementHandleEventFn { (view: ViewData, eventName: string, event: any): boolean; } +export interface ElementHandleEventFn { + (view: ViewData, eventName: string, event: any): boolean; +} export interface ProviderDef { token: any; @@ -299,7 +315,9 @@ export const enum DepFlags { Value = 1 << 3, } -export interface TextDef { prefix: string; } +export interface TextDef { + prefix: string; +} export interface QueryDef { id: number; @@ -313,7 +331,10 @@ export interface QueryBindingDef { bindingType: QueryBindingType; } -export const enum QueryBindingType {First = 0, All = 1} +export const enum QueryBindingType { + First = 0, + All = 1 +} export interface NgContentDef { /** @@ -424,7 +445,9 @@ export function shouldCallLifecycleInitHook( return false; } -export interface DisposableFn { (): void; } +export interface DisposableFn { + (): void; +} /** * Node instance data. @@ -437,14 +460,18 @@ export interface DisposableFn { (): void; } * This way, no usage site can get a `NodeData` from view.nodes and then use it for different * purposes. */ -export class NodeData { private __brand: any; } +export class NodeData { + private __brand: any; +} /** * Data for an instantiated NodeType.Text. * * Attention: Adding fields to this is performance sensitive! */ -export interface TextData { renderText: any; } +export interface TextData { + renderText: any; +} /** * Accessor for view.nodes, enforcing that every usage site stays monomorphic. @@ -494,7 +521,9 @@ export function asElementData(view: ViewData, index: number): ElementData { * * Attention: Adding fields to this is performance sensitive! */ -export interface ProviderData { instance: any; } +export interface ProviderData { + instance: any; +} /** * Accessor for view.nodes, enforcing that every usage site stays monomorphic. @@ -508,7 +537,9 @@ export function asProviderData(view: ViewData, index: number): ProviderData { * * Attention: Adding fields to this is performance sensitive! */ -export interface PureExpressionData { value: any; } +export interface PureExpressionData { + value: any; +} /** * Accessor for view.nodes, enforcing that every usage site stays monomorphic. @@ -552,7 +583,10 @@ export abstract class DebugContext { // Other // ------------------------------------- -export const enum CheckType {CheckAndUpdate, CheckNoChanges} +export const enum CheckType { + CheckAndUpdate, + CheckNoChanges +} export interface ProviderOverride { token: any; @@ -595,21 +629,21 @@ export interface Services { * debug mode can hook it. It is lazily filled when `isDevMode` is known. */ export const Services: Services = { - setCurrentNode: undefined !, - createRootView: undefined !, - createEmbeddedView: undefined !, - createComponentView: undefined !, - createNgModuleRef: undefined !, - overrideProvider: undefined !, - overrideComponentView: undefined !, - clearOverrides: undefined !, - checkAndUpdateView: undefined !, - checkNoChangesView: undefined !, - destroyView: undefined !, - resolveDep: undefined !, - createDebugContext: undefined !, - handleEvent: undefined !, - updateDirectives: undefined !, - updateRenderer: undefined !, - dirtyParentQueries: undefined !, + setCurrentNode: undefined!, + createRootView: undefined!, + createEmbeddedView: undefined!, + createComponentView: undefined!, + createNgModuleRef: undefined!, + overrideProvider: undefined!, + overrideComponentView: undefined!, + clearOverrides: undefined!, + checkAndUpdateView: undefined!, + checkNoChangesView: undefined!, + destroyView: undefined!, + resolveDep: undefined!, + createDebugContext: undefined!, + handleEvent: undefined!, + updateDirectives: undefined!, + updateRenderer: undefined!, + dirtyParentQueries: undefined!, }; diff --git a/packages/core/src/view/util.ts b/packages/core/src/view/util.ts index 5e88b2124bf6b..29077efb69212 100644 --- a/packages/core/src/view/util.ts +++ b/packages/core/src/view/util.ts @@ -6,14 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {WrappedValue, devModeEqual} from '../change_detection/change_detection'; +import {devModeEqual, WrappedValue} from '../change_detection/change_detection'; import {SOURCE} from '../di/injector_compatibility'; import {ViewEncapsulation} from '../metadata/view'; import {RendererType2} from '../render/api'; import {looseIdentical} from '../util/comparison'; import {stringify} from '../util/stringify'; + import {expressionChangedAfterItHasBeenCheckedError} from './errors'; -import {BindingDef, BindingFlags, Definition, DefinitionFactory, DepDef, DepFlags, ElementData, NodeDef, NodeFlags, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asTextData} from './types'; +import {asElementData, asTextData, BindingDef, BindingFlags, Definition, DefinitionFactory, DepDef, DepFlags, ElementData, NodeDef, NodeFlags, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState} from './types'; export const NOOP: any = () => {}; @@ -44,7 +45,7 @@ const EMPTY_RENDERER_TYPE_ID = '$$empty'; // Attention: this function is called as top level function. // Putting any logic in here will destroy closure tree shaking! export function createRendererType2(values: { - styles: (string | any[])[], + styles: (string|any[])[], encapsulation: ViewEncapsulation, data: {[kind: string]: any[]} }): RendererType2 { @@ -58,7 +59,7 @@ export function createRendererType2(values: { let _renderCompCount = 0; -export function resolveRendererType2(type?: RendererType2 | null): RendererType2|null { +export function resolveRendererType2(type?: RendererType2|null): RendererType2|null { if (type && type.id === UNDEFINED_RENDERER_TYPE_ID) { // first time we see this RendererType2. Initialize it... const isFilled = @@ -142,7 +143,7 @@ export function dispatchEvent( export function declaredViewContainer(view: ViewData): ElementData|null { if (view.parent) { const parentView = view.parent; - return asElementData(parentView, view.parentNodeDef !.nodeIndex); + return asElementData(parentView, view.parentNodeDef!.nodeIndex); } return null; } @@ -155,7 +156,7 @@ export function declaredViewContainer(view: ViewData): ElementData|null { export function viewParentEl(view: ViewData): NodeDef|null { const parentView = view.parent; if (parentView) { - return view.parentNodeDef !.parent; + return view.parentNodeDef!.parent; } else { return null; } @@ -170,24 +171,23 @@ export function renderNode(view: ViewData, def: NodeDef): any { } } -export function elementEventFullName(target: string | null, name: string): string { +export function elementEventFullName(target: string|null, name: string): string { return target ? `${target}:${name}` : name; } export function isComponentView(view: ViewData): boolean { - return !!view.parent && !!(view.parentNodeDef !.flags & NodeFlags.Component); + return !!view.parent && !!(view.parentNodeDef!.flags & NodeFlags.Component); } export function isEmbeddedView(view: ViewData): boolean { - return !!view.parent && !(view.parentNodeDef !.flags & NodeFlags.Component); + return !!view.parent && !(view.parentNodeDef!.flags & NodeFlags.Component); } export function filterQueryId(queryId: number): number { return 1 << (queryId % 32); } -export function splitMatchedQueriesDsl( - matchedQueriesDsl: [string | number, QueryValueType][] | null): { +export function splitMatchedQueriesDsl(matchedQueriesDsl: [string|number, QueryValueType][]|null): { matchedQueries: {[queryId: string]: QueryValueType}, references: {[refId: string]: QueryValueType}, matchedQueryIds: number @@ -208,7 +208,7 @@ export function splitMatchedQueriesDsl( return {matchedQueries, references, matchedQueryIds}; } -export function splitDepsDsl(deps: ([DepFlags, any] | any)[], sourceName?: string): DepDef[] { +export function splitDepsDsl(deps: ([DepFlags, any]|any)[], sourceName?: string): DepDef[] { return deps.map(value => { let token: any; let flags: DepFlags; @@ -230,12 +230,11 @@ export function getParentRenderElement(view: ViewData, renderHost: any, def: Nod if (renderParent) { if ((renderParent.flags & NodeFlags.TypeElement) === 0 || (renderParent.flags & NodeFlags.ComponentView) === 0 || - (renderParent.element !.componentRendererType && - renderParent.element !.componentRendererType !.encapsulation === - ViewEncapsulation.Native)) { + (renderParent.element!.componentRendererType && + renderParent.element!.componentRendererType!.encapsulation === ViewEncapsulation.Native)) { // only children of non components, or children of components with native encapsulation should // be attached. - return asElementData(view, def.renderParent !.nodeIndex).renderElement; + return asElementData(view, def.renderParent!.nodeIndex).renderElement; } } else { return renderHost; @@ -245,7 +244,7 @@ export function getParentRenderElement(view: ViewData, renderHost: any, def: Nod const DEFINITION_CACHE = new WeakMap<any, Definition<any>>(); export function resolveDefinition<D extends Definition<any>>(factory: DefinitionFactory<D>): D { - let value = DEFINITION_CACHE.get(factory) !as D; + let value = DEFINITION_CACHE.get(factory)! as D; if (!value) { value = factory(() => NOOP); value.factory = factory; @@ -260,13 +259,18 @@ export function rootRenderNodes(view: ViewData): any[] { return renderNodes; } -export const enum RenderNodeAction {Collect, AppendChild, InsertBefore, RemoveChild} +export const enum RenderNodeAction { + Collect, + AppendChild, + InsertBefore, + RemoveChild +} export function visitRootRenderNodes( view: ViewData, action: RenderNodeAction, parentNode: any, nextSibling: any, target?: any[]) { // We need to re-compute the parent node in case the nodes have been moved around manually if (action === RenderNodeAction.RemoveChild) { - parentNode = view.renderer.parentNode(renderNode(view, view.def.lastRenderRootNode !)); + parentNode = view.renderer.parentNode(renderNode(view, view.def.lastRenderRootNode!)); } visitSiblingRenderNodes( view, action, 0, view.def.nodes.length - 1, parentNode, nextSibling, target); @@ -292,19 +296,19 @@ export function visitProjectedRenderNodes( while (compView && !isComponentView(compView)) { compView = compView.parent; } - const hostView = compView !.parent; - const hostElDef = viewParentEl(compView !); - const startIndex = hostElDef !.nodeIndex + 1; - const endIndex = hostElDef !.nodeIndex + hostElDef !.childCount; + const hostView = compView!.parent; + const hostElDef = viewParentEl(compView!); + const startIndex = hostElDef!.nodeIndex + 1; + const endIndex = hostElDef!.nodeIndex + hostElDef!.childCount; for (let i = startIndex; i <= endIndex; i++) { - const nodeDef = hostView !.def.nodes[i]; + const nodeDef = hostView!.def.nodes[i]; if (nodeDef.ngContentIndex === ngContentIndex) { - visitRenderNode(hostView !, nodeDef, action, parentNode, nextSibling, target); + visitRenderNode(hostView!, nodeDef, action, parentNode, nextSibling, target); } // jump to next sibling i += nodeDef.childCount; } - if (!hostView !.parent) { + if (!hostView!.parent) { // a root view const projectedNodes = view.root.projectableNodes[ngContentIndex]; if (projectedNodes) { @@ -320,7 +324,7 @@ function visitRenderNode( target?: any[]) { if (nodeDef.flags & NodeFlags.TypeNgContent) { visitProjectedRenderNodes( - view, nodeDef.ngContent !.index, action, parentNode, nextSibling, target); + view, nodeDef.ngContent!.index, action, parentNode, nextSibling, target); } else { const rn = renderNode(view, nodeDef); if (action === RenderNodeAction.RemoveChild && (nodeDef.flags & NodeFlags.ComponentView) && @@ -337,12 +341,12 @@ function visitRenderNode( execRenderNodeAction(view, rn, action, parentNode, nextSibling, target); } if (nodeDef.flags & NodeFlags.EmbeddedViews) { - const embeddedViews = asElementData(view, nodeDef.nodeIndex).viewContainer !._embeddedViews; + const embeddedViews = asElementData(view, nodeDef.nodeIndex).viewContainer!._embeddedViews; for (let k = 0; k < embeddedViews.length; k++) { visitRootRenderNodes(embeddedViews[k], action, parentNode, nextSibling, target); } } - if (nodeDef.flags & NodeFlags.TypeElement && !nodeDef.element !.name) { + if (nodeDef.flags & NodeFlags.TypeElement && !nodeDef.element!.name) { visitSiblingRenderNodes( view, action, nodeDef.nodeIndex + 1, nodeDef.nodeIndex + nodeDef.childCount, parentNode, nextSibling, target); @@ -365,7 +369,7 @@ function execRenderNodeAction( renderer.removeChild(parentNode, renderNode); break; case RenderNodeAction.Collect: - target !.push(renderNode); + target!.push(renderNode); break; } } @@ -374,7 +378,7 @@ const NS_PREFIX_RE = /^:([^:]+):(.+)$/; export function splitNamespace(name: string): string[] { if (name[0] === ':') { - const match = name.match(NS_PREFIX_RE) !; + const match = name.match(NS_PREFIX_RE)!; return [match[1], match[2]]; } return ['', name]; diff --git a/packages/core/src/view/view.ts b/packages/core/src/view/view.ts index 434473f435def..a9cc1e13e3590 100644 --- a/packages/core/src/view/view.ts +++ b/packages/core/src/view/view.ts @@ -16,13 +16,13 @@ import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, import {checkAndUpdateQuery, createQuery} from './query'; import {createTemplateData, createViewContainerData} from './refs'; import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text'; -import {ArgumentType, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asQueryList, asTextData, shiftInitState} from './types'; -import {NOOP, checkBindingNoChanges, isComponentView, markParentViewsForCheckProjectedViews, resolveDefinition, tokenKey} from './util'; +import {ArgumentType, asElementData, asQueryList, asTextData, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, shiftInitState, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn} from './types'; +import {checkBindingNoChanges, isComponentView, markParentViewsForCheckProjectedViews, NOOP, resolveDefinition, tokenKey} from './util'; import {detachProjectedView} from './view_attach'; export function viewDef( - flags: ViewFlags, nodes: NodeDef[], updateDirectives?: null | ViewUpdateFn, - updateRenderer?: null | ViewUpdateFn): ViewDefinition { + flags: ViewFlags, nodes: NodeDef[], updateDirectives?: null|ViewUpdateFn, + updateRenderer?: null|ViewUpdateFn): ViewDefinition { // clone nodes and set auto calculated values let viewBindingCount = 0; let viewDisposableCount = 0; @@ -48,7 +48,7 @@ export function viewDef( if (node.element) { const elDef = node.element; elDef.publicProviders = - currentParent ? currentParent.element !.publicProviders : Object.create(null); + currentParent ? currentParent.element!.publicProviders : Object.create(null); elDef.allProviders = elDef.publicProviders; // Note: We assume that all providers of an element are before any child element! currentElementHasPublicProviders = false; @@ -72,25 +72,25 @@ export function viewDef( if (!currentElementHasPublicProviders) { currentElementHasPublicProviders = true; // Use prototypical inheritance to not get O(n^2) complexity... - currentParent !.element !.publicProviders = - Object.create(currentParent !.element !.publicProviders); - currentParent !.element !.allProviders = currentParent !.element !.publicProviders; + currentParent!.element!.publicProviders = + Object.create(currentParent!.element!.publicProviders); + currentParent!.element!.allProviders = currentParent!.element!.publicProviders; } const isPrivateService = (node.flags & NodeFlags.PrivateProvider) !== 0; const isComponent = (node.flags & NodeFlags.Component) !== 0; if (!isPrivateService || isComponent) { - currentParent !.element !.publicProviders ![tokenKey(node.provider !.token)] = node; + currentParent!.element!.publicProviders![tokenKey(node.provider!.token)] = node; } else { if (!currentElementHasPrivateProviders) { currentElementHasPrivateProviders = true; // Use prototypical inheritance to not get O(n^2) complexity... - currentParent !.element !.allProviders = - Object.create(currentParent !.element !.publicProviders); + currentParent!.element!.allProviders = + Object.create(currentParent!.element!.publicProviders); } - currentParent !.element !.allProviders ![tokenKey(node.provider !.token)] = node; + currentParent!.element!.allProviders![tokenKey(node.provider!.token)] = node; } if (isComponent) { - currentParent !.element !.componentProvider = node; + currentParent!.element!.componentProvider = node; } } @@ -135,27 +135,30 @@ export function viewDef( } const handleEvent: ViewHandleEventFn = (view, nodeIndex, eventName, event) => - nodes[nodeIndex].element !.handleEvent !(view, eventName, event); + nodes[nodeIndex].element!.handleEvent!(view, eventName, event); return { // Will be filled later... factory: null, nodeFlags: viewNodeFlags, rootNodeFlags: viewRootNodeFlags, - nodeMatchedQueries: viewMatchedQueries, flags, + nodeMatchedQueries: viewMatchedQueries, + flags, nodes: nodes, updateDirectives: updateDirectives || NOOP, - updateRenderer: updateRenderer || NOOP, handleEvent, + updateRenderer: updateRenderer || NOOP, + handleEvent, bindingCount: viewBindingCount, - outputCount: viewDisposableCount, lastRenderRootNode + outputCount: viewDisposableCount, + lastRenderRootNode }; } function isNgContainer(node: NodeDef): boolean { - return (node.flags & NodeFlags.TypeElement) !== 0 && node.element !.name === null; + return (node.flags & NodeFlags.TypeElement) !== 0 && node.element!.name === null; } -function validateNode(parent: NodeDef | null, node: NodeDef, nodeCount: number) { +function validateNode(parent: NodeDef|null, node: NodeDef, nodeCount: number) { const template = node.element && node.element.template; if (template) { if (!template.lastRenderRootNode) { @@ -164,25 +167,28 @@ function validateNode(parent: NodeDef | null, node: NodeDef, nodeCount: number) if (template.lastRenderRootNode && template.lastRenderRootNode.flags & NodeFlags.EmbeddedViews) { throw new Error( - `Illegal State: Last root node of a template can't have embedded views, at index ${node.nodeIndex}!`); + `Illegal State: Last root node of a template can't have embedded views, at index ${ + node.nodeIndex}!`); } } if (node.flags & NodeFlags.CatProvider) { const parentFlags = parent ? parent.flags : 0; if ((parentFlags & NodeFlags.TypeElement) === 0) { throw new Error( - `Illegal State: StaticProvider/Directive nodes need to be children of elements or anchors, at index ${node.nodeIndex}!`); + `Illegal State: StaticProvider/Directive nodes need to be children of elements or anchors, at index ${ + node.nodeIndex}!`); } } if (node.query) { if (node.flags & NodeFlags.TypeContentQuery && (!parent || (parent.flags & NodeFlags.TypeDirective) === 0)) { throw new Error( - `Illegal State: Content Query nodes need to be children of directives, at index ${node.nodeIndex}!`); + `Illegal State: Content Query nodes need to be children of directives, at index ${ + node.nodeIndex}!`); } if (node.flags & NodeFlags.TypeViewQuery && parent) { - throw new Error( - `Illegal State: View Query nodes have to be top level nodes, at index ${node.nodeIndex}!`); + throw new Error(`Illegal State: View Query nodes have to be top level nodes, at index ${ + node.nodeIndex}!`); } } if (node.childCount) { @@ -213,7 +219,7 @@ export function createRootView(root: RootData, def: ViewDefinition, context?: an export function createComponentView( parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData { - const rendererType = nodeDef.element !.componentRendererType; + const rendererType = nodeDef.element!.componentRendererType; let compRenderer: Renderer2; if (!rendererType) { compRenderer = parentView.root.renderer; @@ -221,22 +227,27 @@ export function createComponentView( compRenderer = parentView.root.rendererFactory.createRenderer(hostElement, rendererType); } return createView( - parentView.root, compRenderer, parentView, nodeDef.element !.componentProvider, viewDef); + parentView.root, compRenderer, parentView, nodeDef.element!.componentProvider, viewDef); } function createView( - root: RootData, renderer: Renderer2, parent: ViewData | null, parentNodeDef: NodeDef | null, + root: RootData, renderer: Renderer2, parent: ViewData|null, parentNodeDef: NodeDef|null, def: ViewDefinition): ViewData { const nodes: NodeData[] = new Array(def.nodes.length); const disposables = def.outputCount ? new Array(def.outputCount) : null; const view: ViewData = { def, parent, - viewContainerParent: null, parentNodeDef, + viewContainerParent: null, + parentNodeDef, context: null, - component: null, nodes, - state: ViewState.CatInit, root, renderer, - oldValues: new Array(def.bindingCount), disposables, + component: null, + nodes, + state: ViewState.CatInit, + root, + renderer, + oldValues: new Array(def.bindingCount), + disposables, initIndex: -1 }; return view; @@ -251,7 +262,7 @@ function createViewNodes(view: ViewData) { let renderHost: any; if (isComponentView(view)) { const hostDef = view.parentNodeDef; - renderHost = asElementData(view.parent !, hostDef !.parent !.nodeIndex).renderElement; + renderHost = asElementData(view.parent!, hostDef!.parent!.nodeIndex).renderElement; } const def = view.def; const nodes = view.nodes; @@ -262,9 +273,9 @@ function createViewNodes(view: ViewData) { switch (nodeDef.flags & NodeFlags.Types) { case NodeFlags.TypeElement: const el = createElement(view, renderHost, nodeDef) as any; - let componentView: ViewData = undefined !; + let componentView: ViewData = undefined!; if (nodeDef.flags & NodeFlags.ComponentView) { - const compViewDef = resolveDefinition(nodeDef.element !.componentView !); + const compViewDef = resolveDefinition(nodeDef.element!.componentView!); componentView = Services.createComponentView(view, nodeDef, compViewDef, el); } listenToElementOutputs(view, componentView, nodeDef, el); @@ -272,7 +283,7 @@ function createViewNodes(view: ViewData) { renderElement: el, componentView, viewContainer: null, - template: nodeDef.element !.template ? createTemplateData(view, nodeDef) : undefined + template: nodeDef.element!.template ? createTemplateData(view, nodeDef) : undefined }; if (nodeDef.flags & NodeFlags.EmbeddedViews) { nodeData.viewContainer = createViewContainerData(view, nodeDef, nodeData); @@ -304,7 +315,7 @@ function createViewNodes(view: ViewData) { nodeData = <ProviderData>{instance}; } if (nodeDef.flags & NodeFlags.Component) { - const compView = asElementData(view, nodeDef.parent !.nodeIndex).componentView; + const compView = asElementData(view, nodeDef.parent!.nodeIndex).componentView; initView(compView, nodeData.instance, nodeData.instance); } break; @@ -529,9 +540,9 @@ function destroyViewNodes(view: ViewData) { for (let i = 0; i < len; i++) { const def = view.def.nodes[i]; if (def.flags & NodeFlags.TypeElement) { - view.renderer.destroyNode !(asElementData(view, i).renderElement); + view.renderer.destroyNode!(asElementData(view, i).renderElement); } else if (def.flags & NodeFlags.TypeText) { - view.renderer.destroyNode !(asTextData(view, i).renderText); + view.renderer.destroyNode!(asTextData(view, i).renderText); } else if (def.flags & NodeFlags.TypeContentQuery || def.flags & NodeFlags.TypeViewQuery) { asQueryList(view, i).destroy(); } @@ -575,7 +586,7 @@ function execEmbeddedViewsAction(view: ViewData, action: ViewAction) { const nodeDef = def.nodes[i]; if (nodeDef.flags & NodeFlags.EmbeddedViews) { // a leaf - const embeddedViews = asElementData(view, i).viewContainer !._embeddedViews; + const embeddedViews = asElementData(view, i).viewContainer!._embeddedViews; for (let k = 0; k < embeddedViews.length; k++) { callViewAction(embeddedViews[k], action); } diff --git a/packages/core/src/view/view_attach.ts b/packages/core/src/view/view_attach.ts index ea86b9a4276ca..81c392d9f4edc 100644 --- a/packages/core/src/view/view_attach.ts +++ b/packages/core/src/view/view_attach.ts @@ -7,23 +7,24 @@ */ import {addToArray, removeFromArray} from '../util/array_utils'; + import {ElementData, NodeDef, NodeFlags, Services, ViewData, ViewDefinition, ViewState} from './types'; -import {RenderNodeAction, declaredViewContainer, renderNode, visitRootRenderNodes} from './util'; +import {declaredViewContainer, renderNode, RenderNodeAction, visitRootRenderNodes} from './util'; export function attachEmbeddedView( - parentView: ViewData, elementData: ElementData, viewIndex: number | undefined | null, + parentView: ViewData, elementData: ElementData, viewIndex: number|undefined|null, view: ViewData) { - let embeddedViews = elementData.viewContainer !._embeddedViews; + let embeddedViews = elementData.viewContainer!._embeddedViews; if (viewIndex === null || viewIndex === undefined) { viewIndex = embeddedViews.length; } view.viewContainerParent = parentView; - addToArray(embeddedViews, viewIndex !, view); + addToArray(embeddedViews, viewIndex!, view); attachProjectedView(elementData, view); Services.dirtyParentQueries(view); - const prevView = viewIndex ! > 0 ? embeddedViews[viewIndex ! - 1] : null; + const prevView = viewIndex! > 0 ? embeddedViews[viewIndex! - 1] : null; renderAttachEmbeddedView(elementData, prevView, view); } @@ -48,7 +49,7 @@ function attachProjectedView(vcElementData: ElementData, view: ViewData) { projectedViews.push(view); // Note: we are changing the NodeDef here as we cannot calculate // the fact whether a template is used for projection during compilation. - markNodeAsProjectedTemplate(view.parent !.def, view.parentNodeDef !); + markNodeAsProjectedTemplate(view.parent!.def, view.parentNodeDef!); } function markNodeAsProjectedTemplate(viewDef: ViewDefinition, nodeDef: NodeDef) { @@ -65,7 +66,7 @@ function markNodeAsProjectedTemplate(viewDef: ViewDefinition, nodeDef: NodeDef) } export function detachEmbeddedView(elementData: ElementData, viewIndex?: number): ViewData|null { - const embeddedViews = elementData.viewContainer !._embeddedViews; + const embeddedViews = elementData.viewContainer!._embeddedViews; if (viewIndex == null || viewIndex >= embeddedViews.length) { viewIndex = embeddedViews.length - 1; } @@ -100,7 +101,7 @@ export function detachProjectedView(view: ViewData) { export function moveEmbeddedView( elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData { - const embeddedViews = elementData.viewContainer !._embeddedViews; + const embeddedViews = elementData.viewContainer!._embeddedViews; const view = embeddedViews[oldViewIndex]; removeFromArray(embeddedViews, oldViewIndex); if (newViewIndex == null) { @@ -121,9 +122,9 @@ export function moveEmbeddedView( } function renderAttachEmbeddedView( - elementData: ElementData, prevView: ViewData | null, view: ViewData) { - const prevRenderNode = prevView ? renderNode(prevView, prevView.def.lastRenderRootNode !) : - elementData.renderElement; + elementData: ElementData, prevView: ViewData|null, view: ViewData) { + const prevRenderNode = + prevView ? renderNode(prevView, prevView.def.lastRenderRootNode!) : elementData.renderElement; const parentNode = view.renderer.parentNode(prevRenderNode); const nextSibling = view.renderer.nextSibling(prevRenderNode); // Note: We can't check if `nextSibling` is present, as on WebWorkers it will always be! diff --git a/packages/core/src/zone/ng_zone.ts b/packages/core/src/zone/ng_zone.ts index bfca86e193850..a5387e4cdb5be 100644 --- a/packages/core/src/zone/ng_zone.ts +++ b/packages/core/src/zone/ng_zone.ts @@ -148,7 +148,9 @@ export class NgZone { forkInnerZoneWithAngularBehavior(self); } - static isInAngularZone(): boolean { return Zone.current.get('isAngularZone') === true; } + static isInAngularZone(): boolean { + return Zone.current.get('isAngularZone') === true; + } static assertInAngularZone(): void { if (!NgZone.isInAngularZone()) { @@ -274,36 +276,40 @@ function delayChangeDetectionForEvents(zone: NgZonePrivate) { } function forkInnerZoneWithAngularBehavior(zone: NgZonePrivate) { - const delayChangeDetectionForEventsDelegate = () => { delayChangeDetectionForEvents(zone); }; + const delayChangeDetectionForEventsDelegate = () => { + delayChangeDetectionForEvents(zone); + }; const maybeDelayChangeDetection = !!zone.shouldCoalesceEventChangeDetection && zone.nativeRequestAnimationFrame && delayChangeDetectionForEventsDelegate; zone._inner = zone._inner.fork({ name: 'angular', properties: <any>{'isAngularZone': true, 'maybeDelayChangeDetection': maybeDelayChangeDetection}, - onInvokeTask: (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task, applyThis: any, - applyArgs: any): any => { - try { - onEnter(zone); - return delegate.invokeTask(target, task, applyThis, applyArgs); - } finally { - if (maybeDelayChangeDetection && task.type === 'eventTask') { - maybeDelayChangeDetection(); - } - onLeave(zone); - } - }, + onInvokeTask: + (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task, applyThis: any, + applyArgs: any): any => { + try { + onEnter(zone); + return delegate.invokeTask(target, task, applyThis, applyArgs); + } finally { + if (maybeDelayChangeDetection && task.type === 'eventTask') { + maybeDelayChangeDetection(); + } + onLeave(zone); + } + }, - onInvoke: (delegate: ZoneDelegate, current: Zone, target: Zone, callback: Function, - applyThis: any, applyArgs?: any[], source?: string): any => { - try { - onEnter(zone); - return delegate.invoke(target, callback, applyThis, applyArgs, source); - } finally { - onLeave(zone); - } - }, + onInvoke: + (delegate: ZoneDelegate, current: Zone, target: Zone, callback: Function, applyThis: any, + applyArgs?: any[], source?: string): any => { + try { + onEnter(zone); + return delegate.invoke(target, callback, applyThis, applyArgs, source); + } finally { + onLeave(zone); + } + }, onHasTask: (delegate: ZoneDelegate, current: Zone, target: Zone, hasTaskState: HasTaskState) => { @@ -372,7 +378,9 @@ export class NoopNgZone implements NgZone { return fn.apply(applyThis, applyArgs); } - runOutsideAngular(fn: (...args: any[]) => any): any { return fn(); } + runOutsideAngular(fn: (...args: any[]) => any): any { + return fn(); + } runTask(fn: (...args: any[]) => any, applyThis?: any, applyArgs?: any, name?: string): any { return fn.apply(applyThis, applyArgs); diff --git a/packages/core/test/BUILD.bazel b/packages/core/test/BUILD.bazel index 3e65243eeb76d..d5fc99181550b 100644 --- a/packages/core/test/BUILD.bazel +++ b/packages/core/test/BUILD.bazel @@ -87,15 +87,6 @@ jasmine_node_test( karma_web_test_suite( name = "test_web", - tags = [ - # FIXME: fix on saucelabs - # IE 11.0.0 (Windows 8.1.0.0) ivy NgModule providers should throw when the aliased provider does not exist FAILED - # Error: Expected function to throw an exception with message 'R3InjectorError(SomeModule)[car -> SportsCar]: - # NullInjectorError: No provider for Car!', but it threw an exception with message 'R3InjectorError(SomeModule)[car -> Car]: - # NullInjectorError: No provider for Car!'. - # at <Jasmine> - "fixme-saucelabs-ivy", - ], deps = [ ":test_lib", ], diff --git a/packages/core/test/acceptance/bootstrap_spec.ts b/packages/core/test/acceptance/bootstrap_spec.ts index 91dd57d46b1fa..a2fc8ced6f4e3 100644 --- a/packages/core/test/acceptance/bootstrap_spec.ts +++ b/packages/core/test/acceptance/bootstrap_spec.ts @@ -6,18 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMPILER_OPTIONS, Component, NgModule, ViewEncapsulation, destroyPlatform} from '@angular/core'; +import {COMPILER_OPTIONS, Component, destroyPlatform, NgModule, ViewEncapsulation} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {onlyInIvy, withBody} from '@angular/private/testing'; describe('bootstrap', () => { - beforeEach(destroyPlatform); afterEach(destroyPlatform); it('should bootstrap using #id selector', - withBody('<div>before|</div><button id="my-app"></button>', async() => { + withBody('<div>before|</div><button id="my-app"></button>', async () => { try { const ngModuleRef = await platformBrowserDynamic().bootstrapModule(IdSelectorAppModule); expect(document.body.textContent).toEqual('before|works!'); @@ -28,7 +27,7 @@ describe('bootstrap', () => { })); it('should bootstrap using one of selectors from the list', - withBody('<div>before|</div><div class="bar"></div>', async() => { + withBody('<div>before|</div><div class="bar"></div>', async () => { try { const ngModuleRef = await platformBrowserDynamic().bootstrapModule(MultipleSelectorsAppModule); @@ -66,7 +65,7 @@ describe('bootstrap', () => { } it('should use ViewEncapsulation.Emulated as default', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule); @@ -75,7 +74,7 @@ describe('bootstrap', () => { })); it('should allow setting defaultEncapsulation using bootstrap option', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const ngModuleRef = await platformBrowserDynamic().bootstrapModule( @@ -86,7 +85,7 @@ describe('bootstrap', () => { })); it('should allow setting defaultEncapsulation using compiler option', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const ngModuleRef = await platformBrowserDynamic([{ @@ -100,7 +99,7 @@ describe('bootstrap', () => { })); it('should prefer encapsulation on component over bootstrap option', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule({encapsulation: ViewEncapsulation.Emulated}); const ngModuleRef = await platformBrowserDynamic().bootstrapModule( @@ -110,7 +109,7 @@ describe('bootstrap', () => { })); it('should use preserveWhitespaces: false as default', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule); @@ -119,7 +118,7 @@ describe('bootstrap', () => { })); it('should allow setting preserveWhitespaces using bootstrap option', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const ngModuleRef = await platformBrowserDynamic().bootstrapModule( @@ -129,7 +128,7 @@ describe('bootstrap', () => { })); it('should allow setting preserveWhitespaces using compiler option', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const ngModuleRef = @@ -141,7 +140,7 @@ describe('bootstrap', () => { })); it('should prefer preserveWhitespaces on component over bootstrap option', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule({preserveWhitespaces: false}); const ngModuleRef = await platformBrowserDynamic().bootstrapModule( @@ -151,10 +150,12 @@ describe('bootstrap', () => { })); onlyInIvy('options cannot be changed in Ivy').describe('changing bootstrap options', () => { - beforeEach(() => { spyOn(console, 'error'); }); + beforeEach(() => { + spyOn(console, 'error'); + }); it('should log an error when changing defaultEncapsulation bootstrap options', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const platformRef = platformBrowserDynamic(); @@ -175,7 +176,7 @@ describe('bootstrap', () => { })); it('should log an error when changing preserveWhitespaces bootstrap options', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const platformRef = platformBrowserDynamic(); @@ -196,7 +197,7 @@ describe('bootstrap', () => { })); it('should log an error when changing defaultEncapsulation to its default', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const platformRef = platformBrowserDynamic(); @@ -215,7 +216,7 @@ describe('bootstrap', () => { })); it('should log an error when changing preserveWhitespaces to its default', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const platformRef = platformBrowserDynamic(); @@ -234,7 +235,7 @@ describe('bootstrap', () => { })); it('should not log an error when passing identical bootstrap options', - withBody('<my-app></my-app>', async() => { + withBody('<my-app></my-app>', async () => { const TestModule = createComponentAndModule(); const platformRef = platformBrowserDynamic(); diff --git a/packages/core/test/acceptance/change_detection_spec.ts b/packages/core/test/acceptance/change_detection_spec.ts index a5d6b41b2ef3a..9e9643ddbc917 100644 --- a/packages/core/test/acceptance/change_detection_spec.ts +++ b/packages/core/test/acceptance/change_detection_spec.ts @@ -16,16 +16,16 @@ import {ivyEnabled} from '@angular/private/testing'; import {BehaviorSubject} from 'rxjs'; describe('change detection', () => { - describe('embedded views', () => { - @Directive({selector: '[viewManipulation]', exportAs: 'vm'}) class ViewManipulation { constructor( private _tplRef: TemplateRef<{}>, public vcRef: ViewContainerRef, private _appRef: ApplicationRef) {} - insertIntoVcRef() { return this.vcRef.createEmbeddedView(this._tplRef); } + insertIntoVcRef() { + return this.vcRef.createEmbeddedView(this._tplRef); + } insertIntoAppRef(): EmbeddedViewRef<{}> { const viewRef = this._tplRef.createEmbeddedView({}); @@ -80,7 +80,9 @@ describe('change detection', () => { changeDetection: ChangeDetectionStrategy.OnPush }) class App { - increment(counter: 'componentView'|'embeddedView') { counters[counter]++; } + increment(counter: 'componentView'|'embeddedView') { + counters[counter]++; + } noop() {} } @@ -129,7 +131,9 @@ describe('change detection', () => { changeDetection: ChangeDetectionStrategy.OnPush }) class DynamicComp { - increment() { counter++; } + increment() { + counter++; + } noop() {} } @@ -170,13 +174,10 @@ describe('change detection', () => { expect(counter).toBe(3); }); - }); describe('markForCheck', () => { - it('should mark OnPush ancestor of dynamically created component views as dirty', () => { - @Component({ selector: `test-cmpt`, template: `{{counter}}|<ng-template #vc></ng-template>`, @@ -184,7 +185,7 @@ describe('change detection', () => { }) class TestCmpt { counter = 0; - @ViewChild('vc', {read: ViewContainerRef}) vcRef !: ViewContainerRef; + @ViewChild('vc', {read: ViewContainerRef}) vcRef!: ViewContainerRef; constructor(private _cfr: ComponentFactoryResolver) {} @@ -252,11 +253,13 @@ describe('change detection', () => { /** * @internal */ - private _input !: number; + private _input!: number; constructor(private cdr: ChangeDetectorRef) {} - get input() { return this._input; } + get input() { + return this._input; + } set input(value: number) { this._input = value; @@ -286,18 +289,19 @@ describe('change detection', () => { template: `{{ doCheckCount }} - {{ name }} <button (click)="onClick()"></button>` }) class MyComponent implements DoCheck { - @Input() - name = 'Nancy'; + @Input() name = 'Nancy'; doCheckCount = 0; - ngDoCheck(): void { this.doCheckCount++; } + ngDoCheck(): void { + this.doCheckCount++; + } onClick() {} } @Component({selector: 'my-app', template: '<my-comp [name]="name"></my-comp>'}) class MyApp { - @ViewChild(MyComponent) comp !: MyComponent; + @ViewChild(MyComponent) comp!: MyComponent; name: string = 'Nancy'; } @@ -369,7 +373,7 @@ describe('change detection', () => { expect(fixture.componentInstance.comp.doCheckCount).toEqual(1); expect(fixture.nativeElement.textContent.trim()).toEqual('1 - Nancy'); - const button = fixture.nativeElement.querySelector('button') !; + const button = fixture.nativeElement.querySelector('button')!; button.click(); // No ticks should have been scheduled. @@ -389,7 +393,7 @@ describe('change detection', () => { template: '<my-comp></my-comp><button id="parent" (click)="noop()"></button>' }) class ButtonParent { - @ViewChild(MyComponent) comp !: MyComponent; + @ViewChild(MyComponent) comp!: MyComponent; noop() {} } @@ -415,16 +419,18 @@ describe('change detection', () => { changeDetection: ChangeDetectionStrategy.OnPush }) class ButtonParent implements DoCheck { - @ViewChild(MyComponent) comp !: MyComponent; + @ViewChild(MyComponent) comp!: MyComponent; noop() {} doCheckCount = 0; - ngDoCheck(): void { this.doCheckCount++; } + ngDoCheck(): void { + this.doCheckCount++; + } } @Component({selector: 'my-button-app', template: '<button-parent></button-parent>'}) class MyButtonApp { - @ViewChild(ButtonParent) parent !: ButtonParent; + @ViewChild(ButtonParent) parent!: ButtonParent; } TestBed.configureTestingModule({declarations: [MyButtonApp, MyComponent, ButtonParent]}); @@ -456,7 +462,6 @@ describe('change detection', () => { expect(comp.doCheckCount).toEqual(2); expect(fixture.nativeElement.textContent.trim()).toEqual('3 - 2 - Nancy'); }); - }); describe('ChangeDetectorRef', () => { @@ -472,18 +477,22 @@ describe('change detection', () => { constructor(public cdr: ChangeDetectorRef) {} - ngDoCheck() { this.doCheckCount++; } + ngDoCheck() { + this.doCheckCount++; + } } @Component({selector: 'parent-comp', template: `{{ doCheckCount}} - <my-comp></my-comp>`}) class ParentComp implements DoCheck { - @ViewChild(MyComp) myComp !: MyComp; + @ViewChild(MyComp) myComp!: MyComp; doCheckCount = 0; constructor(public cdr: ChangeDetectorRef) {} - ngDoCheck() { this.doCheckCount++; } + ngDoCheck() { + this.doCheckCount++; + } } @Directive({selector: '[dir]'}) @@ -562,8 +571,8 @@ describe('change detection', () => { it('should check component view when called by directive on component node', () => { @Component({template: '<my-comp dir></my-comp>'}) class MyApp { - @ViewChild(MyComp) myComp !: MyComp; - @ViewChild(Dir) dir !: Dir; + @ViewChild(MyComp) myComp!: MyComp; + @ViewChild(Dir) dir!: Dir; } TestBed.configureTestingModule({declarations: [MyComp, Dir, MyApp]}); @@ -580,8 +589,8 @@ describe('change detection', () => { it('should check host component when called by directive on element node', () => { @Component({template: '{{ value }}<div dir></div>'}) class MyApp { - @ViewChild(MyComp) myComp !: MyComp; - @ViewChild(Dir) dir !: Dir; + @ViewChild(MyComp) myComp!: MyComp; + @ViewChild(Dir) dir!: Dir; value = ''; } @@ -601,7 +610,7 @@ describe('change detection', () => { it('should check the host component when called from EmbeddedViewRef', () => { @Component({template: '{{ name }}<div *ngIf="showing" dir></div>'}) class MyApp { - @ViewChild(Dir) dir !: Dir; + @ViewChild(Dir) dir!: Dir; showing = true; name = 'Amelia'; } @@ -642,21 +651,30 @@ describe('change detection', () => { @Component({template: '<child-comp [inp]="true"></child-comp>'}) class ParentComp { constructor(public cdr: ChangeDetectorRef) {} - triggerChangeDetection() { this.cdr.detectChanges(); } + triggerChangeDetection() { + this.cdr.detectChanges(); + } } @Component({template: '{{inp}}', selector: 'child-comp'}) class ChildComp { - @Input() - inp: any = ''; + @Input() inp: any = ''; count = 0; constructor(public parentComp: ParentComp) {} - ngOnInit() { this.check('OnInit'); } - ngAfterContentInit() { this.check('AfterContentInit'); } - ngAfterViewInit() { this.check('AfterViewInit'); } - ngOnChanges() { this.check('OnChanges'); } + ngOnInit() { + this.check('OnInit'); + } + ngAfterContentInit() { + this.check('AfterContentInit'); + } + ngAfterViewInit() { + this.check('AfterViewInit'); + } + ngOnChanges() { + this.check('OnChanges'); + } check(h: string) { if (h === hook) { @@ -704,8 +722,7 @@ describe('change detection', () => { ` }) class App { - @ViewChildren('ref') - ref !: QueryList<any>; + @ViewChildren('ref') ref!: QueryList<any>; visible = false; @@ -737,13 +754,14 @@ describe('change detection', () => { describe('dynamic views', () => { @Component({selector: 'structural-comp', template: '{{ value }}'}) class StructuralComp { - @Input() - tmp !: TemplateRef<any>; + @Input() tmp!: TemplateRef<any>; value = 'one'; constructor(public vcr: ViewContainerRef) {} - create() { return this.vcr.createEmbeddedView(this.tmp, {ctx: this}); } + create() { + return this.vcr.createEmbeddedView(this.tmp, {ctx: this}); + } } it('should support ViewRef.detectChanges()', () => { @@ -752,7 +770,7 @@ describe('change detection', () => { '<ng-template #foo let-ctx="ctx">{{ ctx.value }}</ng-template><structural-comp [tmp]="foo"></structural-comp>' }) class App { - @ViewChild(StructuralComp) structuralComp !: StructuralComp; + @ViewChild(StructuralComp) structuralComp!: StructuralComp; } TestBed.configureTestingModule({declarations: [App, StructuralComp]}); @@ -781,7 +799,7 @@ describe('change detection', () => { template: '<ng-template #foo>Template text</ng-template><structural-comp [tmp]="foo">' }) class App { - @ViewChild(StructuralComp) structuralComp !: StructuralComp; + @ViewChild(StructuralComp) structuralComp!: StructuralComp; } TestBed.configureTestingModule({declarations: [App, StructuralComp]}); @@ -794,9 +812,7 @@ describe('change detection', () => { viewRef.detectChanges(); expect(fixture.nativeElement.textContent).toEqual('oneTemplate text'); }); - }); - }); describe('attach/detach', () => { @@ -807,12 +823,14 @@ describe('change detection', () => { constructor(public cdr: ChangeDetectorRef) {} - ngDoCheck() { this.doCheckCount++; } + ngDoCheck() { + this.doCheckCount++; + } } @Component({template: '<detached-comp></detached-comp>'}) class MyApp { - @ViewChild(DetachedComp) comp !: DetachedComp; + @ViewChild(DetachedComp) comp!: DetachedComp; constructor(public cdr: ChangeDetectorRef) {} } @@ -908,22 +926,20 @@ describe('change detection', () => { }); it('should detach OnPush components properly', () => { - @Component({ selector: 'on-push-comp', template: '{{ value }}', changeDetection: ChangeDetectionStrategy.OnPush }) class OnPushComp { - @Input() - value !: string; + @Input() value!: string; constructor(public cdr: ChangeDetectorRef) {} } @Component({template: '<on-push-comp [value]="value"></on-push-comp>'}) class OnPushApp { - @ViewChild(OnPushComp) onPushComp !: OnPushComp; + @ViewChild(OnPushComp) onPushComp!: OnPushComp; value = ''; } @@ -946,7 +962,6 @@ describe('change detection', () => { fixture.detectChanges(); expect(fixture.nativeElement.textContent).toEqual('two'); }); - }); describe('markForCheck()', () => { @@ -962,7 +977,9 @@ describe('change detection', () => { constructor(public cdr: ChangeDetectorRef) {} - ngDoCheck() { this.doCheckCount++; } + ngDoCheck() { + this.doCheckCount++; + } } @Component({ @@ -970,7 +987,7 @@ describe('change detection', () => { changeDetection: ChangeDetectionStrategy.OnPush }) class OnPushParent { - @ViewChild(OnPushComp) comp !: OnPushComp; + @ViewChild(OnPushComp) comp!: OnPushComp; value = 'one'; } @@ -1023,7 +1040,6 @@ describe('change detection', () => { fixture.componentInstance.comp.cdr.markForCheck(); fixture.detectChanges(); expect(fixture.nativeElement.textContent).toEqual('two - one'); - }); it('should ensure OnPush components in embedded views are checked', () => { @@ -1032,7 +1048,7 @@ describe('change detection', () => { changeDetection: ChangeDetectionStrategy.OnPush }) class EmbeddedViewParent { - @ViewChild(OnPushComp) comp !: OnPushComp; + @ViewChild(OnPushComp) comp!: OnPushComp; value = 'one'; showing = true; } @@ -1112,13 +1128,21 @@ describe('change detection', () => { contentCheckCount = 0; viewCheckCount = 0; - ngDoCheck() { this.doCheckCount++; } + ngDoCheck() { + this.doCheckCount++; + } - ngAfterContentChecked() { this.contentCheckCount++; } + ngAfterContentChecked() { + this.contentCheckCount++; + } - ngAfterViewChecked() { this.viewCheckCount++; } + ngAfterViewChecked() { + this.viewCheckCount++; + } - constructor(public cdr: ChangeDetectorRef) { comp = this; } + constructor(public cdr: ChangeDetectorRef) { + comp = this; + } } @Component({template: '{{ value }} - <no-changes-comp></no-changes-comp>'}) @@ -1131,7 +1155,9 @@ describe('change detection', () => { // Custom error handler that just rethrows all the errors from the // view, rather than logging them out. Used to keep our logs clean. class RethrowErrorHandler extends ErrorHandler { - handleError(error: any) { throw error; } + handleError(error: any) { + throw error; + } } it('should throw if bindings in current view have changed', () => { @@ -1141,7 +1167,9 @@ describe('change detection', () => { }); const fixture = TestBed.createComponent(NoChangesComp); - expect(() => { fixture.componentInstance.cdr.checkNoChanges(); }) + expect(() => { + fixture.componentInstance.cdr.checkNoChanges(); + }) .toThrowError( /ExpressionChangedAfterItHasBeenCheckedError: .+ Previous value: '.*undefined'. Current value: '.*1'/gi); }); @@ -1196,9 +1224,7 @@ describe('change detection', () => { expect(comp.contentCheckCount).toEqual(1); expect(comp.viewCheckCount).toEqual(1); }); - }); - }); describe('transplanted views', () => { @@ -1216,13 +1242,20 @@ describe('change detection', () => { </div> ` }) - class InsertComp implements DoCheck, - AfterViewChecked { - get template(): TemplateRef<any> { return declareComp.myTmpl; } + class InsertComp implements DoCheck, AfterViewChecked { + get template(): TemplateRef<any> { + return declareComp.myTmpl; + } greeting: string = 'Hello'; - constructor(public changeDetectorRef: ChangeDetectorRef) { insertComp = this; } - ngDoCheck(): void { logValue = 'Insert'; } - ngAfterViewChecked(): void { logValue = null; } + constructor(public changeDetectorRef: ChangeDetectorRef) { + insertComp = this; + } + ngDoCheck(): void { + logValue = 'Insert'; + } + ngAfterViewChecked(): void { + logValue = null; + } } @Component({ @@ -1234,20 +1267,24 @@ describe('change detection', () => { </ng-template> ` }) - class DeclareComp implements DoCheck, - AfterViewChecked { - @ViewChild('myTmpl') - myTmpl !: TemplateRef<any>; + class DeclareComp implements DoCheck, AfterViewChecked { + @ViewChild('myTmpl') myTmpl!: TemplateRef<any>; name: string = 'world'; - constructor() { declareComp = this; } - ngDoCheck(): void { logValue = 'Declare'; } + constructor() { + declareComp = this; + } + ngDoCheck(): void { + logValue = 'Declare'; + } logName() { // This will log when the embedded view gets CD. The `logValue` will show if the CD was from // `Insert` or from `Declare` component. - log.push(logValue !); + log.push(logValue!); return this.name; } - ngAfterViewChecked(): void { logValue = null; } + ngAfterViewChecked(): void { + logValue = null; + } } @Component({ @@ -1259,15 +1296,17 @@ describe('change detection', () => { class AppComp { showDeclare: boolean = true; showInsert: boolean = true; - constructor() { appComp = this; } + constructor() { + appComp = this; + } } - let log !: string[]; - let logValue !: string | null; - let fixture !: ComponentFixture<AppComp>; - let appComp !: AppComp; - let insertComp !: InsertComp; - let declareComp !: DeclareComp; + let log!: string[]; + let logValue!: string|null; + let fixture!: ComponentFixture<AppComp>; + let appComp!: AppComp; + let insertComp!: InsertComp; + let declareComp!: DeclareComp; beforeEach(() => { TestBed.configureTestingModule({ @@ -1376,16 +1415,16 @@ describe('change detection', () => { class OnPushComp { text = 'initial'; - constructor(private _cdRef: ChangeDetectorRef){} + constructor(private _cdRef: ChangeDetectorRef) {} - [hookName]() { + [hookName]() { this._cdRef.markForCheck(); } } @Component({template: `<on-push-comp></on-push-comp>`}) class TestApp { - @ViewChild(OnPushComp) onPushComp !: OnPushComp; + @ViewChild(OnPushComp) onPushComp!: OnPushComp; } TestBed.configureTestingModule( @@ -1419,16 +1458,16 @@ describe('change detection', () => { class OnPushComp { text = 'initial'; - constructor(private _cdRef: ChangeDetectorRef){} + constructor(private _cdRef: ChangeDetectorRef) {} - [hookName]() { + [hookName]() { this._cdRef.markForCheck(); } } @Component({template: `<on-push-comp></on-push-comp>`}) class TestApp { - @ViewChild(OnPushComp) onPushComp !: OnPushComp; + @ViewChild(OnPushComp) onPushComp!: OnPushComp; } TestBed.configureTestingModule( @@ -1478,7 +1517,9 @@ describe('change detection', () => { return fixture; } - function initWithTemplate(template: string) { return initComponent({template}); } + function initWithTemplate(template: string) { + return initComponent({template}); + } function initWithHostBindings(bindings: {[key: string]: string}) { return initComponent({host: bindings}); } @@ -1612,6 +1653,6 @@ describe('change detection', () => { }); }); -function trim(text: string | null): string { +function trim(text: string|null): string { return text ? text.replace(/[\s\n]+/gm, ' ').trim() : ''; } diff --git a/packages/core/test/acceptance/common_integration_spec.ts b/packages/core/test/acceptance/common_integration_spec.ts index 14615fb8f6100..307d8da888e32 100644 --- a/packages/core/test/acceptance/common_integration_spec.ts +++ b/packages/core/test/acceptance/common_integration_spec.ts @@ -11,7 +11,6 @@ import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; describe('@angular/common integration', () => { - describe('NgForOf', () => { @Directive({selector: '[dir]'}) class MyDirective { @@ -244,7 +243,9 @@ describe('@angular/common integration', () => { name = 'app'; events: string[] = []; - onClick(value: string, name: string) { this.events.push(value, name); } + onClick(value: string, name: string) { + this.events.push(value, name); + } } TestBed.configureTestingModule({declarations: [MultiLevelWithListenerComponent]}); @@ -485,7 +486,6 @@ describe('@angular/common integration', () => { }); describe('NgTemplateOutlet', () => { - it('should create and remove embedded views', () => { @Component({ selector: 'app-multi', diff --git a/packages/core/test/acceptance/component_spec.ts b/packages/core/test/acceptance/component_spec.ts index 352cb57ce841a..dfb34d294a4a6 100644 --- a/packages/core/test/acceptance/component_spec.ts +++ b/packages/core/test/acceptance/component_spec.ts @@ -28,7 +28,9 @@ describe('component', () => { providers: [{provide: testToken, useExisting: ParentWithOnDestroy}] }) class ParentWithOnDestroy { - ngOnDestroy() { destroyCalls++; } + ngOnDestroy() { + destroyCalls++; + } } @Component({selector: 'child', template: ''}) @@ -75,7 +77,7 @@ describe('component', () => { entryComponents: [OtherComponent] }) class TestComponent { - @ViewChild('vc', {read: ViewContainerRef, static: true}) vcref !: ViewContainerRef; + @ViewChild('vc', {read: ViewContainerRef, static: true}) vcref!: ViewContainerRef; constructor(private _cfr: ComponentFactoryResolver) {} @@ -152,7 +154,8 @@ describe('component', () => { expect(match).toBeDefined(); expect(match.length).toEqual(2); expect(html).toMatch( - `<leaf ${match[0].replace('_nghost', '_ngcontent')}="" ${match[1]}=""><span ${match[1].replace('_nghost', '_ngcontent')}="">bar</span></leaf></div>`); + `<leaf ${match[0].replace('_nghost', '_ngcontent')}="" ${match[1]}=""><span ${ + match[1].replace('_nghost', '_ngcontent')}="">bar</span></leaf></div>`); }); }); @@ -162,7 +165,9 @@ describe('component', () => { @Component({selector: 'comp-with-destroy', template: ``}) class ComponentWithOnDestroy implements OnDestroy { - ngOnDestroy() { wasOnDestroyCalled = true; } + ngOnDestroy() { + wasOnDestroyCalled = true; + } } // This test asserts that the view tree is set up correctly based on the knowledge that this @@ -244,7 +249,7 @@ describe('component', () => { encapsulation: ViewEncapsulation.Emulated, }) class Parent { - @ViewChild(Child) childInstance !: Child; + @ViewChild(Child) childInstance!: Child; constructor(public renderer: Renderer2) {} } @@ -266,7 +271,9 @@ describe('component', () => { }) class CompA { @Input() a: string = ''; - ngDoCheck() { log.push('CompA:ngDoCheck'); } + ngDoCheck() { + log.push('CompA:ngDoCheck'); + } } @Component({ @@ -275,7 +282,9 @@ describe('component', () => { }) class CompB { @Input() b: string = ''; - ngDoCheck() { log.push('CompB:ngDoCheck'); } + ngDoCheck() { + log.push('CompB:ngDoCheck'); + } } @Component({template: `<span></span>`}) @@ -435,14 +444,14 @@ describe('component', () => { fixture.detectChanges(); // Create an instance of DynamicComponent and provide host element *reference* - let targetEl = document.getElementById('dynamic-comp-root-a') !; + let targetEl = document.getElementById('dynamic-comp-root-a')!; fixture.componentInstance.createDynamicComponent(targetEl); fixture.detectChanges(); expect(targetEl.innerHTML).not.toContain('Existing content in slot A'); expect(targetEl.innerHTML).toContain('DynamicComponent Content'); // Create an instance of DynamicComponent and provide host element *selector* - targetEl = document.getElementById('dynamic-comp-root-b') !; + targetEl = document.getElementById('dynamic-comp-root-b')!; fixture.componentInstance.createDynamicComponent('#dynamic-comp-root-b'); fixture.detectChanges(); expect(targetEl.innerHTML).not.toContain('Existing content in slot B'); @@ -453,7 +462,8 @@ describe('component', () => { () => runTestWithRenderer([{provide: RendererFactory2, useClass: DomRendererFactory2}])); onlyInIvy('Renderer3 is supported only in Ivy') - .it('with Renderer3', () => runTestWithRenderer( - [{provide: RendererFactory2, useValue: domRendererFactory3}])); + .it('with Renderer3', + () => + runTestWithRenderer([{provide: RendererFactory2, useValue: domRendererFactory3}])); }); }); diff --git a/packages/core/test/acceptance/content_spec.ts b/packages/core/test/acceptance/content_spec.ts index 9aef0532a49d7..d9116753c6386 100644 --- a/packages/core/test/acceptance/content_spec.ts +++ b/packages/core/test/acceptance/content_spec.ts @@ -14,7 +14,6 @@ import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; describe('projection', () => { - function getElementHtml(element: HTMLElement) { return element.innerHTML.replace(/<!--(\W|\w)*?-->/g, '') .replace(/\sng-reflect-\S*="[^"]*"/g, ''); @@ -328,11 +327,13 @@ describe('projection', () => { @Directive({selector: '[trigger]'}) class Trigger { - @Input() trigger !: Comp; + @Input() trigger!: Comp; constructor(public vcr: ViewContainerRef) {} - open() { this.vcr.createEmbeddedView(this.trigger.template); } + open() { + this.vcr.createEmbeddedView(this.trigger.template); + } } @Component({ @@ -861,7 +862,6 @@ describe('projection', () => { expect(getElementHtml(fixture.nativeElement)) .toEqual('<child><span title="Some title">Has title</span></child>'); - }); it('should match selectors against projected containers', () => { @@ -934,7 +934,9 @@ describe('projection', () => { it('should project content if the change detector has been detached', () => { @Component({selector: 'my-comp', template: '<ng-content></ng-content>'}) class MyComp { - constructor(changeDetectorRef: ChangeDetectorRef) { changeDetectorRef.detach(); } + constructor(changeDetectorRef: ChangeDetectorRef) { + changeDetectorRef.detach(); + } } @Component({ @@ -1108,7 +1110,9 @@ describe('projection', () => { @Directive({selector: 'div'}) class DivDirective { - constructor() { divDirectives++; } + constructor() { + divDirectives++; + } } @Component({ @@ -1135,7 +1139,9 @@ describe('projection', () => { @Directive({selector: '[x]'}) class XDirective { - constructor() { xDirectives++; } + constructor() { + xDirectives++; + } } @Component({ @@ -1162,7 +1168,9 @@ describe('projection', () => { @Directive({selector: '.x'}) class XDirective { - constructor() { xDirectives++; } + constructor() { + xDirectives++; + } } @Component({ @@ -1199,7 +1207,9 @@ describe('projection', () => { {id: 2, name: 'two'}, {id: 3, name: 'three'}, ]; - getItemId(item: {id: number}) { return item.id; } + getItemId(item: {id: number}) { + return item.id; + } } TestBed.configureTestingModule({declarations: [SelectedNgContentComp, SelectorMainComp]}); @@ -1265,7 +1275,9 @@ describe('projection', () => { @Directive({selector: '[x]'}) class XDirective { - constructor() { xDirectives++; } + constructor() { + xDirectives++; + } } @Component({ @@ -1293,7 +1305,9 @@ describe('projection', () => { @Directive({selector: '.x'}) class XDirective { - constructor() { xDirectives++; } + constructor() { + xDirectives++; + } } @Component({ diff --git a/packages/core/test/acceptance/copy_definition_feature_spec.ts b/packages/core/test/acceptance/copy_definition_feature_spec.ts index ea4c220c70179..c200ad6f41e4f 100644 --- a/packages/core/test/acceptance/copy_definition_feature_spec.ts +++ b/packages/core/test/acceptance/copy_definition_feature_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, NgModule, ɵɵCopyDefinitionFeature as CopyDefinitionFeature, ɵɵInheritDefinitionFeature as InheritDefinitionFeature, ɵɵdefineComponent as defineComponent} from '@angular/core'; +import {Component, NgModule, ɵɵCopyDefinitionFeature as CopyDefinitionFeature, ɵɵdefineComponent as defineComponent, ɵɵInheritDefinitionFeature as InheritDefinitionFeature} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {onlyInIvy} from '@angular/private/testing'; @@ -14,20 +14,22 @@ describe('Ivy CopyDefinitionFeature', () => { onlyInIvy('this feature is not required in View Engine') .it('should copy the template function of a component definition from parent to child', () => { - // It would be nice if the base component could be JIT compiled. However, this creates // a getter for ɵcmp which precludes adding a static definition of that field for the // child class. // TODO(alxhub): see if there's a cleaner way to do this. class BaseComponent { - name !: string; + name!: string; static ɵcmp = defineComponent({ type: BaseComponent, selectors: [['some-cmp']], decls: 0, vars: 0, inputs: {name: 'name'}, - template: function BaseComponent_Template(rf, ctx) { ctx.rendered = true; }, + template: + function BaseComponent_Template(rf, ctx) { + ctx.rendered = true; + }, encapsulation: 2 }); static ɵfac = function BaseComponent_Factory(t: any) { diff --git a/packages/core/test/acceptance/debug_spec.ts b/packages/core/test/acceptance/debug_spec.ts index d4bfa91531d79..5cdb0ea011e47 100644 --- a/packages/core/test/acceptance/debug_spec.ts +++ b/packages/core/test/acceptance/debug_spec.ts @@ -14,9 +14,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; import {onlyInIvy} from '@angular/private/testing'; describe('Debug Representation', () => { - onlyInIvy('Ivy specific').it('should generate a human readable version', () => { - @Component({selector: 'my-comp', template: '<div id="123">Hello World</div>'}) class MyComponent { } @@ -25,11 +23,11 @@ describe('Debug Representation', () => { const fixture = TestBed.createComponent(MyComponent); fixture.detectChanges(); - const hostView = toDebug(getLContext(fixture.componentInstance) !.lView); + const hostView = toDebug(getLContext(fixture.componentInstance)!.lView); expect(hostView.host).toEqual(null); const myCompView = hostView.childViews[0] as LViewDebug; expect(myCompView.host).toContain('<div id="123">Hello World</div>'); - expect(myCompView.nodes ![0].html).toEqual('<div id="123">'); - expect(myCompView.nodes ![0].nodes ![0].html).toEqual('Hello World'); + expect(myCompView.nodes![0].html).toEqual('<div id="123">'); + expect(myCompView.nodes![0].nodes![0].html).toEqual('Hello World'); }); }); diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 23d319de03617..05f04e6cab408 100644 --- a/packages/core/test/acceptance/di_spec.ts +++ b/packages/core/test/acceptance/di_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Host, HostBinding, INJECTOR, Inject, Injectable, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, forwardRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; +import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; import {ɵINJECTOR_SCOPE} from '@angular/core/src/core'; import {ViewRef} from '@angular/core/src/render3/view_ref'; import {TestBed} from '@angular/core/testing'; @@ -60,14 +60,15 @@ describe('di', () => { }); describe('directive injection', () => { - let log: string[] = []; @Directive({selector: '[dirB]', exportAs: 'dirB'}) class DirectiveB { @Input() value = 'DirB'; - constructor() { log.push(this.value); } + constructor() { + log.push(this.value); + } } beforeEach(() => log = []); @@ -82,7 +83,9 @@ describe('di', () => { class DirectiveC { value: string; - constructor(dirA: DirectiveA, dirB: DirectiveB) { this.value = dirA.value + dirB.value; } + constructor(dirA: DirectiveA, dirB: DirectiveB) { + this.value = dirA.value + dirB.value; + } } @Component({ @@ -108,7 +111,9 @@ describe('di', () => { class DirectiveA { value = 'dirA'; - constructor(dirB: DirectiveB) { log.push(`DirA (dep: ${dirB.value})`); } + constructor(dirB: DirectiveB) { + log.push(`DirA (dep: ${dirB.value})`); + } } @Component({template: '<div dirA dirB></div>'}) @@ -127,7 +132,9 @@ describe('di', () => { class DirectiveA { value = 'dirA'; - constructor(dirB: DirectiveB) { log.push(`DirA (dep: ${dirB.value})`); } + constructor(dirB: DirectiveB) { + log.push(`DirA (dep: ${dirB.value})`); + } } // - dirB is know to the node injectors @@ -150,7 +157,9 @@ describe('di', () => { it('should instantiate injected directives before components', () => { @Component({selector: 'my-comp', template: ''}) class MyComp { - constructor(dirB: DirectiveB) { log.push(`Comp (dep: ${dirB.value})`); } + constructor(dirB: DirectiveB) { + log.push(`Comp (dep: ${dirB.value})`); + } } @Component({template: '<my-comp dirB></my-comp>'}) @@ -167,7 +176,9 @@ describe('di', () => { it('should inject directives in the correct order in a for loop', () => { @Directive({selector: '[dirA]'}) class DirectiveA { - constructor(dir: DirectiveB) { log.push(`DirA (dep: ${dir.value})`); } + constructor(dir: DirectiveB) { + log.push(`DirA (dep: ${dir.value})`); + } } @Component({template: '<div dirA dirB *ngFor="let i of array"></div>'}) @@ -188,14 +199,18 @@ describe('di', () => { class DirectiveA { value = 'DirA'; - constructor() { log.push(this.value); } + constructor() { + log.push(this.value); + } } @Directive({selector: '[dirC]'}) class DirectiveC { value = 'DirC'; - constructor() { log.push(this.value); } + constructor() { + log.push(this.value); + } } @Directive({selector: '[dirB]'}) @@ -221,26 +236,34 @@ describe('di', () => { class DirectiveC { value = 'DirC'; - constructor(dirB: DirectiveB) { log.push(`DirC (dep: ${dirB.value})`); } + constructor(dirB: DirectiveB) { + log.push(`DirC (dep: ${dirB.value})`); + } } @Directive({selector: '[dirA]'}) class DirectiveA { value = 'DirA'; - constructor(dirC: DirectiveC) { log.push(`DirA (dep: ${dirC.value})`); } + constructor(dirC: DirectiveC) { + log.push(`DirA (dep: ${dirC.value})`); + } } @Directive({selector: '[dirD]'}) class DirectiveD { value = 'DirD'; - constructor(dirA: DirectiveA) { log.push(`DirD (dep: ${dirA.value})`); } + constructor(dirA: DirectiveA) { + log.push(`DirD (dep: ${dirA.value})`); + } } @Component({selector: 'my-comp', template: ''}) class MyComp { - constructor(dirD: DirectiveD) { log.push(`Comp (dep: ${dirD.value})`); } + constructor(dirD: DirectiveD) { + log.push(`Comp (dep: ${dirD.value})`); + } } @Component({template: '<my-comp dirA dirB dirC dirD></my-comp>'}) @@ -291,7 +314,9 @@ describe('di', () => { @Directive({selector: '[dirA]'}) class DirectiveA { - constructor(dirB: DirectiveB) { log.push(`DirA (dep: DirB - ${dirB.count})`); } + constructor(dirB: DirectiveB) { + log.push(`DirA (dep: DirB - ${dirB.count})`); + } } @Component({selector: 'my-comp', template: '<div dirA dirB></div>'}) @@ -310,7 +335,6 @@ describe('di', () => { }); describe('dependencies in parent views', () => { - @Directive({selector: '[dirA]', exportAs: 'dirA'}) class DirectiveA { injector: Injector; @@ -385,11 +409,13 @@ describe('di', () => { it('should find dependencies in declaration tree of ng-template (not insertion tree)', () => { @Directive({selector: '[structuralDir]'}) class StructuralDirective { - @Input() tmp !: TemplateRef<any>; + @Input() tmp!: TemplateRef<any>; constructor(public vcr: ViewContainerRef) {} - create() { this.vcr.createEmbeddedView(this.tmp); } + create() { + this.vcr.createEmbeddedView(this.tmp); + } } @Component({ @@ -405,7 +431,7 @@ describe('di', () => { </div>` }) class MyComp { - @ViewChild(StructuralDirective) structuralDir !: StructuralDirective; + @ViewChild(StructuralDirective) structuralDir!: StructuralDirective; } TestBed.configureTestingModule( @@ -449,8 +475,8 @@ describe('di', () => { </div>` }) class MyApp { - @ViewChild(HostBindingDirective) hostBindingDir !: HostBindingDirective; - @ViewChild(DirectiveA) dirA !: DirectiveA; + @ViewChild(HostBindingDirective) hostBindingDir!: HostBindingDirective; + @ViewChild(DirectiveA) dirA!: DirectiveA; } TestBed.configureTestingModule( @@ -501,15 +527,19 @@ describe('di', () => { }) class MyApp { @ViewChild('childOrigin', {read: ViewContainerRef, static: true}) - childOrigin !: ViewContainerRef; + childOrigin!: ViewContainerRef; @ViewChild('childOriginWithDirB', {read: ViewContainerRef, static: true}) - childOriginWithDirB !: ViewContainerRef; + childOriginWithDirB!: ViewContainerRef; childFactory = this.resolver.resolveComponentFactory(Child); constructor(readonly resolver: ComponentFactoryResolver, readonly injector: Injector) {} - addChild() { return this.childOrigin.createComponent(this.childFactory); } - addChildWithDirB() { return this.childOriginWithDirB.createComponent(this.childFactory); } + addChild() { + return this.childOrigin.createComponent(this.childFactory); + } + addChildWithDirB() { + return this.childOriginWithDirB.createComponent(this.childFactory); + } } const fixture = @@ -604,24 +634,21 @@ describe('di', () => { }); describe('flags', () => { - @Directive({selector: '[dirB]'}) class DirectiveB { @Input('dirB') value = ''; } describe('Optional', () => { - @Directive({selector: '[dirA]'}) class DirectiveA { constructor(@Optional() public dirB: DirectiveB) {} } it('should not throw if dependency is @Optional (module injector)', () => { - @Component({template: '<div dirA></div>'}) class MyComp { - @ViewChild(DirectiveA) dirA !: DirectiveA; + @ViewChild(DirectiveA) dirA!: DirectiveA; } TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]}); @@ -633,7 +660,6 @@ describe('di', () => { }); it('should return null if @Optional dependency has @Self flag', () => { - @Directive({selector: '[dirC]'}) class DirectiveC { constructor(@Optional() @Self() public dirB: DirectiveB) {} @@ -641,7 +667,7 @@ describe('di', () => { @Component({template: '<div dirC></div>'}) class MyComp { - @ViewChild(DirectiveC) dirC !: DirectiveC; + @ViewChild(DirectiveC) dirC!: DirectiveC; } TestBed.configureTestingModule({declarations: [DirectiveC, MyComp]}); @@ -653,7 +679,6 @@ describe('di', () => { }); it('should not throw if dependency is @Optional but defined elsewhere', () => { - @Directive({selector: '[dirC]'}) class DirectiveC { constructor(@Optional() public dirB: DirectiveB) {} @@ -661,7 +686,7 @@ describe('di', () => { @Component({template: '<div dirB></div><div dirC></div>'}) class MyComp { - @ViewChild(DirectiveC) dirC !: DirectiveC; + @ViewChild(DirectiveC) dirC!: DirectiveC; } TestBed.configureTestingModule({declarations: [DirectiveB, DirectiveC, MyComp]}); @@ -674,7 +699,6 @@ describe('di', () => { }); it('should skip the current node with @SkipSelf', () => { - @Directive({selector: '[dirA]'}) class DirectiveA { constructor(@SkipSelf() public dirB: DirectiveB) {} @@ -682,12 +706,12 @@ describe('di', () => { @Component({selector: 'my-comp', template: '<div dirA dirB="self"></div>'}) class MyComp { - @ViewChild(DirectiveA) dirA !: DirectiveA; + @ViewChild(DirectiveA) dirA!: DirectiveA; } @Component({template: '<my-comp dirB="parent"></my-comp>'}) class MyApp { - @ViewChild(MyComp) myComp !: MyComp; + @ViewChild(MyComp) myComp!: MyComp; } TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]}); @@ -700,7 +724,6 @@ describe('di', () => { onlyInIvy('Ivy has different error message when dependency is not found') .it('should check only the current node with @Self', () => { - @Directive({selector: '[dirA]'}) class DirectiveA { constructor(@Self() public dirB: DirectiveB) {} @@ -732,12 +755,12 @@ describe('di', () => { viewProviders: [{provide: String, useValue: 'Foo'}] }) class MyComp { - @ViewChild(DirectiveString) dirString !: DirectiveString; + @ViewChild(DirectiveString) dirString!: DirectiveString; } @Component({template: '<my-comp></my-comp>'}) class MyApp { - @ViewChild(MyComp) myComp !: MyComp; + @ViewChild(MyComp) myComp!: MyComp; } TestBed.configureTestingModule({declarations: [DirectiveString, MyComp, MyApp]}); @@ -756,12 +779,12 @@ describe('di', () => { @Component({selector: 'my-comp', template: '<div dirComp></div>'}) class MyComp { - @ViewChild(DirectiveComp) dirComp !: DirectiveComp; + @ViewChild(DirectiveComp) dirComp!: DirectiveComp; } @Component({template: '<my-comp></my-comp>'}) class MyApp { - @ViewChild(MyComp) myComp !: MyComp; + @ViewChild(MyComp) myComp!: MyComp; } TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]}); @@ -820,7 +843,7 @@ describe('di', () => { @Component({template: '<my-comp dirB></my-comp>'}) class MyApp { - @ViewChild(MyComp) myComp !: MyComp; + @ViewChild(MyComp) myComp!: MyComp; } TestBed.configureTestingModule( @@ -837,8 +860,8 @@ describe('di', () => { @Component({template: '<div dirB><div *ngIf="showing" dirA></div></div>'}) class MyApp { showing = false; - @ViewChild(DirectiveA) dirA !: DirectiveA; - @ViewChild(DirectiveB) dirB !: DirectiveB; + @ViewChild(DirectiveA) dirA!: DirectiveA; + @ViewChild(DirectiveB) dirB!: DirectiveB; } TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]}); @@ -885,7 +908,9 @@ describe('di', () => { providers: [{provide: ControlContainer, useExisting: GroupDirective}] }) class GroupDirective { - constructor() { controlContainers.push(this); } + constructor() { + controlContainers.push(this); + } } @Directive({selector: '[control]'}) @@ -919,7 +944,7 @@ describe('di', () => { const fixture = TestBed.createComponent(MyApp); expect(fixture.nativeElement.innerHTML) .toBe('<div group=""><my-comp><input control=""></my-comp></div>'); - expect(controlContainers).toEqual([injectedControlContainer !]); + expect(controlContainers).toEqual([injectedControlContainer!]); }); }); }); @@ -958,7 +983,6 @@ describe('di', () => { }); describe('service injection', () => { - it('should create instance even when no injector present', () => { @Injectable({providedIn: 'root'}) class MyService { @@ -1063,7 +1087,6 @@ describe('di', () => { expect(fixture.componentInstance.myService instanceof MyService).toBe(true); expect(fixture.componentInstance.myOtherService instanceof MyOtherService).toBe(true); }); - }); describe('service injection with useClass', () => { @@ -1075,7 +1098,9 @@ describe('di', () => { @Injectable({providedIn: 'root'}) class BarService { constructor(public dep: BarServiceDep) {} - getMessage() { return 'bar'; } + getMessage() { + return 'bar'; + } } @Injectable({providedIn: 'root'}) @@ -1086,7 +1111,9 @@ describe('di', () => { @Injectable({providedIn: 'root', useClass: BarService}) class FooService { constructor(public dep: FooServiceDep) {} - getMessage() { return 'foo'; } + getMessage() { + return 'foo'; + } } it('should use @Injectable useClass config when token is not provided', () => { @@ -1094,18 +1121,20 @@ describe('di', () => { @Component({template: ''}) class App { - constructor(service: FooService) { provider = service; } + constructor(service: FooService) { + provider = service; + } } TestBed.configureTestingModule({declarations: [App]}); const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(provider !.getMessage()).toBe('bar'); + expect(provider!.getMessage()).toBe('bar'); // ViewEngine incorrectly uses the original class DI config, instead of the one from useClass. if (ivyEnabled) { - expect(provider !.dep.name).toBe('BarServiceDep'); + expect(provider!.dep.name).toBe('BarServiceDep'); } }); @@ -1115,7 +1144,9 @@ describe('di', () => { @Component({template: ''}) class App { - constructor(service: FooService) { provider = service; } + constructor(service: FooService) { + provider = service; + } } TestBed.configureTestingModule( @@ -1123,7 +1154,7 @@ describe('di', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(provider !.getMessage()).toBe('foo'); + expect(provider!.getMessage()).toBe('foo'); }); @@ -1144,13 +1175,13 @@ describe('di', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(directProvider !.getMessage()).toBe('bar'); - expect(overriddenProvider !.getMessage()).toBe('foo'); + expect(directProvider!.getMessage()).toBe('bar'); + expect(overriddenProvider!.getMessage()).toBe('foo'); // ViewEngine incorrectly uses the original class DI config, instead of the one from useClass. if (ivyEnabled) { - expect(directProvider !.dep.name).toBe('BarServiceDep'); - expect(overriddenProvider !.dep.name).toBe('FooServiceDep'); + expect(directProvider!.dep.name).toBe('BarServiceDep'); + expect(overriddenProvider!.dep.name).toBe('FooServiceDep'); } }); @@ -1160,20 +1191,21 @@ describe('di', () => { @Component({template: ''}) class App { - constructor(service: FooService) { provider = service; } + constructor(service: FooService) { + provider = service; + } } TestBed.configureTestingModule({declarations: [App], providers: [FooService]}); const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(provider !.getMessage()).toBe('foo'); - expect(provider !.dep.name).toBe('FooServiceDep'); + expect(provider!.getMessage()).toBe('foo'); + expect(provider!.dep.name).toBe('FooServiceDep'); }); }); describe('inject', () => { - it('should inject from parent view', () => { @Directive({selector: '[parentDir]'}) class ParentDirective { @@ -1182,7 +1214,9 @@ describe('di', () => { @Directive({selector: '[childDir]', exportAs: 'childDir'}) class ChildDirective { value: string; - constructor(public parent: ParentDirective) { this.value = parent.constructor.name; } + constructor(public parent: ParentDirective) { + this.value = parent.constructor.name; + } } @Directive({selector: '[child2Dir]', exportAs: 'child2Dir'}) @@ -1214,9 +1248,7 @@ describe('di', () => { }); describe('Special tokens', () => { - describe('Injector', () => { - it('should inject the injector', () => { @Directive({selector: '[injectorDir]'}) class InjectorDir { @@ -1230,8 +1262,8 @@ describe('di', () => { @Component({template: '<div injectorDir otherInjectorDir></div>'}) class MyComp { - @ViewChild(InjectorDir) injectorDir !: InjectorDir; - @ViewChild(OtherInjectorDir) otherInjectorDir !: OtherInjectorDir; + @ViewChild(InjectorDir) injectorDir!: InjectorDir; + @ViewChild(OtherInjectorDir) otherInjectorDir!: OtherInjectorDir; } TestBed.configureTestingModule({declarations: [InjectorDir, OtherInjectorDir, MyComp]}); @@ -1256,7 +1288,7 @@ describe('di', () => { @Component({template: '<div injectorDir></div>'}) class MyComp { - @ViewChild(InjectorDir) injectorDir !: InjectorDir; + @ViewChild(InjectorDir) injectorDir!: InjectorDir; } TestBed.configureTestingModule({declarations: [InjectorDir, MyComp]}); @@ -1273,7 +1305,6 @@ describe('di', () => { }); describe('ElementRef', () => { - it('should create directive with ElementRef dependencies', () => { @Directive({selector: '[dir]'}) class MyDir { @@ -1293,8 +1324,8 @@ describe('di', () => { @Component({template: '<div dir otherDir></div>'}) class MyComp { - @ViewChild(MyDir) directive !: MyDir; - @ViewChild(MyOtherDir) otherDirective !: MyOtherDir; + @ViewChild(MyDir) directive!: MyDir; + @ViewChild(MyOtherDir) otherDirective!: MyOtherDir; } TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]}); @@ -1325,7 +1356,7 @@ describe('di', () => { @Component({template: '<ng-template dir></ng-template>'}) class MyComp { - @ViewChild(MyDir) directive !: MyDir; + @ViewChild(MyDir) directive!: MyDir; } TestBed.configureTestingModule({declarations: [MyDir, MyComp]}); @@ -1346,7 +1377,9 @@ describe('di', () => { constructor(protected zone: NgZone) { this.subject = new BehaviorSubject<any>(1); // trigger change detection - zone.run(() => { this.subject.next(2); }); + zone.run(() => { + this.subject.next(2); + }); } } @@ -1360,7 +1393,7 @@ describe('di', () => { template: `<div id="test-id" dir></div>`, }) class ChildComp { - @ViewChild(DirectiveA) directive !: DirectiveA; + @ViewChild(DirectiveA) directive!: DirectiveA; } @Component({ @@ -1368,7 +1401,7 @@ describe('di', () => { template: '...', }) class RootComp { - public childCompRef !: ComponentRef<ChildComp>; + public childCompRef!: ComponentRef<ChildComp>; constructor( public factoryResolver: ComponentFactoryResolver, public vcr: ViewContainerRef) {} @@ -1404,7 +1437,6 @@ describe('di', () => { }); describe('TemplateRef', () => { - @Directive({selector: '[dir]', exportAs: 'dir'}) class MyDir { value: string; @@ -1426,8 +1458,8 @@ describe('di', () => { template: '<ng-template dir otherDir #dir="dir" #otherDir="otherDir"></ng-template>' }) class MyComp { - @ViewChild(MyDir) directive !: MyDir; - @ViewChild(MyOtherDir) otherDirective !: MyOtherDir; + @ViewChild(MyDir) directive!: MyDir; + @ViewChild(MyOtherDir) otherDirective!: MyOtherDir; } TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]}); @@ -1470,7 +1502,7 @@ describe('di', () => { } @Component({template: '<div optionalDir></div>'}) class MyComp { - @ViewChild(OptionalDir) directive !: OptionalDir; + @ViewChild(OptionalDir) directive!: OptionalDir; } TestBed.configureTestingModule({declarations: [OptionalDir, MyComp]}); @@ -1499,8 +1531,8 @@ describe('di', () => { } @Component({template: '<div dir otherDir #dir="dir" #otherDir="otherDir"></div>'}) class MyComp { - @ViewChild(MyDir) directive !: MyDir; - @ViewChild(MyOtherDir) otherDirective !: MyOtherDir; + @ViewChild(MyDir) directive!: MyDir; + @ViewChild(MyOtherDir) otherDirective!: MyOtherDir; } TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]}); @@ -1524,12 +1556,13 @@ describe('di', () => { template: `<ng-template #tmpl>Test</ng-template>`, }) class Root { - @ViewChild(TemplateRef, {static: true}) - tmpl !: TemplateRef<any>; + @ViewChild(TemplateRef, {static: true}) tmpl!: TemplateRef<any>; constructor(public vcr: ViewContainerRef, public vcr2: ViewContainerRef) {} - ngOnInit(): void { this.vcr.createEmbeddedView(this.tmpl); } + ngOnInit(): void { + this.vcr.createEmbeddedView(this.tmpl); + } } TestBed.configureTestingModule({ @@ -1551,11 +1584,12 @@ describe('di', () => { }); describe('ChangeDetectorRef', () => { - @Directive({selector: '[dir]', exportAs: 'dir'}) class MyDir { value: string; - constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; } + constructor(public cdr: ChangeDetectorRef) { + this.value = (cdr.constructor as any).name; + } } @Directive({selector: '[otherDir]', exportAs: 'otherDir'}) class MyOtherDir { @@ -1571,9 +1605,13 @@ describe('di', () => { @Pipe({name: 'pipe'}) class MyPipe implements PipeTransform { - constructor(public cdr: ChangeDetectorRef) { pipeInstance = this; } + constructor(public cdr: ChangeDetectorRef) { + pipeInstance = this; + } - transform(value: any): any { return value; } + transform(value: any): any { + return value; + } } @Component({ @@ -1589,29 +1627,29 @@ describe('di', () => { TestBed.configureTestingModule({declarations: [MyApp, MyPipe], imports: [CommonModule]}); const fixture = TestBed.createComponent(MyApp); fixture.detectChanges(); - expect((pipeInstance !.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance); + expect((pipeInstance!.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance); }); it('should inject current component ChangeDetectorRef into directives on the same node as components', () => { @Component({selector: 'my-app', template: '<my-comp dir otherDir #dir="dir"></my-comp>'}) class MyApp { - @ViewChild(MyComp) component !: MyComp; - @ViewChild(MyDir) directive !: MyDir; - @ViewChild(MyOtherDir) otherDirective !: MyOtherDir; + @ViewChild(MyComp) component!: MyComp; + @ViewChild(MyDir) directive!: MyDir; + @ViewChild(MyOtherDir) otherDirective!: MyOtherDir; } TestBed.configureTestingModule({declarations: [MyApp, MyComp, MyDir, MyOtherDir]}); const fixture = TestBed.createComponent(MyApp); fixture.detectChanges(); const app = fixture.componentInstance; const comp = fixture.componentInstance.component; - expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp); + expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp); // ChangeDetectorRef is the token, ViewRef has historically been the constructor expect(app.directive.value).toContain('ViewRef'); // Each ChangeDetectorRef instance should be unique - expect(app.directive !.cdr).not.toBe(comp !.cdr); - expect(app.directive !.cdr).not.toBe(app.otherDirective !.cdr); + expect(app.directive!.cdr).not.toBe(comp!.cdr); + expect(app.directive!.cdr).not.toBe(app.otherDirective!.cdr); }); it('should inject host component ChangeDetectorRef into directives on normal elements', @@ -1619,20 +1657,20 @@ describe('di', () => { @Component({selector: 'my-comp', template: '<div dir otherDir #dir="dir"></div>'}) class MyComp { constructor(public cdr: ChangeDetectorRef) {} - @ViewChild(MyDir) directive !: MyDir; - @ViewChild(MyOtherDir) otherDirective !: MyOtherDir; + @ViewChild(MyDir) directive!: MyDir; + @ViewChild(MyOtherDir) otherDirective!: MyOtherDir; } TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]}); const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); const comp = fixture.componentInstance; - expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp); + expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp); // ChangeDetectorRef is the token, ViewRef has historically been the constructor expect(comp.directive.value).toContain('ViewRef'); // Each ChangeDetectorRef instance should be unique - expect(comp.directive !.cdr).not.toBe(comp.cdr); - expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr); + expect(comp.directive!.cdr).not.toBe(comp.cdr); + expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr); }); it('should inject host component ChangeDetectorRef into directives in a component\'s ContentChildren', @@ -1646,22 +1684,22 @@ describe('di', () => { }) class MyApp { constructor(public cdr: ChangeDetectorRef) {} - @ViewChild(MyComp) component !: MyComp; - @ViewChild(MyDir) directive !: MyDir; - @ViewChild(MyOtherDir) otherDirective !: MyOtherDir; + @ViewChild(MyComp) component!: MyComp; + @ViewChild(MyDir) directive!: MyDir; + @ViewChild(MyOtherDir) otherDirective!: MyOtherDir; } TestBed.configureTestingModule({declarations: [MyApp, MyComp, MyDir, MyOtherDir]}); const fixture = TestBed.createComponent(MyApp); fixture.detectChanges(); const app = fixture.componentInstance; - expect((app !.cdr as ViewRef<MyApp>).context).toBe(app); + expect((app!.cdr as ViewRef<MyApp>).context).toBe(app); const comp = fixture.componentInstance.component; // ChangeDetectorRef is the token, ViewRef has historically been the constructor expect(app.directive.value).toContain('ViewRef'); // Each ChangeDetectorRef instance should be unique - expect(app.directive !.cdr).not.toBe(comp.cdr); - expect(app.directive !.cdr).not.toBe(app.otherDirective !.cdr); + expect(app.directive!.cdr).not.toBe(comp.cdr); + expect(app.directive!.cdr).not.toBe(app.otherDirective!.cdr); }); it('should inject host component ChangeDetectorRef into directives in embedded views', () => { @@ -1674,21 +1712,21 @@ describe('di', () => { class MyComp { showing = true; constructor(public cdr: ChangeDetectorRef) {} - @ViewChild(MyDir) directive !: MyDir; - @ViewChild(MyOtherDir) otherDirective !: MyOtherDir; + @ViewChild(MyDir) directive!: MyDir; + @ViewChild(MyOtherDir) otherDirective!: MyOtherDir; } TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]}); const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); const comp = fixture.componentInstance; - expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp); + expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp); // ChangeDetectorRef is the token, ViewRef has historically been the constructor expect(comp.directive.value).toContain('ViewRef'); // Each ChangeDetectorRef instance should be unique - expect(comp.directive !.cdr).not.toBe(comp.cdr); - expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr); + expect(comp.directive!.cdr).not.toBe(comp.cdr); + expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr); }); it('should inject host component ChangeDetectorRef into directives on containers', () => { @@ -1697,21 +1735,21 @@ describe('di', () => { class MyComp { showing = true; constructor(public cdr: ChangeDetectorRef) {} - @ViewChild(MyDir) directive !: MyDir; - @ViewChild(MyOtherDir) otherDirective !: MyOtherDir; + @ViewChild(MyDir) directive!: MyDir; + @ViewChild(MyOtherDir) otherDirective!: MyOtherDir; } TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]}); const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); const comp = fixture.componentInstance; - expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp); + expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp); // ChangeDetectorRef is the token, ViewRef has historically been the constructor expect(comp.directive.value).toContain('ViewRef'); // Each ChangeDetectorRef instance should be unique - expect(comp.directive !.cdr).not.toBe(comp.cdr); - expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr); + expect(comp.directive!.cdr).not.toBe(comp.cdr); + expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr); }); it('should inject host component ChangeDetectorRef into directives on ng-container', () => { @@ -1719,7 +1757,9 @@ describe('di', () => { @Directive({selector: '[getCDR]'}) class MyDirective { - constructor(public cdr: ChangeDetectorRef) { dirInstance = this; } + constructor(public cdr: ChangeDetectorRef) { + dirInstance = this; + } } @Component({ @@ -1733,7 +1773,7 @@ describe('di', () => { TestBed.configureTestingModule({declarations: [MyApp, MyDirective]}); const fixture = TestBed.createComponent(MyApp); fixture.detectChanges(); - expect((dirInstance !.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance); + expect((dirInstance!.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance); }); }); }); @@ -1747,7 +1787,7 @@ describe('di', () => { @Component({template: '<div injectorDir></div>'}) class MyComp { - @ViewChild(InjectorDir) injectorDirInstance !: InjectorDir; + @ViewChild(InjectorDir) injectorDirInstance!: InjectorDir; } TestBed.configureTestingModule({declarations: [InjectorDir, MyComp]}); @@ -1840,7 +1880,7 @@ describe('di', () => { providers: [{provide: LOCALE_ID, useValue: 'en-GB'}] }) class MyComp { - @ViewChild(MyDir) myDir !: MyDir; + @ViewChild(MyDir) myDir!: MyDir; constructor(@Inject(LOCALE_ID) public localeId: string) {} } @@ -1851,7 +1891,6 @@ describe('di', () => { }); describe('@Attribute', () => { - it('should inject attributes', () => { @Directive({selector: '[dir]'}) class MyDir { @@ -1862,7 +1901,7 @@ describe('di', () => { @Component({template: '<div dir exist="existValue" other="ignore"></div>'}) class MyComp { - @ViewChild(MyDir) directiveInstance !: MyDir; + @ViewChild(MyDir) directiveInstance!: MyDir; } TestBed.configureTestingModule({declarations: [MyDir, MyComp]}); @@ -1886,7 +1925,7 @@ describe('di', () => { @Component( {template: '<ng-template dir="initial" exist="existValue" other="ignore"></ng-template>'}) class MyComp { - @ViewChild(MyDir) directiveInstance !: MyDir; + @ViewChild(MyDir) directiveInstance!: MyDir; } TestBed.configureTestingModule({declarations: [MyDir, MyComp]}); @@ -1911,7 +1950,7 @@ describe('di', () => { template: '<ng-container dir="initial" exist="existValue" other="ignore"></ng-container>' }) class MyComp { - @ViewChild(MyDir) directiveInstance !: MyDir; + @ViewChild(MyDir) directiveInstance!: MyDir; } TestBed.configureTestingModule({declarations: [MyDir, MyComp]}); @@ -1938,7 +1977,7 @@ describe('di', () => { '<div dir style="margin: 1px; color: red;" class="hello there" other-attr="value"></div>' }) class MyComp { - @ViewChild(MyDir) directiveInstance !: MyDir; + @ViewChild(MyDir) directiveInstance!: MyDir; } TestBed.configureTestingModule({declarations: [MyDir, MyComp]}); @@ -1966,7 +2005,7 @@ describe('di', () => { template: '<div dir exist="existValue" svg:exist="testExistValue" other="otherValue"></div>' }) class MyComp { - @ViewChild(MyDir) directiveInstance !: MyDir; + @ViewChild(MyDir) directiveInstance!: MyDir; } TestBed.configureTestingModule({declarations: [MyDir, MyComp]}); @@ -1983,7 +2022,7 @@ describe('di', () => { it('should not inject attributes representing bindings and outputs', () => { @Directive({selector: '[dir]'}) class MyDir { - @Input() binding !: string; + @Input() binding!: string; @Output() output = new EventEmitter(); constructor( @Attribute('exist') public exist: string, @@ -1997,7 +2036,7 @@ describe('di', () => { '<div dir exist="existValue" [binding]="bindingValue" (output)="outputValue" other="otherValue" ignore="ignoreValue"></div>' }) class MyComp { - @ViewChild(MyDir) directiveInstance !: MyDir; + @ViewChild(MyDir) directiveInstance!: MyDir; } TestBed.configureTestingModule({declarations: [MyDir, MyComp]}); @@ -2016,13 +2055,17 @@ describe('di', () => { it('should support dependencies in Pipes used inside ICUs', () => { @Injectable() class MyService { - transform(value: string): string { return `${value} (transformed)`; } + transform(value: string): string { + return `${value} (transformed)`; + } } @Pipe({name: 'somePipe'}) class MyPipe { constructor(private service: MyService) {} - transform(value: any): any { return this.service.transform(value); } + transform(value: any): any { + return this.service.transform(value); + } } @Component({ @@ -2051,13 +2094,17 @@ describe('di', () => { it('should support dependencies in Pipes used inside i18n blocks', () => { @Injectable() class MyService { - transform(value: string): string { return `${value} (transformed)`; } + transform(value: string): string { + return `${value} (transformed)`; + } } @Pipe({name: 'somePipe'}) class MyPipe { constructor(private service: MyService) {} - transform(value: any): any { return this.service.transform(value); } + transform(value: any): any { + return this.service.transform(value); + } } @Component({ @@ -2071,10 +2118,12 @@ describe('di', () => { class MyComp { count = '2'; - @ViewChild('target', {read: ViewContainerRef}) target !: ViewContainerRef; - @ViewChild('source', {read: TemplateRef}) source !: TemplateRef<any>; + @ViewChild('target', {read: ViewContainerRef}) target!: ViewContainerRef; + @ViewChild('source', {read: TemplateRef}) source!: TemplateRef<any>; - create() { this.target.createEmbeddedView(this.source); } + create() { + this.target.createEmbeddedView(this.source); + } } TestBed.configureTestingModule({ @@ -2109,8 +2158,8 @@ describe('di', () => { @Component({template: `<div dirA> <child></child> </div>`}) class App { - @ViewChild(DirA) dirA !: DirA; - @ViewChild(Child) child !: Child; + @ViewChild(DirA) dirA!: DirA; + @ViewChild(Child) child!: Child; } const fixture = TestBed.configureTestingModule({declarations: [DirA, DirB, App, Child]}) @@ -2121,4 +2170,134 @@ describe('di', () => { fixture.componentInstance.child.base, 'should not get dirA from parent, but create new dirB from the useFactory provider'); }); + + + describe('provider access on the same node', () => { + const token = new InjectionToken<number>('token'); + + onlyInIvy('accessing providers on the same node through a pipe was not supported in ViewEngine') + .it('pipes should access providers from the component they are on', () => { + @Pipe({name: 'token'}) + class TokenPipe { + constructor(@Inject(token) private _token: string) {} + + transform(value: string): string { + return value + this._token; + } + } + + @Component({ + selector: 'child-comp', + template: '{{value}}', + providers: [{provide: token, useValue: 'child'}] + }) + class ChildComp { + @Input() value: any; + } + + @Component({ + template: `<child-comp [value]="'' | token"></child-comp>`, + providers: [{provide: token, useValue: 'parent'}] + }) + class App { + } + + TestBed.configureTestingModule({declarations: [App, ChildComp, TokenPipe]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent.trim()).toBe('child'); + }); + + it('pipes should not access viewProviders from the component they are on', () => { + @Pipe({name: 'token'}) + class TokenPipe { + constructor(@Inject(token) private _token: string) {} + + transform(value: string): string { + return value + this._token; + } + } + + @Component({ + selector: 'child-comp', + template: '{{value}}', + viewProviders: [{provide: token, useValue: 'child'}] + }) + class ChildComp { + @Input() value: any; + } + + @Component({ + template: `<child-comp [value]="'' | token"></child-comp>`, + viewProviders: [{provide: token, useValue: 'parent'}] + }) + class App { + } + + TestBed.configureTestingModule({declarations: [App, ChildComp, TokenPipe]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent.trim()).toBe('parent'); + }); + + it('directives should access providers from the component they are on', () => { + @Directive({selector: '[dir]'}) + class Dir { + constructor(@Inject(token) public token: string) {} + } + + @Component({ + selector: 'child-comp', + template: '', + providers: [{provide: token, useValue: 'child'}], + }) + class ChildComp { + } + + @Component({ + template: '<child-comp dir></child-comp>', + providers: [{provide: token, useValue: 'parent'}] + }) + class App { + @ViewChild(Dir) dir!: Dir; + } + + TestBed.configureTestingModule({declarations: [App, ChildComp, Dir]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(fixture.componentInstance.dir.token).toBe('child'); + }); + + it('directives should not access viewProviders from the component they are on', () => { + @Directive({selector: '[dir]'}) + class Dir { + constructor(@Inject(token) public token: string) {} + } + + @Component({ + selector: 'child-comp', + template: '', + viewProviders: [{provide: token, useValue: 'child'}] + }) + class ChildComp { + } + + @Component({ + template: '<child-comp dir></child-comp>', + viewProviders: [{provide: token, useValue: 'parent'}] + }) + class App { + @ViewChild(Dir) dir!: Dir; + } + + TestBed.configureTestingModule({declarations: [App, ChildComp, Dir]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(fixture.componentInstance.dir.token).toBe('parent'); + }); + }); }); diff --git a/packages/core/test/acceptance/directive_spec.ts b/packages/core/test/acceptance/directive_spec.ts index 437cf825f23c4..21a6d87850aff 100644 --- a/packages/core/test/acceptance/directive_spec.ts +++ b/packages/core/test/acceptance/directive_spec.ts @@ -13,9 +13,7 @@ import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; describe('directives', () => { - describe('matching', () => { - @Directive({selector: 'ng-template[test]'}) class TestDirective { constructor(public templateRef: TemplateRef<any>) {} @@ -241,7 +239,9 @@ describe('directives', () => { @Directive({selector: '[dir]'}) class MyDir { - ngOnInit() { calls.push('MyDir.ngOnInit'); } + ngOnInit() { + calls.push('MyDir.ngOnInit'); + } } @Component({ @@ -266,7 +266,9 @@ describe('directives', () => { @Directive({selector: 'svg[dir]'}) class MyDir { constructor(private el: ElementRef) {} - ngOnInit() { calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`); } + ngOnInit() { + calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`); + } } @Component({ @@ -289,7 +291,9 @@ describe('directives', () => { @Directive({selector: 'text[dir]'}) class MyDir { constructor(private el: ElementRef) {} - ngOnInit() { calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`); } + ngOnInit() { + calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`); + } } @Component({ @@ -311,13 +315,13 @@ describe('directives', () => { @Directive({selector: '[test]'}) class MyDir { - constructor() { logs.push('MyDir.contructor'); } + constructor() { + logs.push('MyDir.contructor'); + } - @Input('test') - myInput = ''; + @Input('test') myInput = ''; - @Input('disabled') - myInput2 = ''; + @Input('disabled') myInput2 = ''; } @Component({ @@ -338,7 +342,6 @@ describe('directives', () => { expect(logs).toEqual(['MyDir.contructor']); }); - }); describe('inputs', () => { @@ -346,7 +349,9 @@ describe('directives', () => { let dirInstance: WithInput; @Directive({selector: '[dir]'}) class WithInput { - constructor() { dirInstance = this; } + constructor() { + dirInstance = this; + } @Input() dir: string = ''; } @@ -362,14 +367,16 @@ describe('directives', () => { const fixture = TestBed.createComponent(TestComp); fixture.detectChanges(); - expect(dirInstance !.dir).toBe('Hello'); + expect(dirInstance!.dir).toBe('Hello'); }); it('should allow directive inputs (as an interpolated prop) on <ng-template>', () => { let dirInstance: WithInput; @Directive({selector: '[dir]'}) class WithInput { - constructor() { dirInstance = this; } + constructor() { + dirInstance = this; + } @Input() dir: string = ''; } @@ -385,7 +392,7 @@ describe('directives', () => { const fixture = TestBed.createComponent(TestComp); fixture.detectChanges(); - expect(dirInstance !.dir).toBe('Hello'); + expect(dirInstance!.dir).toBe('Hello'); }); it('should allow directive inputs (as an interpolated prop) on <ng-template> with structural directives', @@ -393,7 +400,9 @@ describe('directives', () => { let dirInstance: WithInput; @Directive({selector: '[dir]'}) class WithInput { - constructor() { dirInstance = this; } + constructor() { + dirInstance = this; + } @Input() dir: string = ''; } @@ -409,8 +418,95 @@ describe('directives', () => { const fixture = TestBed.createComponent(TestComp); fixture.detectChanges(); - expect(dirInstance !.dir).toBe('Hello'); + expect(dirInstance!.dir).toBe('Hello'); }); + + it('should not set structural directive inputs from static element attrs', () => { + const dirInstances: StructuralDir[] = []; + + @Directive({selector: '[dir]'}) + class StructuralDir { + constructor() { + dirInstances.push(this); + } + @Input() dirOf!: number[]; + @Input() dirUnboundInput: any; + } + + @Component({ + template: ` + <!-- Regular form of structural directive --> + <div *dir="let item of items" dirUnboundInput>Some content</div> + + <!-- De-sugared version of the same structural directive --> + <ng-template dir let-item [dirOf]="items" dirUnboundInput> + <div>Some content</div> + </ng-template> + `, + }) + class App { + items: number[] = [1, 2, 3]; + } + + TestBed.configureTestingModule({ + declarations: [App, StructuralDir], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + const [regularDir, desugaredDir] = dirInstances; + + // When directive is used as a structural one, the `dirUnboundInput` should not be treated as + // an input. + expect(regularDir.dirUnboundInput).toBe(undefined); + + // In de-sugared version the `dirUnboundInput` acts as a regular input, so it should be set + // to an empty string. + expect(desugaredDir.dirUnboundInput).toBe(''); + }); + + it('should not set structural directive inputs from element bindings', () => { + const dirInstances: StructuralDir[] = []; + + @Directive({selector: '[dir]'}) + class StructuralDir { + constructor() { + dirInstances.push(this); + } + @Input() dirOf!: number[]; + @Input() title: any; + } + + @Component({ + template: ` + <!-- Regular form of structural directive --> + <div *dir="let item of items" [title]="title">Some content</div> + + <!-- De-sugared version of the same structural directive --> + <ng-template dir let-item [dirOf]="items" [title]="title"> + <div>Some content</div> + </ng-template> + `, + }) + class App { + items: number[] = [1, 2, 3]; + title: string = 'element title'; + } + + TestBed.configureTestingModule({ + declarations: [App, StructuralDir], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + const [regularDir, desugaredDir] = dirInstances; + + // When directive is used as a structural one, the `title` should not be treated as an input. + expect(regularDir.title).toBe(undefined); + + // In de-sugared version the `title` acts as a regular input, so it should be set. + expect(desugaredDir.title).toBe('element title'); + }); }); describe('outputs', () => { @@ -433,7 +529,7 @@ describe('directives', () => { expect(fixture.componentInstance.testDir).toBeTruthy(); expect(fixture.componentInstance.value).toBe(false); - fixture.componentInstance.testDir !.out.emit(); + fixture.componentInstance.testDir!.out.emit(); fixture.detectChanges(); expect(fixture.componentInstance.value).toBe(true); }); @@ -459,7 +555,6 @@ describe('directives', () => { fixture.detectChanges(); expect(fixture.componentInstance.value).toBeTruthy(); }); - }); describe('attribute shadowing behaviors', () => { @@ -471,8 +566,7 @@ describe('directives', () => { selector: '[dir-with-title]', }) class DirWithTitle { - @Input() - title = ''; + @Input() title = ''; } it('should set both the div attribute and the directive input for `title="value"`', () => { @@ -711,8 +805,12 @@ describe('directives', () => { const log: string[] = []; @Directive({selector: '[dir]'}) class DirectiveA { - constructor() { log.push('DirectiveA.constructor'); } - ngOnInit() { log.push('DirectiveA.ngOnInit'); } + constructor() { + log.push('DirectiveA.constructor'); + } + ngOnInit() { + log.push('DirectiveA.ngOnInit'); + } } @NgModule({ @@ -724,8 +822,12 @@ describe('directives', () => { @Directive({selector: '[dir]'}) class DirectiveB { - constructor() { log.push('DirectiveB.constructor'); } - ngOnInit() { log.push('DirectiveB.ngOnInit'); } + constructor() { + log.push('DirectiveB.constructor'); + } + ngOnInit() { + log.push('DirectiveB.ngOnInit'); + } } @Component({ @@ -752,8 +854,12 @@ describe('directives', () => { const log: string[] = []; @Directive({selector: '[dir]'}) class DirectiveA { - constructor() { log.push('DirectiveA.constructor'); } - ngOnInit() { log.push('DirectiveA.ngOnInit'); } + constructor() { + log.push('DirectiveA.constructor'); + } + ngOnInit() { + log.push('DirectiveA.ngOnInit'); + } } @NgModule({ @@ -765,8 +871,12 @@ describe('directives', () => { @Directive({selector: '[dir]'}) class DirectiveB { - constructor() { log.push('DirectiveB.constructor'); } - ngOnInit() { log.push('DirectiveB.ngOnInit'); } + constructor() { + log.push('DirectiveB.constructor'); + } + ngOnInit() { + log.push('DirectiveB.ngOnInit'); + } } @NgModule({ @@ -795,6 +905,5 @@ describe('directives', () => { 'DirectiveB.ngOnInit' ]); }); - }); }); diff --git a/packages/core/test/acceptance/discover_utils_spec.ts b/packages/core/test/acceptance/discover_utils_spec.ts index 101750b3d1eec..9bbebc4fda6c8 100644 --- a/packages/core/test/acceptance/discover_utils_spec.ts +++ b/packages/core/test/acceptance/discover_utils_spec.ts @@ -48,12 +48,16 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { @Component( {selector: 'child', template: '<p></p>', providers: [{provide: String, useValue: 'Child'}]}) class Child { - constructor() { childComponent.push(this); } + constructor() { + childComponent.push(this); + } } @Directive({selector: '[dirA]', exportAs: 'dirA'}) class DirectiveA { - constructor() { dirA.push(this); } + constructor() { + dirA.push(this); + } } @Component({ @@ -69,9 +73,13 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { }) class MyApp { text: string = 'INIT'; - constructor() { myApp = this; } + constructor() { + myApp = this; + } - log(event: any) { log.push(event); } + log(event: any) { + log.push(event); + } } describe('getComponent', () => { @@ -112,7 +120,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { }); it('should return context from element', () => { expect(getContext<MyApp>(child[0])).toEqual(myApp); - expect(getContext<{$implicit: boolean}>(child[2]) !.$implicit).toEqual(true); + expect(getContext<{$implicit: boolean}>(child[2])!.$implicit).toEqual(true); expect(getContext<Child>(p[0])).toEqual(childComponent[0]); }); }); @@ -264,7 +272,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { it('should work on templates', () => { const templateComment = Array.from((fixture.nativeElement as HTMLElement).childNodes) - .find((node: ChildNode) => node.nodeType === Node.COMMENT_NODE) !; + .find((node: ChildNode) => node.nodeType === Node.COMMENT_NODE)!; const lContext = loadLContext(templateComment); expect(lContext).toBeDefined(); expect(lContext.native as any).toBe(templateComment); @@ -274,7 +282,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { const ngContainerComment = Array.from((fixture.nativeElement as HTMLElement).childNodes) .find( (node: ChildNode) => node.nodeType === Node.COMMENT_NODE && - node.textContent === `ng-container`) !; + node.textContent === `ng-container`)!; const lContext = loadLContext(ngContainerComment); expect(lContext).toBeDefined(); expect(lContext.native as any).toBe(ngContainerComment); @@ -285,7 +293,6 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => { describe('getRootComponents()', () => { it('should return a list of the root components of the application from an element', () => { - @Component({selector: 'inner-comp', template: '<div></div>'}) class InnerComp { } @@ -299,13 +306,13 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => fixture.detectChanges(); const hostElm = fixture.nativeElement; - const innerElm = hostElm.querySelector('inner-comp') !; - const divElm = hostElm.querySelector('div') !; + const innerElm = hostElm.querySelector('inner-comp')!; + const divElm = hostElm.querySelector('div')!; const component = fixture.componentInstance; - expect(getRootComponents(hostElm) !).toEqual([component]); - expect(getRootComponents(innerElm) !).toEqual([component]); - expect(getRootComponents(divElm) !).toEqual([component]); + expect(getRootComponents(hostElm)!).toEqual([component]); + expect(getRootComponents(innerElm)!).toEqual([component]); + expect(getRootComponents(divElm)!).toEqual([component]); }); }); @@ -331,9 +338,9 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => ` }) class Comp { - @ViewChild(MyDir1) myDir1Instance !: MyDir1; - @ViewChild(MyDir2) myDir2Instance !: MyDir2; - @ViewChild(MyDir3) myDir3Instance !: MyDir3; + @ViewChild(MyDir1) myDir1Instance!: MyDir1; + @ViewChild(MyDir2) myDir2Instance!: MyDir2; + @ViewChild(MyDir3) myDir3Instance!: MyDir3; } TestBed.configureTestingModule({declarations: [Comp, MyDir1, MyDir2, MyDir3]}); @@ -345,19 +352,17 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => const elm1 = elements[0]; const elm1Dirs = getDirectives(elm1); - expect(elm1Dirs).toContain(fixture.componentInstance.myDir1Instance !); - expect(elm1Dirs).toContain(fixture.componentInstance.myDir2Instance !); + expect(elm1Dirs).toContain(fixture.componentInstance.myDir1Instance!); + expect(elm1Dirs).toContain(fixture.componentInstance.myDir2Instance!); const elm2 = elements[1]; const elm2Dirs = getDirectives(elm2); - expect(elm2Dirs).toContain(fixture.componentInstance.myDir3Instance !); + expect(elm2Dirs).toContain(fixture.componentInstance.myDir3Instance!); }); }); describe('getInjector', () => { - it('should return an injector that can return directive instances', () => { - @Component({template: ''}) class Comp { } @@ -386,7 +391,6 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => describe('getLocalRefs', () => { it('should return a map of local refs for an element', () => { - @Directive({selector: '[myDir]', exportAs: 'myDir'}) class MyDir { } @@ -399,7 +403,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => const fixture = TestBed.createComponent(Comp); fixture.detectChanges(); - const divEl = fixture.nativeElement.querySelector('div') !; + const divEl = fixture.nativeElement.querySelector('div')!; const localRefs = getLocalRefs(divEl); expect(localRefs.elRef.tagName.toLowerCase()).toBe('div'); @@ -416,7 +420,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => const fixture = TestBed.createComponent(Comp); fixture.detectChanges(); - const divEl = fixture.nativeElement.querySelector('div') !; + const divEl = fixture.nativeElement.querySelector('div')!; const localRefs = getLocalRefs(divEl); expect(localRefs.elRef.tagName.toLowerCase()).toBe('div'); @@ -439,11 +443,11 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => const fixture = TestBed.createComponent(Comp); fixture.detectChanges(); - const parent = fixture.nativeElement.querySelector('.parent') !; - const child = fixture.nativeElement.querySelector('.child') !; + const parent = fixture.nativeElement.querySelector('.parent')!; + const child = fixture.nativeElement.querySelector('.child')!; - const parentDebug = getDebugNode(parent) !; - const childDebug = getDebugNode(child) !; + const parentDebug = getDebugNode(parent)!; + const childDebug = getDebugNode(child)!; expect(parentDebug.native).toBe(parent); expect(childDebug.native).toBe(child); @@ -472,8 +476,8 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => const fixture = TestBed.createComponent(Comp); fixture.detectChanges(); - const child = fixture.nativeElement.querySelector('child-comp') !; - const childDebug = getDebugNode(child) !; + const child = fixture.nativeElement.querySelector('child-comp')!; + const childDebug = getDebugNode(child)!; expect(childDebug.native).toBe(child); expect(getElementStyles(child)).toEqual({ diff --git a/packages/core/test/acceptance/embedded_views_spec.ts b/packages/core/test/acceptance/embedded_views_spec.ts index a4fd8ba31bd1e..d0f17974e0f8d 100644 --- a/packages/core/test/acceptance/embedded_views_spec.ts +++ b/packages/core/test/acceptance/embedded_views_spec.ts @@ -10,7 +10,6 @@ import {Component, Input} from '@angular/core'; import {TestBed} from '@angular/core/testing'; describe('embedded views', () => { - it('should correctly resolve the implicit receiver in expressions', () => { const items: string[] = []; @@ -27,7 +26,9 @@ describe('embedded views', () => { }) class TestCmp { item: string = 'CmpItem'; - addItem() { items.push(this.item); } + addItem() { + items.push(this.item); + } } TestBed.configureTestingModule({declarations: [ChildCmp, TestCmp]}); @@ -36,8 +37,8 @@ describe('embedded views', () => { const childCmp: ChildCmp = fixture.debugElement.children[0].componentInstance; - childCmp.addItemFn !(); - childCmp.addItemFn !(); + childCmp.addItemFn!(); + childCmp.addItemFn!(); expect(items).toEqual(['CmpItem', 'CmpItem']); }); @@ -71,5 +72,4 @@ describe('embedded views', () => { expect(fixture.nativeElement.textContent).toBe('HelloHello'); }); - }); diff --git a/packages/core/test/acceptance/exports_spec.ts b/packages/core/test/acceptance/exports_spec.ts index 41921f280557b..1513528a13bd0 100644 --- a/packages/core/test/acceptance/exports_spec.ts +++ b/packages/core/test/acceptance/exports_spec.ts @@ -247,7 +247,13 @@ class DirWithCompInput { class DirToReferenceWithPreOrderHooks implements OnInit, OnChanges, DoCheck { @Input() in : any = null; name = 'Drew'; - ngOnChanges(changes: SimpleChanges) { this.name += '!'; } - ngOnInit() { this.name += '?'; } - ngDoCheck() { this.name += '@'; } + ngOnChanges(changes: SimpleChanges) { + this.name += '!'; + } + ngOnInit() { + this.name += '?'; + } + ngDoCheck() { + this.name += '@'; + } } diff --git a/packages/core/test/acceptance/host_binding_spec.ts b/packages/core/test/acceptance/host_binding_spec.ts index 6ed40776605c3..8457bf06d03c3 100644 --- a/packages/core/test/acceptance/host_binding_spec.ts +++ b/packages/core/test/acceptance/host_binding_spec.ts @@ -121,10 +121,9 @@ describe('host bindings', () => { class ParentCmp { private _prop = ''; - @ViewChild('template', {read: ViewContainerRef}) - vcr: ViewContainerRef = null !; + @ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!; - private child: ComponentRef<ChildCmp> = null !; + private child: ComponentRef<ChildCmp> = null!; @Input() set prop(value: string) { @@ -137,10 +136,11 @@ describe('host bindings', () => { } } - get prop() { return this._prop; } + get prop() { + return this._prop; + } - @Input() - prop2 = 0; + @Input() prop2 = 0; ngAfterViewInit() { const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp); @@ -212,7 +212,6 @@ describe('host bindings', () => { onlyInIvy('[style.prop] and [class.name] prioritization is a new feature') .it('should prioritize styling present in the order of directive hostBinding evaluation, but consider sub-classed directive styling to be the most important', () => { - @Component({template: '<div child-dir sibling-dir></div>'}) class MyApp { } @@ -220,37 +219,55 @@ describe('host bindings', () => { @Directive({selector: '[parent-dir]'}) class ParentDir { @HostBinding('style.width') - get width1() { return '100px'; } + get width1() { + return '100px'; + } @HostBinding('style.height') - get height1() { return '100px'; } + get height1() { + return '100px'; + } @HostBinding('style.color') - get color1() { return 'red'; } + get color1() { + return 'red'; + } } @Directive({selector: '[child-dir]'}) class ChildDir extends ParentDir { @HostBinding('style.width') - get width2() { return '200px'; } + get width2() { + return '200px'; + } @HostBinding('style.height') - get height2() { return '200px'; } + get height2() { + return '200px'; + } } @Directive({selector: '[sibling-dir]'}) class SiblingDir { @HostBinding('style.width') - get width3() { return '300px'; } + get width3() { + return '300px'; + } @HostBinding('style.height') - get height3() { return '300px'; } + get height3() { + return '300px'; + } @HostBinding('style.opacity') - get opacity3() { return '0.5'; } + get opacity3() { + return '0.5'; + } @HostBinding('style.color') - get color1() { return 'blue'; } + get color1() { + return 'blue'; + } } TestBed.configureTestingModule( @@ -295,25 +312,22 @@ describe('host bindings', () => { fixture.detectChanges(); }).not.toThrow(); }); - }); @Directive({selector: '[hostBindingDir]'}) class HostBindingDir { - @HostBinding() - id = 'foo'; + @HostBinding() id = 'foo'; } it('should support host bindings in directives', () => { @Directive({selector: '[dir]'}) class Dir { - @HostBinding('className') - klass = 'foo'; + @HostBinding('className') klass = 'foo'; } @Component({template: '<span dir></span>'}) class App { - @ViewChild(Dir) directiveInstance !: Dir; + @ViewChild(Dir) directiveInstance!: Dir; } TestBed.configureTestingModule({declarations: [App, Dir]}); @@ -333,8 +347,7 @@ describe('host bindings', () => { it('should support host bindings on root component', () => { @Component({template: ''}) class HostBindingComp { - @HostBinding() - title = 'my-title'; + @HostBinding() title = 'my-title'; } TestBed.configureTestingModule({declarations: [HostBindingComp]}); @@ -365,8 +378,7 @@ describe('host bindings', () => { class App { constructor(public serviceOne: ServiceOne, public serviceTwo: ServiceTwo) {} - @HostBinding() - title = 'my-title'; + @HostBinding() title = 'my-title'; } TestBed.configureTestingModule({declarations: [App]}); @@ -390,8 +402,7 @@ describe('host bindings', () => { @Component({selector: 'host-title-comp', template: ''}) class HostTitleComp { - @HostBinding() - title = 'my-title'; + @HostBinding() title = 'my-title'; } @Component({ @@ -402,7 +413,7 @@ describe('host bindings', () => { ` }) class App { - @ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir; + @ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir; } TestBed.configureTestingModule({declarations: [App, SomeDir, HostTitleComp, HostBindingDir]}); @@ -415,7 +426,7 @@ describe('host bindings', () => { expect(hostBindingDiv.id).toEqual('foo'); expect(hostTitleComp.title).toEqual('my-title'); - fixture.componentInstance.hostBindingDir !.id = 'bar'; + fixture.componentInstance.hostBindingDir!.id = 'bar'; fixture.detectChanges(); expect(hostBindingDiv.id).toEqual('bar'); }); @@ -423,8 +434,7 @@ describe('host bindings', () => { it('should support consecutive components with host bindings', () => { @Component({selector: 'host-binding-comp', template: ''}) class HostBindingComp { - @HostBinding() - id = 'blue'; + @HostBinding() id = 'blue'; } @Component({ @@ -434,7 +444,7 @@ describe('host bindings', () => { ` }) class App { - @ViewChildren(HostBindingComp) hostBindingComp !: QueryList<HostBindingComp>; + @ViewChildren(HostBindingComp) hostBindingComp!: QueryList<HostBindingComp>; } TestBed.configureTestingModule({declarations: [App, HostBindingComp]}); @@ -470,7 +480,7 @@ describe('host bindings', () => { @Component({template: '<div someDir hostBindingDir></div>'}) class App { - @ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir; + @ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir; } TestBed.configureTestingModule({declarations: [App, SomeDir, HostBindingDir]}); @@ -480,7 +490,7 @@ describe('host bindings', () => { const hostBindingDiv = fixture.nativeElement.querySelector('div') as HTMLElement; expect(hostBindingDiv.id).toEqual('foo'); - fixture.componentInstance.hostBindingDir !.id = 'bar'; + fixture.componentInstance.hostBindingDir!.id = 'bar'; fixture.detectChanges(); expect(hostBindingDiv.id).toEqual('bar'); }); @@ -490,18 +500,23 @@ describe('host bindings', () => { it('should support host bindings that rely on values from init hooks', () => { @Component({template: '', selector: 'init-hook-comp'}) class InitHookComp implements OnInit, OnChanges, DoCheck { - @Input() - inputValue = ''; + @Input() inputValue = ''; changesValue = ''; initValue = ''; checkValue = ''; - ngOnChanges() { this.changesValue = 'changes'; } + ngOnChanges() { + this.changesValue = 'changes'; + } - ngOnInit() { this.initValue = 'init'; } + ngOnInit() { + this.initValue = 'init'; + } - ngDoCheck() { this.checkValue = 'check'; } + ngDoCheck() { + this.checkValue = 'check'; + } @HostBinding('title') get value() { @@ -529,16 +544,14 @@ describe('host bindings', () => { it('should support host bindings with the same name as inputs', () => { @Directive({selector: '[hostBindingDir]'}) class HostBindingInputDir { - @Input() - disabled = false; + @Input() disabled = false; - @HostBinding('disabled') - hostDisabled = false; + @HostBinding('disabled') hostDisabled = false; } @Component({template: '<input hostBindingDir [disabled]="isDisabled">'}) class App { - @ViewChild(HostBindingInputDir) hostBindingInputDir !: HostBindingInputDir; + @ViewChild(HostBindingInputDir) hostBindingInputDir!: HostBindingInputDir; isDisabled = true; } @@ -611,14 +624,12 @@ describe('host bindings', () => { it('should support component with host bindings and array literals', () => { @Component({selector: 'host-binding-comp', template: ''}) class HostBindingComp { - @HostBinding() - id = 'my-id'; + @HostBinding() id = 'my-id'; } @Component({selector: 'name-comp', template: ''}) class NameComp { - @Input() - names !: string[]; + @Input() names!: string[]; } @Component({ @@ -628,7 +639,7 @@ describe('host bindings', () => { ` }) class App { - @ViewChild(NameComp) nameComp !: NameComp; + @ViewChild(NameComp) nameComp!: NameComp; name = ''; } @@ -661,8 +672,7 @@ describe('host bindings', () => { it('should support host bindings that contain array literals', () => { @Component({selector: 'name-comp', template: ''}) class NameComp { - @Input() - names !: string[]; + @Input() names!: string[]; } @Component({ @@ -684,8 +694,8 @@ describe('host bindings', () => { ` }) class App { - @ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp; - @ViewChild(NameComp) nameComp !: NameComp; + @ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp; + @ViewChild(NameComp) nameComp!: NameComp; name = ''; otherName = ''; } @@ -703,11 +713,11 @@ describe('host bindings', () => { expect(hostBindingEl.id).toBe('red,blue'); expect(hostBindingEl.dir).toBe('ltr'); expect(hostBindingEl.title).toBe('my title,other title'); - expect(nameComp !.names).toEqual(['Frank', 'Nancy', 'Joe']); + expect(nameComp!.names).toEqual(['Frank', 'Nancy', 'Joe']); - const firstArray = nameComp !.names; + const firstArray = nameComp!.names; fixture.detectChanges(); - expect(firstArray).toBe(nameComp !.names); + expect(firstArray).toBe(nameComp!.names); hostBindingComp.id = 'green'; hostBindingComp.dir = 'rtl'; @@ -729,7 +739,9 @@ describe('host bindings', () => { @Directive({selector: '[hostListenerDir]'}) class HostListenerDir { @HostListener('click') - onClick() { events.push('click!'); } + onClick() { + events.push('click!'); + } } @Component({template: '<button hostListenerDir hostDir>Click</button>'}) @@ -740,7 +752,7 @@ describe('host bindings', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - const button = fixture.nativeElement.querySelector('button') !; + const button = fixture.nativeElement.querySelector('button')!; button.click(); expect(events).toEqual(['click!']); expect(button.title).toEqual('my title,other title'); @@ -759,8 +771,8 @@ describe('host bindings', () => { @Component({template: '<host-binding-comp hostDir></host-binding-comp>'}) class App { - @ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp; - @ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir; + @ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp; + @ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir; } TestBed.configureTestingModule({declarations: [App, HostBindingComp, HostBindingDir]}); @@ -798,7 +810,7 @@ describe('host bindings', () => { @Component({template: `<host-binding-comp></host-binding-comp>{{ name }}`}) class App { - @ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp; + @ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp; name = ''; } @@ -872,8 +884,8 @@ describe('host bindings', () => { ` }) class App { - @ViewChild(SubDirective) subDir !: SubDirective; - @ViewChild(SuperDirective) superDir !: SuperDirective; + @ViewChild(SubDirective) subDir!: SubDirective; + @ViewChild(SuperDirective) superDir!: SuperDirective; } TestBed.configureTestingModule({declarations: [App, SuperDirective, SubDirective]}); @@ -927,8 +939,7 @@ describe('host bindings', () => { host: {'[id]': 'foos.length'} }) class HostBindingWithContentChildren { - @ContentChildren('foo') - foos !: QueryList<any>; + @ContentChildren('foo') foos!: QueryList<any>; } @Component({ @@ -955,7 +966,9 @@ describe('host bindings', () => { class HostBindingWithContentHooks implements AfterContentInit { myValue = 'initial'; - ngAfterContentInit() { this.myValue = 'after-content'; } + ngAfterContentInit() { + this.myValue = 'after-content'; + } } @Component({template: '<host-binding-comp></host-binding-comp>'}) @@ -971,7 +984,6 @@ describe('host bindings', () => { }); describe('styles', () => { - it('should bind to host styles', () => { @Component( {selector: 'host-binding-to-styles', host: {'[style.width.px]': 'width'}, template: ''}) @@ -981,7 +993,7 @@ describe('host bindings', () => { @Component({template: '<host-binding-to-styles></host-binding-to-styles>'}) class App { - @ViewChild(HostBindingToStyles) hostBindingDir !: HostBindingToStyles; + @ViewChild(HostBindingToStyles) hostBindingDir!: HostBindingToStyles; } TestBed.configureTestingModule({declarations: [App, HostBindingToStyles]}); @@ -1010,7 +1022,7 @@ describe('host bindings', () => { @Component({template: '<div hostStyles containerDir></div>'}) class App { - @ViewChild(HostBindingToStyles) hostBindingDir !: HostBindingToStyles; + @ViewChild(HostBindingToStyles) hostBindingDir!: HostBindingToStyles; } TestBed.configureTestingModule({declarations: [App, HostBindingToStyles, ContainerDir]}); @@ -1044,10 +1056,12 @@ describe('host bindings', () => { }); describe('sanitization', () => { - function identity(value: any) { return value; } - function verify( - tag: string, prop: string, value: any, expectedSanitizedValue: any, bypassFn: Function, - isAttribute: boolean = true, throws: boolean = false) { + function identity(value: any) { + return value; + } + function verify(tag: string, prop: string, value: any, expectedSanitizedValue: any, + bypassFn: Function, isAttribute: boolean = true, + throws: boolean = false) { it(`should sanitize <${tag} ${prop}> ${isAttribute ? 'properties' : 'attributes'}`, () => { @Directive({ selector: '[unsafeUrlHostBindingDir]', @@ -1061,13 +1075,13 @@ describe('host bindings', () => { @Component({template: `<${tag} unsafeUrlHostBindingDir></${tag}>`}) class App { - @ViewChild(UnsafeDir) unsafeDir !: UnsafeDir; + @ViewChild(UnsafeDir) unsafeDir!: UnsafeDir; } TestBed.configureTestingModule({declarations: [App, UnsafeDir]}); const fixture = TestBed.createComponent(App); fixture.detectChanges(); - const el = fixture.nativeElement.querySelector(tag) !; + const el = fixture.nativeElement.querySelector(tag)!; const current = () => isAttribute ? el.getAttribute(prop) : (el as any)[prop]; fixture.componentInstance.unsafeDir.value = value; @@ -1103,5 +1117,4 @@ describe('host bindings', () => { '<img src="unsafe:javascript:alert(3)">', bypassSanitizationTrustHtml, /* isAttribute */ false); }); - }); diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index bf598a8868976..6fd2e8e631b5b 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -8,16 +8,17 @@ // Make the `$localize()` global function available to the compiled templates, and the direct calls // below. This would normally be done inside the application `polyfills.ts` file. import '@angular/localize/init'; + import {CommonModule, registerLocaleData} from '@angular/common'; import localeRo from '@angular/common/locales/ro'; -import {Component, ContentChild, ElementRef, ContentChildren, Directive, HostBinding, Input, LOCALE_ID, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef, Pipe, PipeTransform, NO_ERRORS_SCHEMA} from '@angular/core'; +import {computeMsgId} from '@angular/compiler'; +import {Component, ContentChild, ContentChildren, Directive, ElementRef, HostBinding, Input, LOCALE_ID, NO_ERRORS_SCHEMA, Pipe, PipeTransform, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core'; import {setDelayProjection} from '@angular/core/src/render3/instructions/projection'; import {TestBed} from '@angular/core/testing'; -import {loadTranslations, clearTranslations} from '@angular/localize'; +import {clearTranslations, loadTranslations} from '@angular/localize'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {onlyInIvy} from '@angular/private/testing'; -import {computeMsgId} from '@angular/compiler'; import {BehaviorSubject} from 'rxjs'; @@ -72,7 +73,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should support interpolations with custom interpolation config', () => { loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); - const interpolation = ['{%', '%}'] as[string, string]; + const interpolation = ['{%', '%}'] as [string, string]; TestBed.overrideComponent(AppComp, {set: {interpolation}}); const fixture = initWithTemplate(AppComp, `<div i18n>Hello {% name %}</div>`); @@ -277,7 +278,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { name = `Angular`; clicks = 0; - onClick() { this.clicks++; } + onClick() { + this.clicks++; + } } TestBed.configureTestingModule({declarations: [ListenerComp]}); @@ -619,7 +622,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { [computeMsgId('{VAR_SELECT, select, 10 {ten} other {{INTERPOLATION}}}')]: '{VAR_SELECT, select, 10 {dix} other {{INTERPOLATION}}}' }); - const interpolation = ['{%', '%}'] as[string, string]; + const interpolation = ['{%', '%}'] as [string, string]; TestBed.overrideComponent(AppComp, {set: {interpolation}}); const fixture = initWithTemplate(AppComp, `<div i18n>{count, select, 10 {ten} other {{% name %}}}</div>`); @@ -704,7 +707,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' }); const fixture = initWithTemplate( - AppComp, ` + AppComp, + ` <ng-template i18n tplRef>` + `{count, select, 10 {ten} 20 {twenty} other {other}}` + `</ng-template> @@ -864,7 +868,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { private readonly viewContainerRef: ViewContainerRef, private readonly templateRef: TemplateRef<any>) {} - ngOnInit() { this.viewContainerRef.createEmbeddedView(this.templateRef); } + ngOnInit() { + this.viewContainerRef.createEmbeddedView(this.templateRef); + } } @Component({ @@ -941,7 +947,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { dir = this; } - attachEmbeddedView() { this.viewContainerRef.createEmbeddedView(this.templateRef); } + attachEmbeddedView() { + this.viewContainerRef.createEmbeddedView(this.templateRef); + } } @Component({ @@ -983,7 +991,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { expect(fixture.debugElement.nativeElement.innerHTML) .toBe('<my-cmp><!--container--></my-cmp>'); - dir !.attachEmbeddedView(); + dir!.attachEmbeddedView(); fixture.detectChanges(); expect(fixture.debugElement.nativeElement.innerHTML) .toBe( @@ -1019,7 +1027,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { class Comp { type = 'A'; visible = true; - isVisible() { return true; } + isVisible() { + return true; + } } TestBed.configureTestingModule({declarations: [Comp]}); @@ -1042,7 +1052,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { '{VAR_SELECT, select, A {A - {PH_A}} ' + 'B {B - {PH_B}} other {other - {PH_WITH_SPACES}}}')]: '{VAR_SELECT, select, A {A (translated) - {PH_A}} ' + - 'B {B (translated) - {PH_B}} other {other (translated) - {PH_WITH_SPACES}}}', + 'B {B (translated) - {PH_B}} other {other (translated) - {PH_WITH_SPACES}}}', }); @Component({ selector: 'comp', @@ -1326,7 +1336,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('with custom interpolation config', () => { loadTranslations({[computeMsgId('Hello {$INTERPOLATION}', 'm')]: 'Bonjour {$INTERPOLATION}'}); - const interpolation = ['{%', '%}'] as[string, string]; + const interpolation = ['{%', '%}'] as [string, string]; TestBed.overrideComponent(AppComp, {set: {interpolation}}); const fixture = initWithTemplate(AppComp, `<div i18n-title="m|d" title="Hello {% name %}"></div>`); @@ -1367,7 +1377,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }) class TitleDir { @Input() title = ''; - constructor() { titleDirInstances.push(this); } + constructor() { + titleDirInstances.push(this); + } } @Component({ @@ -1397,7 +1409,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { @Directive({selector: '[title]'}) class TitleDir { @Input() title: string = ''; - constructor(public elRef: ElementRef) { titleDirInstances.push(this); } + constructor(public elRef: ElementRef) { + titleDirInstances.push(this); + } } @Component({ @@ -1429,7 +1443,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { let dirInstance: WithInput; @Directive({selector: '[dir]'}) class WithInput { - constructor() { dirInstance = this; } + constructor() { + dirInstance = this; + } @Input() dir: string = ''; } @@ -1445,7 +1461,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { const fixture = TestBed.createComponent(TestComp); fixture.detectChanges(); - expect(dirInstance !.dir).toBe('Bonjour Angular'); + expect(dirInstance!.dir).toBe('Bonjour Angular'); }); it('should allow directive inputs (as interpolated props)' + @@ -1456,7 +1472,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { let dirInstance: WithInput; @Directive({selector: '[dir]'}) class WithInput { - constructor() { dirInstance = this; } + constructor() { + dirInstance = this; + } @Input() dir: string = ''; } @@ -1472,7 +1490,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { const fixture = TestBed.createComponent(TestComp); fixture.detectChanges(); - expect(dirInstance !.dir).toBe('Bonjour Angular'); + expect(dirInstance!.dir).toBe('Bonjour Angular'); }); it('should apply i18n attributes during second template pass', () => { @@ -1537,7 +1555,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { template: '{{ messageText }}', }) class WelcomeComp { - @Input() messageText !: string; + @Input() messageText!: string; } @Component({ @@ -1593,15 +1611,51 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); }); + describe('empty translations', () => { + it('should replace existing text content with empty translation', () => { + loadTranslations({[computeMsgId('Some Text')]: ''}); + const fixture = initWithTemplate(AppComp, '<div i18n>Some Text</div>'); + expect(fixture.nativeElement.textContent).toBe(''); + }); + + it('should replace existing DOM elements with empty translation', () => { + loadTranslations({ + [computeMsgId( + ' Start {$START_TAG_DIV}DIV{$CLOSE_TAG_DIV}' + + '{$START_TAG_SPAN}SPAN{$CLOSE_TAG_SPAN} End ')]: '', + }); + const fixture = initWithTemplate(AppComp, ` + <div i18n> + Start + <div>DIV</div> + <span>SPAN</span> + End + </div> + `); + expect(fixture.nativeElement.textContent).toBe(''); + }); + + it('should replace existing ICU content with empty translation', () => { + loadTranslations({ + [computeMsgId('{VAR_PLURAL, plural, =0 {zero} other {more than zero}}')]: '', + }); + const fixture = initWithTemplate(AppComp, ` + <div i18n>{count, plural, =0 {zero} other {more than zero}}</div> + `); + expect(fixture.nativeElement.textContent).toBe(''); + }); + }); + it('should work with directives and host bindings', () => { let directiveInstances: ClsDir[] = []; @Directive({selector: '[test]'}) class ClsDir { - @HostBinding('className') - klass = 'foo'; + @HostBinding('className') klass = 'foo'; - constructor() { directiveInstances.push(this); } + constructor() { + directiveInstances.push(this); + } } @Component({ @@ -1641,7 +1695,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { // but IE does not. expect(outerDiv.getAttribute('title')).toBe('début 2 milieu 1 fin'); expect(outerDiv.getAttribute('class')).toBe('foo'); - expect(outerDiv.textContent !.trim()).toBe('traduction: un email'); + expect(outerDiv.textContent!.trim()).toBe('traduction: un email'); expect(innerDiv.getAttribute('class')).toBe('foo'); directiveInstances.forEach(instance => instance.klass = 'bar'); @@ -1651,7 +1705,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { expect(outerDiv.getAttribute('title')).toBe('début 3 milieu 2 fin'); expect(outerDiv.getAttribute('class')).toBe('bar'); - expect(outerDiv.textContent !.trim()).toBe('traduction: 2 emails'); + expect(outerDiv.textContent!.trim()).toBe('traduction: 2 emails'); expect(innerDiv.getAttribute('class')).toBe('bar'); }); @@ -1660,21 +1714,25 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { let calledValue = false; @Component({selector: 'my-comp', template: ''}) class MyComp { - t !: string; + t!: string; @Input() - get title() { return this.t; } + get title() { + return this.t; + } set title(title) { calledTitle = true; this.t = title; } @Input() - get value() { return this.val; } + get value() { + return this.val; + } set value(value: string) { calledValue = true; this.val = value; } - val !: string; + val!: string; } TestBed.configureTestingModule({declarations: [AppComp, MyComp]}); @@ -2050,7 +2108,6 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should display/destroy projected i18n content', () => { - loadTranslations({ [computeMsgId('{VAR_SELECT, select, A {A} B {B} other {other}}')]: '{VAR_SELECT, select, A {A} B {B} other {other}}' @@ -2109,7 +2166,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { @Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'}) class TextDirective { // TODO(issue/24571): remove '!'. - text !: string; + text!: string; constructor() {} } @@ -2119,16 +2176,18 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { @ContentChild(TemplateRef, {static: true}) template !: TemplateRef<any>; // TODO(issue/24571): remove '!'. - @ViewChild('vc', {read: ViewContainerRef, static: true}) - vc !: ViewContainerRef; + @ViewChild('vc', {read: ViewContainerRef, static: true}) vc!: ViewContainerRef; // TODO(issue/24571): remove '!'. - @ContentChildren(TextDirective, {descendants: true}) - query !: QueryList<TextDirective>; + @ContentChildren(TextDirective, {descendants: true}) query!: QueryList<TextDirective>; - create() { this.vc.createEmbeddedView(this.template); } + create() { + this.vc.createEmbeddedView(this.template); + } - destroy() { this.vc.clear(); } + destroy() { + this.vc.clear(); + } } TestBed.configureTestingModule({declarations: [TextDirective, DivQuery]}); @@ -2224,7 +2283,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { @Directive({selector: 'input'}) class InputsDir { constructor(private elementRef: ElementRef) {} - ngOnInit() { this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; } + ngOnInit() { + this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; + } } @Component({ @@ -2248,7 +2309,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { @Directive({selector: 'input'}) class InputsDir { constructor(private elementRef: ElementRef) {} - ngOnInit() { this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; } + ngOnInit() { + this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; + } } @Component({ @@ -2401,12 +2464,16 @@ class AppCompWithWhitespaces { }) class DirectiveWithTplRef { constructor(public vcRef: ViewContainerRef, public tplRef: TemplateRef<{}>) {} - ngOnInit() { this.vcRef.createEmbeddedView(this.tplRef, {}); } + ngOnInit() { + this.vcRef.createEmbeddedView(this.tplRef, {}); + } } @Pipe({name: 'uppercase'}) class UppercasePipe implements PipeTransform { - transform(value: string) { return value.toUpperCase(); } + transform(value: string) { + return value.toUpperCase(); + } } @Directive({selector: `[dialog]`}) diff --git a/packages/core/test/acceptance/inherit_definition_feature_spec.ts b/packages/core/test/acceptance/inherit_definition_feature_spec.ts index 91fc4dfa3d60d..42f69a2e7aa6c 100644 --- a/packages/core/test/acceptance/inherit_definition_feature_spec.ts +++ b/packages/core/test/acceptance/inherit_definition_feature_spec.ts @@ -118,10 +118,10 @@ describe('inheritance', () => { 'Base.backgroundColor', 'Super.color', 'Sub2.width', // ]); if (ivyEnabled) { - expect(getDirectiveDef(BaseDirective) !.hostVars).toEqual(2); - expect(getDirectiveDef(SuperDirective) !.hostVars).toEqual(4); - expect(getDirectiveDef(Sub1Directive) !.hostVars).toEqual(6); - expect(getDirectiveDef(Sub2Directive) !.hostVars).toEqual(6); + expect(getDirectiveDef(BaseDirective)!.hostVars).toEqual(2); + expect(getDirectiveDef(SuperDirective)!.hostVars).toEqual(4); + expect(getDirectiveDef(Sub1Directive)!.hostVars).toEqual(6); + expect(getDirectiveDef(Sub2Directive)!.hostVars).toEqual(6); } }); }); @@ -134,7 +134,9 @@ describe('inheritance', () => { class SuperDirective implements OnChanges { @Input() someInput = ''; - ngOnChanges() { log.push('on changes!'); } + ngOnChanges() { + log.push('on changes!'); + } } @Directive({selector: '[subDir]'}) @@ -158,7 +160,9 @@ describe('inheritance', () => { const log: string[] = []; class SuperClass { - ngOnChanges() { log.push('on changes!'); } + ngOnChanges() { + log.push('on changes!'); + } } @Directive({selector: '[subDir]'}) @@ -186,7 +190,9 @@ describe('inheritance', () => { class GrandSuperDirective implements OnChanges { @Input() someInput = ''; - ngOnChanges() { log.push('on changes!'); } + ngOnChanges() { + log.push('on changes!'); + } } @Directive({selector: '[superDir]'}) @@ -215,7 +221,9 @@ describe('inheritance', () => { const log: string[] = []; class GrandSuperClass { - ngOnChanges() { log.push('on changes!'); } + ngOnChanges() { + log.push('on changes!'); + } } @Directive({selector: '[superDir]'}) @@ -248,7 +256,9 @@ describe('inheritance', () => { class GrandSuperDirective implements OnChanges { @Input() someInput = ''; - ngOnChanges() { log.push('on changes!'); } + ngOnChanges() { + log.push('on changes!'); + } } class SuperClass extends GrandSuperDirective {} @@ -275,7 +285,9 @@ describe('inheritance', () => { const log: string[] = []; class GrandSuperClass { - ngOnChanges() { log.push('on changes!'); } + ngOnChanges() { + log.push('on changes!'); + } } class SuperClass extends GrandSuperClass {} @@ -310,7 +322,9 @@ describe('inheritance', () => { abstract class UndecoratedBase extends Base { abstract input: any; - ngOnChanges() { changes++; } + ngOnChanges() { + changes++; + } } @Component({selector: 'my-comp', template: ''}) @@ -339,13 +353,27 @@ describe('inheritance', () => { const fired: string[] = []; class SuperDirective { - ngOnInit() { fired.push('super init'); } - ngOnDestroy() { fired.push('super destroy'); } - ngAfterContentInit() { fired.push('super after content init'); } - ngAfterContentChecked() { fired.push('super after content checked'); } - ngAfterViewInit() { fired.push('super after view init'); } - ngAfterViewChecked() { fired.push('super after view checked'); } - ngDoCheck() { fired.push('super do check'); } + ngOnInit() { + fired.push('super init'); + } + ngOnDestroy() { + fired.push('super destroy'); + } + ngAfterContentInit() { + fired.push('super after content init'); + } + ngAfterContentChecked() { + fired.push('super after content checked'); + } + ngAfterViewInit() { + fired.push('super after view init'); + } + ngAfterViewChecked() { + fired.push('super after view checked'); + } + ngDoCheck() { + fired.push('super do check'); + } } beforeEach(() => fired.length = 0); @@ -355,7 +383,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngOnInit() { fired.push('sub init'); } + ngOnInit() { + fired.push('sub init'); + } } @Component({ @@ -394,7 +424,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngDoCheck() { fired.push('sub do check'); } + ngDoCheck() { + fired.push('sub do check'); + } } @Component({ @@ -433,7 +465,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterContentInit() { fired.push('sub after content init'); } + ngAfterContentInit() { + fired.push('sub after content init'); + } } @Component({ @@ -472,7 +506,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterContentChecked() { fired.push('sub after content checked'); } + ngAfterContentChecked() { + fired.push('sub after content checked'); + } } @Component({ @@ -511,7 +547,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterViewInit() { fired.push('sub after view init'); } + ngAfterViewInit() { + fired.push('sub after view init'); + } } @Component({ @@ -550,7 +588,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterViewChecked() { fired.push('sub after view checked'); } + ngAfterViewChecked() { + fired.push('sub after view checked'); + } } @Component({ @@ -589,7 +629,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngOnDestroy() { fired.push('sub destroy'); } + ngOnDestroy() { + fired.push('sub destroy'); + } } @Component({ @@ -631,25 +673,20 @@ describe('inheritance', () => { it('should inherit inputs', () => { class SuperDirective { - @Input() - foo = ''; + @Input() foo = ''; - @Input() - bar = ''; + @Input() bar = ''; - @Input() - baz = ''; + @Input() baz = ''; } @Directive({ selector: '[sub-dir]', }) class SubDirective extends SuperDirective { - @Input() - baz = ''; + @Input() baz = ''; - @Input() - qux = ''; + @Input() qux = ''; } @Component({template: `<p sub-dir [foo]="a" [bar]="b" [baz]="c" [qux]="d"></p>`}) @@ -684,15 +721,16 @@ describe('inheritance', () => { it('should inherit outputs', () => { class SuperDirective { - @Output() - foo = new EventEmitter<string>(); + @Output() foo = new EventEmitter<string>(); } @Directive({ selector: '[sub-dir]', }) class SubDirective extends SuperDirective { - ngOnInit() { this.foo.emit('test'); } + ngOnInit() { + this.foo.emit('test'); + } } @Component({ @@ -703,7 +741,9 @@ describe('inheritance', () => { class App { foo = ''; - handleFoo(event: string) { this.foo = event; } + handleFoo(event: string) { + this.foo = event; + } } TestBed.configureTestingModule({ @@ -722,11 +762,9 @@ describe('inheritance', () => { // TODO: sub and super HostBinding same binding on two different properties it('should compose host bindings for styles', () => { class SuperDirective { - @HostBinding('style.color') - color = 'red'; + @HostBinding('style.color') color = 'red'; - @HostBinding('style.backgroundColor') - bg = 'black'; + @HostBinding('style.backgroundColor') bg = 'black'; } @Directive({ @@ -762,10 +800,11 @@ describe('inheritance', () => { it('should compose host bindings (non-style related)', () => { class SuperDirective { @HostBinding('title') - get boundTitle() { return this.superTitle + '!!!'; } + get boundTitle() { + return this.superTitle + '!!!'; + } - @Input() - superTitle = ''; + @Input() superTitle = ''; } @Directive({ @@ -803,15 +842,16 @@ describe('inheritance', () => { } class SuperDirective { - @ContentChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ContentChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Directive({ selector: '[sub-dir]', }) class SubDirective extends SuperDirective { - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -831,7 +871,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(2); + expect(foundQueryList!.length).toBe(2); }); }); @@ -873,13 +913,27 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - ngOnInit() { fired.push('super init'); } - ngOnDestroy() { fired.push('super destroy'); } - ngAfterContentInit() { fired.push('super after content init'); } - ngAfterContentChecked() { fired.push('super after content checked'); } - ngAfterViewInit() { fired.push('super after view init'); } - ngAfterViewChecked() { fired.push('super after view checked'); } - ngDoCheck() { fired.push('super do check'); } + ngOnInit() { + fired.push('super init'); + } + ngOnDestroy() { + fired.push('super destroy'); + } + ngAfterContentInit() { + fired.push('super after content init'); + } + ngAfterContentChecked() { + fired.push('super after content checked'); + } + ngAfterViewInit() { + fired.push('super after view init'); + } + ngAfterViewChecked() { + fired.push('super after view checked'); + } + ngDoCheck() { + fired.push('super do check'); + } } beforeEach(() => fired.length = 0); @@ -889,7 +943,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngOnInit() { fired.push('sub init'); } + ngOnInit() { + fired.push('sub init'); + } } @Component({ @@ -928,7 +984,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngDoCheck() { fired.push('sub do check'); } + ngDoCheck() { + fired.push('sub do check'); + } } @Component({ @@ -967,7 +1025,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterContentInit() { fired.push('sub after content init'); } + ngAfterContentInit() { + fired.push('sub after content init'); + } } @Component({ @@ -1006,7 +1066,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterContentChecked() { fired.push('sub after content checked'); } + ngAfterContentChecked() { + fired.push('sub after content checked'); + } } @Component({ @@ -1045,7 +1107,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterViewInit() { fired.push('sub after view init'); } + ngAfterViewInit() { + fired.push('sub after view init'); + } } @Component({ @@ -1084,7 +1148,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterViewChecked() { fired.push('sub after view checked'); } + ngAfterViewChecked() { + fired.push('sub after view checked'); + } } @Component({ @@ -1123,7 +1189,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngOnDestroy() { fired.push('sub destroy'); } + ngOnDestroy() { + fired.push('sub destroy'); + } } @Component({ @@ -1166,25 +1234,20 @@ describe('inheritance', () => { it('should inherit inputs', () => { @Directive({selector: '[super-dir]'}) class SuperDirective { - @Input() - foo = ''; + @Input() foo = ''; - @Input() - bar = ''; + @Input() bar = ''; - @Input() - baz = ''; + @Input() baz = ''; } @Directive({ selector: '[sub-dir]', }) class SubDirective extends SuperDirective { - @Input() - baz = ''; + @Input() baz = ''; - @Input() - qux = ''; + @Input() qux = ''; } @Component({template: `<p sub-dir [foo]="a" [bar]="b" [baz]="c" [qux]="d"></p>`}) @@ -1209,7 +1272,6 @@ describe('inheritance', () => { expect(subDir.baz).toBe('c'); expect(subDir.qux).toBe('d'); }); - }); describe('outputs', () => { @@ -1223,15 +1285,16 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @Output() - foo = new EventEmitter<string>(); + @Output() foo = new EventEmitter<string>(); } @Directive({ selector: '[sub-dir]', }) class SubDirective extends SuperDirective { - ngOnInit() { this.foo.emit('test'); } + ngOnInit() { + this.foo.emit('test'); + } } @Component({ @@ -1242,7 +1305,9 @@ describe('inheritance', () => { class App { foo = ''; - handleFoo(event: string) { this.foo = event; } + handleFoo(event: string) { + this.foo = event; + } } TestBed.configureTestingModule({ @@ -1264,11 +1329,9 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @HostBinding('style.color') - color = 'red'; + @HostBinding('style.color') color = 'red'; - @HostBinding('style.backgroundColor') - bg = 'black'; + @HostBinding('style.backgroundColor') bg = 'black'; } @Directive({ @@ -1307,10 +1370,11 @@ describe('inheritance', () => { }) class SuperDirective { @HostBinding('title') - get boundTitle() { return this.superTitle + '!!!'; } + get boundTitle() { + return this.superTitle + '!!!'; + } - @Input() - superTitle = ''; + @Input() superTitle = ''; } @Directive({ @@ -1348,15 +1412,16 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @ContentChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ContentChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Directive({ selector: '[sub-dir]', }) class SubDirective extends SuperDirective { - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -1376,7 +1441,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(2); + expect(foundQueryList!.length).toBe(2); }); xdescribe( @@ -1416,13 +1481,27 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperSuperDirective { - ngOnInit() { fired.push('super init'); } - ngOnDestroy() { fired.push('super destroy'); } - ngAfterContentInit() { fired.push('super after content init'); } - ngAfterContentChecked() { fired.push('super after content checked'); } - ngAfterViewInit() { fired.push('super after view init'); } - ngAfterViewChecked() { fired.push('super after view checked'); } - ngDoCheck() { fired.push('super do check'); } + ngOnInit() { + fired.push('super init'); + } + ngOnDestroy() { + fired.push('super destroy'); + } + ngAfterContentInit() { + fired.push('super after content init'); + } + ngAfterContentChecked() { + fired.push('super after content checked'); + } + ngAfterViewInit() { + fired.push('super after view init'); + } + ngAfterViewChecked() { + fired.push('super after view checked'); + } + ngDoCheck() { + fired.push('super do check'); + } } class SuperDirective extends SuperSuperDirective {} @@ -1434,7 +1513,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngOnInit() { fired.push('sub init'); } + ngOnInit() { + fired.push('sub init'); + } } @Component({ @@ -1473,7 +1554,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngDoCheck() { fired.push('sub do check'); } + ngDoCheck() { + fired.push('sub do check'); + } } @Component({ @@ -1512,7 +1595,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterContentInit() { fired.push('sub after content init'); } + ngAfterContentInit() { + fired.push('sub after content init'); + } } @Component({ @@ -1551,7 +1636,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterContentChecked() { fired.push('sub after content checked'); } + ngAfterContentChecked() { + fired.push('sub after content checked'); + } } @Component({ @@ -1590,7 +1677,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterViewInit() { fired.push('sub after view init'); } + ngAfterViewInit() { + fired.push('sub after view init'); + } } @Component({ @@ -1629,7 +1718,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngAfterViewChecked() { fired.push('sub after view checked'); } + ngAfterViewChecked() { + fired.push('sub after view checked'); + } } @Component({ @@ -1668,7 +1759,9 @@ describe('inheritance', () => { selector: '[subDir]', }) class SubDirective extends SuperDirective { - ngOnDestroy() { fired.push('sub destroy'); } + ngOnDestroy() { + fired.push('sub destroy'); + } } @Component({ @@ -1711,27 +1804,22 @@ describe('inheritance', () => { it('should inherit inputs', () => { @Directive({selector: '[super-dir]'}) class SuperSuperDirective { - @Input() - foo = ''; + @Input() foo = ''; - @Input() - baz = ''; + @Input() baz = ''; } class SuperDirective extends SuperSuperDirective { - @Input() - bar = ''; + @Input() bar = ''; } @Directive({ selector: '[sub-dir]', }) class SubDirective extends SuperDirective { - @Input() - baz = ''; + @Input() baz = ''; - @Input() - qux = ''; + @Input() qux = ''; } @Component({ @@ -1772,13 +1860,11 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperSuperDirective { - @Output() - foo = new EventEmitter<string>(); + @Output() foo = new EventEmitter<string>(); } class SuperDirective extends SuperSuperDirective { - @Output() - bar = new EventEmitter<string>(); + @Output() bar = new EventEmitter<string>(); } @Directive({ @@ -1801,9 +1887,13 @@ describe('inheritance', () => { bar = ''; - handleFoo(event: string) { this.foo = event; } + handleFoo(event: string) { + this.foo = event; + } - handleBar(event: string) { this.bar = event; } + handleBar(event: string) { + this.bar = event; + } } TestBed.configureTestingModule({ @@ -1826,13 +1916,11 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperSuperDirective { - @HostBinding('style.color') - color = 'red'; + @HostBinding('style.color') color = 'red'; } class SuperDirective extends SuperSuperDirective { - @HostBinding('style.backgroundColor') - bg = 'black'; + @HostBinding('style.backgroundColor') bg = 'black'; } @Directive({ @@ -1871,18 +1959,20 @@ describe('inheritance', () => { }) class SuperSuperDirective { @HostBinding('title') - get boundTitle() { return this.superTitle + '!!!'; } + get boundTitle() { + return this.superTitle + '!!!'; + } - @Input() - superTitle = ''; + @Input() superTitle = ''; } class SuperDirective extends SuperSuperDirective { @HostBinding('accessKey') - get boundAltKey() { return this.superAccessKey + '???'; } + get boundAltKey() { + return this.superAccessKey + '???'; + } - @Input() - superAccessKey = ''; + @Input() superAccessKey = ''; } @Directive({ @@ -1927,13 +2017,11 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperSuperDirective { - @ContentChildren(ChildDir1) - childDir1s !: QueryList<ChildDir1>; + @ContentChildren(ChildDir1) childDir1s!: QueryList<ChildDir1>; } class SuperDirective extends SuperSuperDirective { - @ContentChildren(ChildDir1) - childDir2s !: QueryList<ChildDir2>; + @ContentChildren(ChildDir1) childDir2s!: QueryList<ChildDir2>; } @Directive({ @@ -1964,8 +2052,8 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundChildDir1s !.length).toBe(2); - expect(foundChildDir2s !.length).toBe(2); + expect(foundChildDir1s!.length).toBe(2); + expect(foundChildDir2s!.length).toBe(2); }); xdescribe( @@ -2002,13 +2090,27 @@ describe('inheritance', () => { const fired: string[] = []; class SuperComponent { - ngOnInit() { fired.push('super init'); } - ngOnDestroy() { fired.push('super destroy'); } - ngAfterContentInit() { fired.push('super after content init'); } - ngAfterContentChecked() { fired.push('super after content checked'); } - ngAfterViewInit() { fired.push('super after view init'); } - ngAfterViewChecked() { fired.push('super after view checked'); } - ngDoCheck() { fired.push('super do check'); } + ngOnInit() { + fired.push('super init'); + } + ngOnDestroy() { + fired.push('super destroy'); + } + ngAfterContentInit() { + fired.push('super after content init'); + } + ngAfterContentChecked() { + fired.push('super after content checked'); + } + ngAfterViewInit() { + fired.push('super after view init'); + } + ngAfterViewChecked() { + fired.push('super after view checked'); + } + ngDoCheck() { + fired.push('super do check'); + } } beforeEach(() => fired.length = 0); @@ -2016,7 +2118,9 @@ describe('inheritance', () => { it('ngOnInit', () => { @Component({selector: 'my-comp', template: `<p>test</p>`}) class MyComponent extends SuperComponent { - ngOnInit() { fired.push('sub init'); } + ngOnInit() { + fired.push('sub init'); + } } @Component({ @@ -2055,7 +2159,9 @@ describe('inheritance', () => { selector: 'my-comp', }) class MyComponent extends SuperComponent { - ngDoCheck() { fired.push('sub do check'); } + ngDoCheck() { + fired.push('sub do check'); + } } @Component({ @@ -2095,7 +2201,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterContentInit() { fired.push('sub after content init'); } + ngAfterContentInit() { + fired.push('sub after content init'); + } } @Component({ @@ -2135,7 +2243,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterContentChecked() { fired.push('sub after content checked'); } + ngAfterContentChecked() { + fired.push('sub after content checked'); + } } @Component({ @@ -2175,7 +2285,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterViewInit() { fired.push('sub after view init'); } + ngAfterViewInit() { + fired.push('sub after view init'); + } } @Component({ @@ -2215,7 +2327,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterViewChecked() { fired.push('sub after view checked'); } + ngAfterViewChecked() { + fired.push('sub after view checked'); + } } @Component({ @@ -2255,7 +2369,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngOnDestroy() { fired.push('sub destroy'); } + ngOnDestroy() { + fired.push('sub destroy'); + } } @Component({ @@ -2297,23 +2413,18 @@ describe('inheritance', () => { it('should inherit inputs', () => { class SuperComponent { - @Input() - foo = ''; + @Input() foo = ''; - @Input() - bar = ''; + @Input() bar = ''; - @Input() - baz = ''; + @Input() baz = ''; } @Component({selector: 'my-comp', template: `<p>test</p>`}) class MyComponent extends SuperComponent { - @Input() - baz = ''; + @Input() baz = ''; - @Input() - qux = ''; + @Input() qux = ''; } @Component({template: `<my-comp [foo]="a" [bar]="b" [baz]="c" [qux]="d"></my-comp>`}) @@ -2338,7 +2449,6 @@ describe('inheritance', () => { expect(subDir.baz).toEqual('c'); expect(subDir.qux).toEqual('d'); }); - }); describe('outputs', () => { @@ -2349,8 +2459,7 @@ describe('inheritance', () => { it('should inherit outputs', () => { class SuperComponent { - @Output() - foo = new EventEmitter<string>(); + @Output() foo = new EventEmitter<string>(); } @Component({ @@ -2358,7 +2467,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngOnInit() { this.foo.emit('test'); } + ngOnInit() { + this.foo.emit('test'); + } } @Component({ @@ -2369,7 +2480,9 @@ describe('inheritance', () => { class App { foo = ''; - handleFoo(event: string) { this.foo = event; } + handleFoo(event: string) { + this.foo = event; + } } TestBed.configureTestingModule({ @@ -2388,11 +2501,9 @@ describe('inheritance', () => { // TODO: sub and super HostBinding same binding on two different properties it('should compose host bindings for styles', () => { class SuperComponent { - @HostBinding('style.color') - color = 'red'; + @HostBinding('style.color') color = 'red'; - @HostBinding('style.backgroundColor') - bg = 'black'; + @HostBinding('style.backgroundColor') bg = 'black'; } @Component({ @@ -2429,10 +2540,11 @@ describe('inheritance', () => { it('should compose host bindings (non-style related)', () => { class SuperComponent { @HostBinding('title') - get boundTitle() { return this.superTitle + '!!!'; } + get boundTitle() { + return this.superTitle + '!!!'; + } - @Input() - superTitle = ''; + @Input() superTitle = ''; } @Component({ @@ -2468,13 +2580,14 @@ describe('inheritance', () => { } class SuperComponent { - @ContentChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ContentChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Component({selector: 'my-comp', template: `<ul><ng-content></ng-content></ul>`}) class MyComponent extends SuperComponent { - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -2494,7 +2607,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(2); + expect(foundQueryList!.length).toBe(2); }); xdescribe( @@ -2534,13 +2647,27 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - ngOnInit() { fired.push('super init'); } - ngOnDestroy() { fired.push('super destroy'); } - ngAfterContentInit() { fired.push('super after content init'); } - ngAfterContentChecked() { fired.push('super after content checked'); } - ngAfterViewInit() { fired.push('super after view init'); } - ngAfterViewChecked() { fired.push('super after view checked'); } - ngDoCheck() { fired.push('super do check'); } + ngOnInit() { + fired.push('super init'); + } + ngOnDestroy() { + fired.push('super destroy'); + } + ngAfterContentInit() { + fired.push('super after content init'); + } + ngAfterContentChecked() { + fired.push('super after content checked'); + } + ngAfterViewInit() { + fired.push('super after view init'); + } + ngAfterViewChecked() { + fired.push('super after view checked'); + } + ngDoCheck() { + fired.push('super do check'); + } } beforeEach(() => fired.length = 0); @@ -2548,7 +2675,9 @@ describe('inheritance', () => { it('ngOnInit', () => { @Component({selector: 'my-comp', template: `<p>test</p>`}) class MyComponent extends SuperDirective { - ngOnInit() { fired.push('sub init'); } + ngOnInit() { + fired.push('sub init'); + } } @Component({ @@ -2588,7 +2717,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperDirective { - ngDoCheck() { fired.push('sub do check'); } + ngDoCheck() { + fired.push('sub do check'); + } } @Component({ @@ -2628,7 +2759,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperDirective { - ngAfterContentInit() { fired.push('sub after content init'); } + ngAfterContentInit() { + fired.push('sub after content init'); + } } @Component({ @@ -2668,7 +2801,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperDirective { - ngAfterContentChecked() { fired.push('sub after content checked'); } + ngAfterContentChecked() { + fired.push('sub after content checked'); + } } @Component({ @@ -2708,7 +2843,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperDirective { - ngAfterViewInit() { fired.push('sub after view init'); } + ngAfterViewInit() { + fired.push('sub after view init'); + } } @Component({ @@ -2748,7 +2885,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperDirective { - ngAfterViewChecked() { fired.push('sub after view checked'); } + ngAfterViewChecked() { + fired.push('sub after view checked'); + } } @Component({ @@ -2788,7 +2927,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperDirective { - ngOnDestroy() { fired.push('sub destroy'); } + ngOnDestroy() { + fired.push('sub destroy'); + } } @Component({ @@ -2833,23 +2974,18 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @Input() - foo = ''; + @Input() foo = ''; - @Input() - bar = ''; + @Input() bar = ''; - @Input() - baz = ''; + @Input() baz = ''; } @Component({selector: 'my-comp', template: `<p>test</p>`}) class MyComponent extends SuperDirective { - @Input() - baz = ''; + @Input() baz = ''; - @Input() - qux = ''; + @Input() qux = ''; } @Component({template: `<my-comp [foo]="a" [bar]="b" [baz]="c" [qux]="d"></my-comp>`}) @@ -2887,8 +3023,7 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @Output() - foo = new EventEmitter<string>(); + @Output() foo = new EventEmitter<string>(); } @Component({ @@ -2896,7 +3031,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperDirective { - ngOnInit() { this.foo.emit('test'); } + ngOnInit() { + this.foo.emit('test'); + } } @Component({ @@ -2907,7 +3044,9 @@ describe('inheritance', () => { class App { foo = ''; - handleFoo(event: string) { this.foo = event; } + handleFoo(event: string) { + this.foo = event; + } } TestBed.configureTestingModule({ @@ -2929,11 +3068,9 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @HostBinding('style.color') - color = 'red'; + @HostBinding('style.color') color = 'red'; - @HostBinding('style.backgroundColor') - bg = 'black'; + @HostBinding('style.backgroundColor') bg = 'black'; } @Component({ @@ -2973,10 +3110,11 @@ describe('inheritance', () => { }) class SuperDirective { @HostBinding('title') - get boundTitle() { return this.superTitle + '!!!'; } + get boundTitle() { + return this.superTitle + '!!!'; + } - @Input() - superTitle = ''; + @Input() superTitle = ''; } @Component({ @@ -3015,13 +3153,14 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @ContentChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ContentChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Component({selector: 'my-comp', template: `<ul><ng-content></ng-content></ul>`}) class MyComponent extends SuperDirective { - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -3041,7 +3180,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(2); + expect(foundQueryList!.length).toBe(2); }); it('should inherit ViewChildren queries', () => { @@ -3055,8 +3194,7 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @ViewChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ViewChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Component({ @@ -3069,7 +3207,9 @@ describe('inheritance', () => { }) class MyComponent extends SuperDirective { items = [1, 2, 3, 4, 5]; - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -3086,7 +3226,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(5); + expect(foundQueryList!.length).toBe(5); }); xdescribe( @@ -3126,13 +3266,27 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - ngOnInit() { fired.push('super init'); } - ngOnDestroy() { fired.push('super destroy'); } - ngAfterContentInit() { fired.push('super after content init'); } - ngAfterContentChecked() { fired.push('super after content checked'); } - ngAfterViewInit() { fired.push('super after view init'); } - ngAfterViewChecked() { fired.push('super after view checked'); } - ngDoCheck() { fired.push('super do check'); } + ngOnInit() { + fired.push('super init'); + } + ngOnDestroy() { + fired.push('super destroy'); + } + ngAfterContentInit() { + fired.push('super after content init'); + } + ngAfterContentChecked() { + fired.push('super after content checked'); + } + ngAfterViewInit() { + fired.push('super after view init'); + } + ngAfterViewChecked() { + fired.push('super after view checked'); + } + ngDoCheck() { + fired.push('super do check'); + } } class BareClass extends SuperDirective {} @@ -3142,7 +3296,9 @@ describe('inheritance', () => { it('ngOnInit', () => { @Component({selector: 'my-comp', template: `<p>test</p>`}) class MyComponent extends BareClass { - ngOnInit() { fired.push('sub init'); } + ngOnInit() { + fired.push('sub init'); + } } @Component({ @@ -3181,7 +3337,9 @@ describe('inheritance', () => { selector: 'my-comp', }) class MyComponent extends BareClass { - ngDoCheck() { fired.push('sub do check'); } + ngDoCheck() { + fired.push('sub do check'); + } } @Component({ @@ -3221,7 +3379,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends BareClass { - ngAfterContentInit() { fired.push('sub after content init'); } + ngAfterContentInit() { + fired.push('sub after content init'); + } } @Component({ @@ -3261,7 +3421,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends BareClass { - ngAfterContentChecked() { fired.push('sub after content checked'); } + ngAfterContentChecked() { + fired.push('sub after content checked'); + } } @Component({ @@ -3301,7 +3463,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends BareClass { - ngAfterViewInit() { fired.push('sub after view init'); } + ngAfterViewInit() { + fired.push('sub after view init'); + } } @Component({ @@ -3341,7 +3505,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends BareClass { - ngAfterViewChecked() { fired.push('sub after view checked'); } + ngAfterViewChecked() { + fired.push('sub after view checked'); + } } @Component({ @@ -3381,7 +3547,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends BareClass { - ngOnDestroy() { fired.push('sub destroy'); } + ngOnDestroy() { + fired.push('sub destroy'); + } } @Component({ @@ -3426,25 +3594,20 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @Input() - foo = ''; + @Input() foo = ''; - @Input() - baz = ''; + @Input() baz = ''; } class BareClass extends SuperDirective { - @Input() - bar = ''; + @Input() bar = ''; } @Component({selector: 'my-comp', template: `<p>test</p>`}) class MyComponent extends BareClass { - @Input() - baz = ''; + @Input() baz = ''; - @Input() - qux = ''; + @Input() qux = ''; } @Component({template: `<my-comp [foo]="a" [bar]="b" [baz]="c" [qux]="d"></my-comp>`}) @@ -3482,8 +3645,7 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @Output() - foo = new EventEmitter<string>(); + @Output() foo = new EventEmitter<string>(); } class BareClass extends SuperDirective {} @@ -3493,7 +3655,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends BareClass { - ngOnInit() { this.foo.emit('test'); } + ngOnInit() { + this.foo.emit('test'); + } } @Component({ @@ -3504,7 +3668,9 @@ describe('inheritance', () => { class App { foo = ''; - handleFoo(event: string) { this.foo = event; } + handleFoo(event: string) { + this.foo = event; + } } TestBed.configureTestingModule({ @@ -3526,11 +3692,9 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @HostBinding('style.color') - color = 'red'; + @HostBinding('style.color') color = 'red'; - @HostBinding('style.backgroundColor') - bg = 'black'; + @HostBinding('style.backgroundColor') bg = 'black'; } class BareClass extends SuperDirective {} @@ -3572,18 +3736,20 @@ describe('inheritance', () => { }) class SuperDirective { @HostBinding('title') - get boundTitle() { return this.superTitle + '!!!'; } + get boundTitle() { + return this.superTitle + '!!!'; + } - @Input() - superTitle = ''; + @Input() superTitle = ''; } class BareClass extends SuperDirective { @HostBinding('accessKey') - get boundAccessKey() { return this.superAccessKey + '???'; } + get boundAccessKey() { + return this.superAccessKey + '???'; + } - @Input() - superAccessKey = ''; + @Input() superAccessKey = ''; } @Component({ @@ -3623,8 +3789,7 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @ContentChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ContentChildren(ChildDir) customDirs!: QueryList<ChildDir>; } class BareClass extends SuperDirective {} @@ -3634,7 +3799,9 @@ describe('inheritance', () => { template: `<ul><ng-content></ng-content></ul>`, }) class MyComponent extends BareClass { - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -3654,7 +3821,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(2); + expect(foundQueryList!.length).toBe(2); }); it('should inherit ViewChildren queries', () => { @@ -3668,8 +3835,7 @@ describe('inheritance', () => { selector: '[super-dir]', }) class SuperDirective { - @ViewChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ViewChildren(ChildDir) customDirs!: QueryList<ChildDir>; } class BareClass extends SuperDirective {} @@ -3684,7 +3850,9 @@ describe('inheritance', () => { }) class MyComponent extends BareClass { items = [1, 2, 3, 4, 5]; - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -3701,7 +3869,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(5); + expect(foundQueryList!.length).toBe(5); }); xdescribe( @@ -3742,13 +3910,27 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperComponent { - ngOnInit() { fired.push('super init'); } - ngOnDestroy() { fired.push('super destroy'); } - ngAfterContentInit() { fired.push('super after content init'); } - ngAfterContentChecked() { fired.push('super after content checked'); } - ngAfterViewInit() { fired.push('super after view init'); } - ngAfterViewChecked() { fired.push('super after view checked'); } - ngDoCheck() { fired.push('super do check'); } + ngOnInit() { + fired.push('super init'); + } + ngOnDestroy() { + fired.push('super destroy'); + } + ngAfterContentInit() { + fired.push('super after content init'); + } + ngAfterContentChecked() { + fired.push('super after content checked'); + } + ngAfterViewInit() { + fired.push('super after view init'); + } + ngAfterViewChecked() { + fired.push('super after view checked'); + } + ngDoCheck() { + fired.push('super do check'); + } } beforeEach(() => fired.length = 0); @@ -3759,7 +3941,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngOnInit() { fired.push('sub init'); } + ngOnInit() { + fired.push('sub init'); + } } @Component({ @@ -3799,7 +3983,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngDoCheck() { fired.push('sub do check'); } + ngDoCheck() { + fired.push('sub do check'); + } } @Component({ @@ -3839,7 +4025,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterContentInit() { fired.push('sub after content init'); } + ngAfterContentInit() { + fired.push('sub after content init'); + } } @Component({ @@ -3879,7 +4067,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterContentChecked() { fired.push('sub after content checked'); } + ngAfterContentChecked() { + fired.push('sub after content checked'); + } } @Component({ @@ -3919,7 +4109,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterViewInit() { fired.push('sub after view init'); } + ngAfterViewInit() { + fired.push('sub after view init'); + } } @Component({ @@ -3959,7 +4151,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterViewChecked() { fired.push('sub after view checked'); } + ngAfterViewChecked() { + fired.push('sub after view checked'); + } } @Component({ @@ -3999,7 +4193,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngOnDestroy() { fired.push('sub destroy'); } + ngOnDestroy() { + fired.push('sub destroy'); + } } @Component({ @@ -4045,23 +4241,18 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperComponent { - @Input() - foo = ''; + @Input() foo = ''; - @Input() - bar = ''; + @Input() bar = ''; - @Input() - baz = ''; + @Input() baz = ''; } @Component({selector: 'my-comp', template: `<p>test</p>`}) class MyComponent extends SuperComponent { - @Input() - baz = ''; + @Input() baz = ''; - @Input() - qux = ''; + @Input() qux = ''; } @Component({template: `<my-comp [foo]="a" [bar]="b" [baz]="c" [qux]="d"></my-comp>`}) @@ -4100,8 +4291,7 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperComponent { - @Output() - foo = new EventEmitter<string>(); + @Output() foo = new EventEmitter<string>(); } @Component({ @@ -4109,7 +4299,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngOnInit() { this.foo.emit('test'); } + ngOnInit() { + this.foo.emit('test'); + } } @Component({ @@ -4120,7 +4312,9 @@ describe('inheritance', () => { class App { foo = ''; - handleFoo(event: string) { this.foo = event; } + handleFoo(event: string) { + this.foo = event; + } } TestBed.configureTestingModule({ @@ -4236,11 +4430,9 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperComponent { - @HostBinding('style.color') - color = 'red'; + @HostBinding('style.color') color = 'red'; - @HostBinding('style.backgroundColor') - bg = 'black'; + @HostBinding('style.backgroundColor') bg = 'black'; } @Component({ @@ -4281,10 +4473,11 @@ describe('inheritance', () => { }) class SuperComponent { @HostBinding('title') - get boundTitle() { return this.superTitle + '!!!'; } + get boundTitle() { + return this.superTitle + '!!!'; + } - @Input() - superTitle = ''; + @Input() superTitle = ''; } @Component({ @@ -4324,8 +4517,7 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperComponent { - @ContentChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ContentChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Component({ @@ -4333,7 +4525,9 @@ describe('inheritance', () => { template: `<ul><ng-content></ng-content></ul>`, }) class MyComponent extends SuperComponent { - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -4353,7 +4547,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(2); + expect(foundQueryList!.length).toBe(2); }); it('should inherit ViewChildren queries', () => { @@ -4368,8 +4562,7 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperComponent { - @ViewChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ViewChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Component({ @@ -4382,7 +4575,9 @@ describe('inheritance', () => { }) class MyComponent extends SuperComponent { items = [1, 2, 3, 4, 5]; - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -4399,7 +4594,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(5); + expect(foundQueryList!.length).toBe(5); }); it('should inherit host listeners from base class once', () => { @@ -4411,7 +4606,9 @@ describe('inheritance', () => { }) class BaseComponent { @HostListener('click') - clicked() { events.push('BaseComponent.clicked'); } + clicked() { + events.push('BaseComponent.clicked'); + } } @Component({ @@ -4423,9 +4620,12 @@ describe('inheritance', () => { // component def, which would trigger `hostBindings` functions merge operation in // InheritDefinitionFeature logic (merging Child and Base host binding functions) @HostListener('focus') - focused() {} + focused() { + } - clicked() { events.push('ChildComponent.clicked'); } + clicked() { + events.push('ChildComponent.clicked'); + } } @Component({ @@ -4437,9 +4637,12 @@ describe('inheritance', () => { // component def, which would trigger `hostBindings` functions merge operation in // InheritDefinitionFeature logic (merging GrandChild and Child host binding functions) @HostListener('blur') - blurred() {} + blurred() { + } - clicked() { events.push('GrandChildComponent.clicked'); } + clicked() { + events.push('GrandChildComponent.clicked'); + } } @Component({ @@ -4505,13 +4708,27 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperSuperComponent { - ngOnInit() { fired.push('super init'); } - ngOnDestroy() { fired.push('super destroy'); } - ngAfterContentInit() { fired.push('super after content init'); } - ngAfterContentChecked() { fired.push('super after content checked'); } - ngAfterViewInit() { fired.push('super after view init'); } - ngAfterViewChecked() { fired.push('super after view checked'); } - ngDoCheck() { fired.push('super do check'); } + ngOnInit() { + fired.push('super init'); + } + ngOnDestroy() { + fired.push('super destroy'); + } + ngAfterContentInit() { + fired.push('super after content init'); + } + ngAfterContentChecked() { + fired.push('super after content checked'); + } + ngAfterViewInit() { + fired.push('super after view init'); + } + ngAfterViewChecked() { + fired.push('super after view checked'); + } + ngDoCheck() { + fired.push('super do check'); + } } class SuperComponent extends SuperSuperComponent {} @@ -4524,7 +4741,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngOnInit() { fired.push('sub init'); } + ngOnInit() { + fired.push('sub init'); + } } @Component({ @@ -4564,7 +4783,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngDoCheck() { fired.push('sub do check'); } + ngDoCheck() { + fired.push('sub do check'); + } } @Component({ @@ -4604,7 +4825,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterContentInit() { fired.push('sub after content init'); } + ngAfterContentInit() { + fired.push('sub after content init'); + } } @Component({ @@ -4644,7 +4867,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterContentChecked() { fired.push('sub after content checked'); } + ngAfterContentChecked() { + fired.push('sub after content checked'); + } } @Component({ @@ -4684,7 +4909,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterViewInit() { fired.push('sub after view init'); } + ngAfterViewInit() { + fired.push('sub after view init'); + } } @Component({ @@ -4724,7 +4951,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngAfterViewChecked() { fired.push('sub after view checked'); } + ngAfterViewChecked() { + fired.push('sub after view checked'); + } } @Component({ @@ -4764,7 +4993,9 @@ describe('inheritance', () => { template: `<p>test</p>`, }) class MyComponent extends SuperComponent { - ngOnDestroy() { fired.push('sub destroy'); } + ngOnDestroy() { + fired.push('sub destroy'); + } } @Component({ @@ -4810,25 +5041,20 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperSuperComponent { - @Input() - foo = ''; + @Input() foo = ''; - @Input() - baz = ''; + @Input() baz = ''; } class BareClass extends SuperSuperComponent { - @Input() - bar = ''; + @Input() bar = ''; } @Component({selector: 'my-comp', template: `<p>test</p>`}) class MyComponent extends BareClass { - @Input() - baz = ''; + @Input() baz = ''; - @Input() - qux = ''; + @Input() qux = ''; } @Component({template: `<my-comp [foo]="a" [bar]="b" [baz]="c" [qux]="d"></my-comp>`}) @@ -4867,13 +5093,11 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperSuperComponent { - @Output() - foo = new EventEmitter<string>(); + @Output() foo = new EventEmitter<string>(); } class SuperComponent extends SuperSuperComponent { - @Output() - bar = new EventEmitter<string>(); + @Output() bar = new EventEmitter<string>(); } @Component({ @@ -4895,11 +5119,15 @@ describe('inheritance', () => { class App { foo = ''; - handleFoo(event: string) { this.foo = event; } + handleFoo(event: string) { + this.foo = event; + } bar = ''; - handleBar(event: string) { this.bar = event; } + handleBar(event: string) { + this.bar = event; + } } TestBed.configureTestingModule({ @@ -4987,13 +5215,11 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperSuperComponent { - @HostBinding('style.color') - color = 'red'; + @HostBinding('style.color') color = 'red'; } class SuperComponent extends SuperSuperComponent { - @HostBinding('style.backgroundColor') - bg = 'black'; + @HostBinding('style.backgroundColor') bg = 'black'; } @Component({ @@ -5034,18 +5260,20 @@ describe('inheritance', () => { }) class SuperSuperComponent { @HostBinding('title') - get boundTitle() { return this.superTitle + '!!!'; } + get boundTitle() { + return this.superTitle + '!!!'; + } - @Input() - superTitle = ''; + @Input() superTitle = ''; } class SuperComponent extends SuperSuperComponent { @HostBinding('accessKey') - get boundAccessKey() { return this.superAccessKey + '???'; } + get boundAccessKey() { + return this.superAccessKey + '???'; + } - @Input() - superAccessKey = ''; + @Input() superAccessKey = ''; } @Component({ @@ -5087,8 +5315,7 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperComponent { - @ContentChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ContentChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Component({ @@ -5096,7 +5323,9 @@ describe('inheritance', () => { template: `<ul><ng-content></ng-content></ul>`, }) class MyComponent extends SuperComponent { - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -5116,7 +5345,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(2); + expect(foundQueryList!.length).toBe(2); }); it('should inherit ViewChildren queries', () => { @@ -5131,8 +5360,7 @@ describe('inheritance', () => { template: `<p>super</p>`, }) class SuperComponent { - @ViewChildren(ChildDir) - customDirs !: QueryList<ChildDir>; + @ViewChildren(ChildDir) customDirs!: QueryList<ChildDir>; } @Component({ @@ -5145,7 +5373,9 @@ describe('inheritance', () => { }) class MyComponent extends SuperComponent { items = [1, 2, 3, 4, 5]; - ngAfterViewInit() { foundQueryList = this.customDirs; } + ngAfterViewInit() { + foundQueryList = this.customDirs; + } } @Component({ @@ -5162,7 +5392,7 @@ describe('inheritance', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); - expect(foundQueryList !.length).toBe(5); + expect(foundQueryList!.length).toBe(5); }); xdescribe( diff --git a/packages/core/test/acceptance/integration_spec.ts b/packages/core/test/acceptance/integration_spec.ts index d53825871c82b..164550bdf5ca0 100644 --- a/packages/core/test/acceptance/integration_spec.ts +++ b/packages/core/test/acceptance/integration_spec.ts @@ -16,10 +16,11 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; describe('acceptance integration tests', () => { - function stripHtmlComments(str: string) { return str.replace(/<!--[\s\S]*?-->/g, ''); } + function stripHtmlComments(str: string) { + return str.replace(/<!--[\s\S]*?-->/g, ''); + } describe('render', () => { - it('should render basic template', () => { @Component({template: '<span title="Hello">Greetings</span>'}) class App { @@ -64,12 +65,10 @@ describe('acceptance integration tests', () => { tView: 2, tNode: 4, }); - }); }); describe('ng-container', () => { - it('should insert as a child of a regular element', () => { @Component( {template: '<div>before|<ng-container>Greetings<span></span></ng-container>|after</div>'}) @@ -108,7 +107,6 @@ describe('acceptance integration tests', () => { }); it('should add and remove DOM nodes when ng-container is a child of an embedded view', () => { - @Component({template: '<ng-container *ngIf="render">content</ng-container>'}) class App { render = false; @@ -131,21 +129,24 @@ describe('acceptance integration tests', () => { // https://stackblitz.com/edit/angular-tfhcz1?file=src%2Fapp%2Fapp.component.ts it('should add and remove DOM nodes when ng-container is a child of a delayed embedded view', () => { - @Directive({selector: '[testDirective]'}) class TestDirective { constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {} - createAndInsert() { this._vcRef.insert(this._tplRef.createEmbeddedView({})); } + createAndInsert() { + this._vcRef.insert(this._tplRef.createEmbeddedView({})); + } - clear() { this._vcRef.clear(); } + clear() { + this._vcRef.clear(); + } } @Component({ template: '<ng-template testDirective><ng-container>content</ng-container></ng-template>' }) class App { - @ViewChild(TestDirective, {static: true}) testDirective !: TestDirective; + @ViewChild(TestDirective, {static: true}) testDirective!: TestDirective; } TestBed.configureTestingModule({declarations: [App, TestDirective]}); @@ -205,9 +206,13 @@ describe('acceptance integration tests', () => { class TestDirective { constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {} - createAndInsert() { this._vcRef.insert(this._tplRef.createEmbeddedView({})); } + createAndInsert() { + this._vcRef.insert(this._tplRef.createEmbeddedView({})); + } - clear() { this._vcRef.clear(); } + clear() { + this._vcRef.clear(); + } } @Component({ @@ -215,7 +220,7 @@ describe('acceptance integration tests', () => { '<ng-template testDirective><ng-container><ng-container><ng-container>content</ng-container></ng-container></ng-container></ng-template>' }) class App { - @ViewChild(TestDirective, {static: true}) testDirective !: TestDirective; + @ViewChild(TestDirective, {static: true}) testDirective!: TestDirective; } TestBed.configureTestingModule({declarations: [App, TestDirective]}); @@ -245,7 +250,7 @@ describe('acceptance integration tests', () => { @Component({template: '<div><ng-container dir></ng-container></div>'}) class App { - @ViewChild(TestDirective) testDirective !: TestDirective; + @ViewChild(TestDirective) testDirective!: TestDirective; } TestBed.configureTestingModule({declarations: [App, TestDirective]}); @@ -260,14 +265,17 @@ describe('acceptance integration tests', () => { it('should support ViewContainerRef when ng-container is at the root of a view', () => { @Directive({selector: '[dir]'}) class TestDirective { - @Input() - contentTpl: TemplateRef<{}>|null = null; + @Input() contentTpl: TemplateRef<{}>|null = null; constructor(private _vcRef: ViewContainerRef) {} - insertView() { this._vcRef.createEmbeddedView(this.contentTpl as TemplateRef<{}>); } + insertView() { + this._vcRef.createEmbeddedView(this.contentTpl as TemplateRef<{}>); + } - clear() { this._vcRef.clear(); } + clear() { + this._vcRef.clear(); + } } @Component({ @@ -275,7 +283,7 @@ describe('acceptance integration tests', () => { '<ng-container dir [contentTpl]="content"><ng-template #content>Content</ng-template></ng-container>' }) class App { - @ViewChild(TestDirective) testDirective !: TestDirective; + @ViewChild(TestDirective) testDirective!: TestDirective; } TestBed.configureTestingModule({declarations: [App, TestDirective]}); @@ -298,14 +306,18 @@ describe('acceptance integration tests', () => { class TestDirective { constructor(private _tplRef: TemplateRef<{}>, private _vcRef: ViewContainerRef) {} - insertView() { this._vcRef.createEmbeddedView(this._tplRef); } + insertView() { + this._vcRef.createEmbeddedView(this._tplRef); + } - clear() { this._vcRef.clear(); } + clear() { + this._vcRef.clear(); + } } @Component({template: '<ng-container><ng-template dir>Content</ng-template></ng-container>'}) class App { - @ViewChild(TestDirective) testDirective !: TestDirective; + @ViewChild(TestDirective) testDirective!: TestDirective; } TestBed.configureTestingModule({declarations: [App, TestDirective]}); @@ -334,7 +346,6 @@ describe('acceptance integration tests', () => { expect(stripHtmlComments(fixture.nativeElement.innerHTML)).toEqual('<div></div>'); }); - }); describe('text bindings', () => { @@ -373,11 +384,12 @@ describe('acceptance integration tests', () => { expect(fixture.nativeElement.innerHTML).toEqual(''); }); - }); describe('ngNonBindable handling', () => { - function stripNgNonBindable(str: string) { return str.replace(/ ngnonbindable=""/i, ''); } + function stripNgNonBindable(str: string) { + return str.replace(/ ngnonbindable=""/i, ''); + } it('should keep local ref for host element', () => { @Component({ @@ -404,7 +416,9 @@ describe('acceptance integration tests', () => { @Directive({selector: '[directive]'}) class TestDirective implements OnInit { - ngOnInit() { directiveInvoked = true; } + ngOnInit() { + directiveInvoked = true; + } } @Component({ @@ -432,7 +446,9 @@ describe('acceptance integration tests', () => { @Directive({selector: '[directive]'}) class TestDirective implements OnInit { - ngOnInit() { directiveInvoked = true; } + ngOnInit() { + directiveInvoked = true; + } } @Component({ @@ -604,14 +620,12 @@ describe('acceptance integration tests', () => { it('should support a component with binding on host element', () => { @Component({selector: 'todo', template: '{{title}}'}) class TodoComponentHostBinding { - @HostBinding() - title = 'one'; + @HostBinding() title = 'one'; } @Component({template: '<todo></todo>'}) class App { - @ViewChild(TodoComponentHostBinding) - todoComponentHostBinding !: TodoComponentHostBinding; + @ViewChild(TodoComponentHostBinding) todoComponentHostBinding!: TodoComponentHostBinding; } TestBed.configureTestingModule({declarations: [App, TodoComponentHostBinding]}); @@ -658,8 +672,7 @@ describe('acceptance integration tests', () => { it('should support a component with sub-views', () => { @Component({selector: 'comp', template: '<div *ngIf="condition">text</div>'}) class MyComp { - @Input() - condition !: boolean; + @Input() condition!: boolean; } @Component({template: '<comp [condition]="condition"></comp>'}) @@ -680,7 +693,6 @@ describe('acceptance integration tests', () => { fixture.detectChanges(); expect(stripHtmlComments(compElement.innerHTML)).toEqual(''); }); - }); describe('element bindings', () => { @@ -814,7 +826,7 @@ describe('acceptance integration tests', () => { fixture.detectChanges(); const span: HTMLSpanElement = fixture.nativeElement.querySelector('span'); - const bold: HTMLElement = span.querySelector('b') !; + const bold: HTMLElement = span.querySelector('b')!; fixture.componentInstance.title = 'Hello'; fixture.detectChanges(); @@ -840,13 +852,12 @@ describe('acceptance integration tests', () => { it('should support host attribute bindings', () => { @Directive({selector: '[hostBindingDir]'}) class HostBindingDir { - @HostBinding('attr.aria-label') - label = 'some label'; + @HostBinding('attr.aria-label') label = 'some label'; } @Component({template: '<div hostBindingDir></div>'}) class App { - @ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir; + @ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir; } TestBed.configureTestingModule({declarations: [App, HostBindingDir]}); @@ -999,12 +1010,13 @@ describe('acceptance integration tests', () => { it('should apply classes properly when nodes have containers', () => { @Component({selector: 'structural-comp', template: 'Comp Content'}) class StructuralComp { - @Input() - tmp !: TemplateRef<any>; + @Input() tmp!: TemplateRef<any>; constructor(public vcr: ViewContainerRef) {} - create() { this.vcr.createEmbeddedView(this.tmp); } + create() { + this.vcr.createEmbeddedView(this.tmp); + } } @Component({ @@ -1014,7 +1026,7 @@ describe('acceptance integration tests', () => { ` }) class App { - @ViewChild(StructuralComp) structuralComp !: StructuralComp; + @ViewChild(StructuralComp) structuralComp!: StructuralComp; value: any; } @@ -1040,7 +1052,9 @@ describe('acceptance integration tests', () => { public classesVal: string = ''; @Input('class') - set klass(value: string) { this.classesVal = value; } + set klass(value: string) { + this.classesVal = value; + } } @Directive({selector: '[DirWithStyle]'}) @@ -1048,15 +1062,16 @@ describe('acceptance integration tests', () => { public stylesVal: any = ''; @Input() - set style(value: any) { this.stylesVal = value; } + set style(value: any) { + this.stylesVal = value; + } } it('should delegate initial classes to a [class] input binding if present on a directive on the same element', () => { @Component({template: '<div class="apple orange banana" DirWithClass></div>'}) class App { - @ViewChild(DirWithClassDirective) - mockClassDirective !: DirWithClassDirective; + @ViewChild(DirWithClassDirective) mockClassDirective!: DirWithClassDirective; } TestBed.configureTestingModule({declarations: [App, DirWithClassDirective]}); @@ -1073,8 +1088,7 @@ describe('acceptance integration tests', () => { () => { @Component({template: '<div style="width: 100px; height: 200px" DirWithStyle></div>'}) class App { - @ViewChild(DirWithStyleDirective) - mockStyleDirective !: DirWithStyleDirective; + @ViewChild(DirWithStyleDirective) mockStyleDirective!: DirWithStyleDirective; } TestBed.configureTestingModule({declarations: [App, DirWithStyleDirective]}); @@ -1092,8 +1106,7 @@ describe('acceptance integration tests', () => { () => { @Component({template: '<div DirWithClass [class]="value"></div>'}) class App { - @ViewChild(DirWithClassDirective) - mockClassDirective !: DirWithClassDirective; + @ViewChild(DirWithClassDirective) mockClassDirective!: DirWithClassDirective; value = ''; } @@ -1111,9 +1124,8 @@ describe('acceptance integration tests', () => { () => { @Component({template: '<div DirWithStyle [style]="value"></div>'}) class App { - @ViewChild(DirWithStyleDirective) - mockStyleDirective !: DirWithStyleDirective; - value !: {[key: string]: string}; + @ViewChild(DirWithStyleDirective) mockStyleDirective!: DirWithStyleDirective; + value!: {[key: string]: string}; } TestBed.configureTestingModule({declarations: [App, DirWithStyleDirective]}); @@ -1155,7 +1167,7 @@ describe('acceptance integration tests', () => { fixture.detectChanges(); const target: HTMLDivElement = fixture.nativeElement.querySelector('div'); - const classes = target.getAttribute('class') !.split(/\s+/).sort(); + const classes = target.getAttribute('class')!.split(/\s+/).sort(); expect(classes).toEqual(['big', 'golden', 'heavy']); expect(target.getAttribute('title')).toEqual('foo'); @@ -1189,7 +1201,7 @@ describe('acceptance integration tests', () => { }) class App { @ViewChild(DirWithSingleStylingBindings) - dirInstance !: DirWithSingleStylingBindings; + dirInstance!: DirWithSingleStylingBindings; } TestBed.configureTestingModule({declarations: [App, DirWithSingleStylingBindings]}); @@ -1244,8 +1256,8 @@ describe('acceptance integration tests', () => { @Component( {template: '<div Dir1WithStyle Dir2WithStyle [style.width]="width"></div>'}) class App { - @ViewChild(Dir1WithStyle) dir1Instance !: Dir1WithStyle; - @ViewChild(Dir2WithStyle) dir2Instance !: Dir2WithStyle; + @ViewChild(Dir1WithStyle) dir1Instance!: Dir1WithStyle; + @ViewChild(Dir2WithStyle) dir2Instance!: Dir2WithStyle; width: string|null|undefined = undefined; } @@ -1309,8 +1321,8 @@ describe('acceptance integration tests', () => { '<div Dir1WithStyling Dir2WithStyling [style]="stylesExp" [class]="classesExp"></div>' }) class App { - @ViewChild(Dir1WithStyling) dir1Instance !: Dir1WithStyling; - @ViewChild(Dir2WithStyling) dir2Instance !: Dir2WithStyling; + @ViewChild(Dir1WithStyling) dir1Instance!: Dir1WithStyling; + @ViewChild(Dir2WithStyling) dir2Instance!: Dir2WithStyling; stylesExp: any = {}; classesExp: any = {}; } @@ -1321,7 +1333,7 @@ describe('acceptance integration tests', () => { fixture.detectChanges(); const {dir1Instance, dir2Instance} = fixture.componentInstance; - const target = fixture.nativeElement.querySelector('div') !; + const target = fixture.nativeElement.querySelector('div')!; expect(target.style.getPropertyValue('width')).toEqual('111px'); const compInstance = fixture.componentInstance; @@ -1393,7 +1405,7 @@ describe('acceptance integration tests', () => { TestBed.configureTestingModule({declarations: [App]}); const fixture = TestBed.createComponent(App); - const target = fixture.nativeElement.querySelector('div') !; + const target = fixture.nativeElement.querySelector('div')!; expect(target.classList.contains('-fred-36-')).toBeFalsy(); @@ -1510,7 +1522,6 @@ describe('acceptance integration tests', () => { // The ViewEngine error has a typo, whereas the Ivy one fixes it. /^Unexpected value 'SomeModule' imported by the module 'ModuleWithImportedModule'\. Please add (a|an) @NgModule annotation\.$/); }); - }); it('should only call inherited host listeners once', () => { @@ -1519,7 +1530,9 @@ describe('acceptance integration tests', () => { @Component({template: ''}) class ButtonSuperClass { @HostListener('click') - clicked() { clicks++; } + clicked() { + clicks++; + } } @Component({selector: 'button[custom-button]', template: ''}) @@ -1548,7 +1561,7 @@ describe('acceptance integration tests', () => { @Component({template: '<div someDir></div>'}) class SuperComp { - @ViewChildren(SomeDir) dirs !: QueryList<SomeDir>; + @ViewChildren(SomeDir) dirs!: QueryList<SomeDir>; } @Component({selector: 'button[custom-button]', template: '<div someDir></div>'}) @@ -1576,7 +1589,9 @@ describe('acceptance integration tests', () => { private _isDestroyed = false; @Input() - get value() { return this._value; } + get value() { + return this._value; + } set value(newValue: any) { if (this._isDestroyed) { throw Error('Cannot assign to value after destroy.'); @@ -1586,7 +1601,9 @@ describe('acceptance integration tests', () => { } private _value: any; - ngOnDestroy() { this._isDestroyed = true; } + ngOnDestroy() { + this._isDestroyed = true; + } } @Component({template: '<div no-assign-after-destroy [value]="directiveValue"></div>'}) @@ -1608,7 +1625,7 @@ describe('acceptance integration tests', () => { @Component( {selector: 'test-component', template: `foo`, host: {'[attr.aria-disabled]': 'true'}}) class TestComponent { - @ContentChild(TemplateRef, {static: true}) tpl !: TemplateRef<any>; + @ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>; } TestBed.configureTestingModule({declarations: [TestComponent]}); @@ -1621,7 +1638,7 @@ describe('acceptance integration tests', () => { it('should inherit inputs from undecorated superclasses', () => { class ButtonSuperClass { - @Input() isDisabled !: boolean; + @Input() isDisabled!: boolean; } @Component({selector: 'button[custom-button]', template: ''}) @@ -1651,7 +1668,9 @@ describe('acceptance integration tests', () => { class ButtonSuperClass { @Output() clicked = new EventEmitter<void>(); - emitClick() { this.clicked.emit(); } + emitClick() { + this.clicked.emit(); + } } @Component({selector: 'button[custom-button]', template: ''}) @@ -1660,7 +1679,9 @@ describe('acceptance integration tests', () => { @Component({template: '<button custom-button (clicked)="handleClick()"></button>'}) class MyApp { - handleClick() { clicks++; } + handleClick() { + clicks++; + } } TestBed.configureTestingModule({declarations: [MyApp, ButtonSubClass]}); @@ -1675,8 +1696,7 @@ describe('acceptance integration tests', () => { it('should inherit host bindings from undecorated superclasses', () => { class BaseButton { - @HostBinding('attr.tabindex') - tabindex = -1; + @HostBinding('attr.tabindex') tabindex = -1; } @Component({selector: '[sub-button]', template: '<ng-content></ng-content>'}) @@ -1702,8 +1722,7 @@ describe('acceptance integration tests', () => { it('should inherit host bindings from undecorated grand superclasses', () => { class SuperBaseButton { - @HostBinding('attr.tabindex') - tabindex = -1; + @HostBinding('attr.tabindex') tabindex = -1; } class BaseButton extends SuperBaseButton {} @@ -1734,7 +1753,9 @@ describe('acceptance integration tests', () => { class BaseButton { @HostListener('click') - handleClick() { clicks++; } + handleClick() { + clicks++; + } } @Component({selector: '[sub-button]', template: '<ng-content></ng-content>'}) @@ -1761,7 +1782,9 @@ describe('acceptance integration tests', () => { @Directive({selector: '[baseButton]'}) class BaseButton { @HostListener('click') - handleClick() { clicks++; } + handleClick() { + clicks++; + } } @Component({selector: '[subButton]', template: '<ng-content></ng-content>'}) @@ -1788,7 +1811,9 @@ describe('acceptance integration tests', () => { @Directive({selector: '[superBaseButton]'}) class SuperBaseButton { @HostListener('click') - handleClick() { clicks++; } + handleClick() { + clicks++; + } } @Directive({selector: '[baseButton]'}) @@ -1819,7 +1844,9 @@ describe('acceptance integration tests', () => { @Directive({selector: '[superSuperBaseButton]'}) class SuperSuperBaseButton { @HostListener('click') - handleClick() { clicks++; } + handleClick() { + clicks++; + } } @Directive({selector: '[superBaseButton]'}) @@ -1855,9 +1882,13 @@ describe('acceptance integration tests', () => { inputs: ['dir'], }) class Dir { - get dir(): any { return null; } + get dir(): any { + return null; + } - set dir(value: any) { throw new Error('this error is expected'); } + set dir(value: any) { + throw new Error('this error is expected'); + } } @Component({ @@ -1919,7 +1950,7 @@ describe('acceptance integration tests', () => { }); const fixture = TestBed.createComponent(Cmp); fixture.detectChanges(false); - const element = fixture.nativeElement.querySelector('div') !; + const element = fixture.nativeElement.querySelector('div')!; assertAttrValues(element, 'first-update-pass'); diff --git a/packages/core/test/acceptance/lifecycle_spec.ts b/packages/core/test/acceptance/lifecycle_spec.ts index ebfd9aa7f882a..ebccdf3fe4c4e 100644 --- a/packages/core/test/acceptance/lifecycle_spec.ts +++ b/packages/core/test/acceptance/lifecycle_spec.ts @@ -69,13 +69,13 @@ describe('onChanges', () => { template: `<p>test</p>`, }) class Comp { - @Input() - val1 = 'a'; + @Input() val1 = 'a'; - @Input('publicVal2') - val2 = 'b'; + @Input('publicVal2') val2 = 'b'; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'comp', changes}); + } } @Component({template: `<comp [val1]="val1" [publicVal2]="val2"></comp>`}) @@ -122,10 +122,11 @@ describe('onChanges', () => { template: `<child [val]="val"></child>`, }) class Parent { - @Input() - val = ''; + @Input() val = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'parent', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'parent', changes}); + } } @Component({ @@ -133,10 +134,11 @@ describe('onChanges', () => { template: `<p>test</p>`, }) class Child { - @Input() - val = ''; + @Input() val = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'child', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'child', changes}); + } } @Component({template: `<parent [val]="val"></parent>`}) @@ -193,13 +195,13 @@ describe('onChanges', () => { template: `<child [name]="name" [val]="val"></child>`, }) class Parent { - @Input() - val = ''; + @Input() val = ''; - @Input() - name = ''; + @Input() name = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'parent ' + this.name, changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'parent ' + this.name, changes}); + } } @Component({ @@ -207,13 +209,13 @@ describe('onChanges', () => { template: `<p>test</p>`, }) class Child { - @Input() - val = ''; + @Input() val = ''; - @Input() - name = ''; + @Input() name = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'child ' + this.name, changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'child ' + this.name, changes}); + } } @Component({ @@ -303,10 +305,11 @@ describe('onChanges', () => { template: `<p>{{val}}</p>`, }) class Comp { - @Input() - val = ''; + @Input() val = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'comp', changes}); + } } @Component({template: `<comp *ngIf="show" [val]="val"></comp>`}) @@ -355,10 +358,11 @@ describe('onChanges', () => { template: `<p>{{val}}</p>`, }) class Projected { - @Input() - val = ''; + @Input() val = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'projected', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'projected', changes}); + } } @Component({ @@ -366,10 +370,11 @@ describe('onChanges', () => { template: `<div><ng-content></ng-content></div>`, }) class Comp { - @Input() - val = ''; + @Input() val = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'comp', changes}); + } } @Component({ @@ -427,11 +432,9 @@ describe('onChanges', () => { template: `<p>{{val}}</p>`, }) class Projected { - @Input() - val = ''; + @Input() val = ''; - @Input() - name = ''; + @Input() name = ''; ngOnChanges(changes: SimpleChanges) { events.push({name: 'projected ' + this.name, changes}); @@ -443,13 +446,13 @@ describe('onChanges', () => { template: `<div><ng-content></ng-content></div>`, }) class Comp { - @Input() - val = ''; + @Input() val = ''; - @Input() - name = ''; + @Input() name = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp ' + this.name, changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'comp ' + this.name, changes}); + } } @Component({ @@ -542,10 +545,11 @@ describe('onChanges', () => { selector: '[dir]', }) class Dir { - @Input() - dir = ''; + @Input() dir = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'dir', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'dir', changes}); + } } @Component({ @@ -553,10 +557,11 @@ describe('onChanges', () => { template: `<p>{{val}}</p>`, }) class Comp { - @Input() - val = ''; + @Input() val = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'comp', changes}); + } } @Component({ @@ -608,17 +613,17 @@ describe('onChanges', () => { }); it('should be called on directives before component if component injects directives', () => { - const events: any[] = []; @Directive({ selector: '[dir]', }) class Dir { - @Input() - dir = ''; + @Input() dir = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'dir', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'dir', changes}); + } } @Component({ @@ -626,12 +631,13 @@ describe('onChanges', () => { template: `<p>{{val}}</p>`, }) class Comp { - @Input() - val = ''; + @Input() val = ''; constructor(public dir: Dir) {} - ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'comp', changes}); + } } @Component({ @@ -680,33 +686,33 @@ describe('onChanges', () => { } } ]); - }); it('should be called on multiple directives in injection order', () => { - const events: any[] = []; @Directive({ selector: '[dir]', }) class Dir { - @Input() - dir = ''; + @Input() dir = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'dir', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'dir', changes}); + } } @Directive({ selector: '[injectionDir]', }) class InjectionDir { - @Input() - injectionDir = ''; + @Input() injectionDir = ''; constructor(public dir: Dir) {} - ngOnChanges(changes: SimpleChanges) { events.push({name: 'injectionDir', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'injectionDir', changes}); + } } @Component({ @@ -746,13 +752,13 @@ describe('onChanges', () => { selector: '[dir]', }) class Dir { - @Input() - dir = ''; + @Input() dir = ''; - @Input('dir-val') - val = ''; + @Input('dir-val') val = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'dir', changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'dir', changes}); + } } @Component({template: `<div [dir]="val1" [dir-val]="val2"></div>`}) @@ -796,13 +802,13 @@ describe('onChanges', () => { template: `<p>{{val}}</p>`, }) class Comp { - @Input() - val = ''; + @Input() val = ''; - @Input() - name = ''; + @Input() name = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp ' + this.name, changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'comp ' + this.name, changes}); + } } @Component({ @@ -909,11 +915,9 @@ describe('onChanges', () => { template: `<p>{{val}}</p>`, }) class Child { - @Input() - val = ''; + @Input() val = ''; - @Input() - name = ''; + @Input() name = ''; ngOnChanges(changes: SimpleChanges) { events.push({name: 'child of parent ' + this.name, changes}); @@ -925,13 +929,13 @@ describe('onChanges', () => { template: `<child [name]="name" [val]="val"></child>`, }) class Parent { - @Input() - val = ''; + @Input() val = ''; - @Input() - name = ''; + @Input() name = ''; - ngOnChanges(changes: SimpleChanges) { events.push({name: 'parent ' + this.name, changes}); } + ngOnChanges(changes: SimpleChanges) { + events.push({name: 'parent ' + this.name, changes}); + } } @Component({ @@ -1100,7 +1104,9 @@ describe('onChanges', () => { @Component({template: `<p>{{value}}</p>`}) class App { value = 'a'; - ngOnChanges(changes: SimpleChanges) { events.push(changes); } + ngOnChanges(changes: SimpleChanges) { + events.push(changes); + } } TestBed.configureTestingModule({ @@ -1125,15 +1131,31 @@ it('should call all hooks in correct order when several directives on same node' id: number = -1; /** @internal */ - private _log(hook: string, id: number) { log.push(hook + id); } - - ngOnChanges() { this._log('onChanges', this.id); } - ngOnInit() { this._log('onInit', this.id); } - ngDoCheck() { this._log('doCheck', this.id); } - ngAfterContentInit() { this._log('afterContentInit', this.id); } - ngAfterContentChecked() { this._log('afterContentChecked', this.id); } - ngAfterViewInit() { this._log('afterViewInit', this.id); } - ngAfterViewChecked() { this._log('afterViewChecked', this.id); } + private _log(hook: string, id: number) { + log.push(hook + id); + } + + ngOnChanges() { + this._log('onChanges', this.id); + } + ngOnInit() { + this._log('onInit', this.id); + } + ngDoCheck() { + this._log('doCheck', this.id); + } + ngAfterContentInit() { + this._log('afterContentInit', this.id); + } + ngAfterContentChecked() { + this._log('afterContentChecked', this.id); + } + ngAfterViewInit() { + this._log('afterViewInit', this.id); + } + ngAfterViewChecked() { + this._log('afterViewChecked', this.id); + } } @Directive({selector: 'div'}) @@ -1190,21 +1212,31 @@ it('should call hooks after setting directives inputs', () => { @Directive({selector: 'div'}) class DirA { @Input() a: number = 0; - ngOnInit() { log.push('onInitA' + this.a); } + ngOnInit() { + log.push('onInitA' + this.a); + } } @Directive({selector: 'div'}) class DirB { @Input() b: number = 0; - ngOnInit() { log.push('onInitB' + this.b); } - ngDoCheck() { log.push('doCheckB' + this.b); } + ngOnInit() { + log.push('onInitB' + this.b); + } + ngDoCheck() { + log.push('doCheckB' + this.b); + } } @Directive({selector: 'div'}) class DirC { @Input() c: number = 0; - ngOnInit() { log.push('onInitC' + this.c); } - ngDoCheck() { log.push('doCheckC' + this.c); } + ngOnInit() { + log.push('onInitC' + this.c); + } + ngDoCheck() { + log.push('doCheckC' + this.c); + } } @Component({ @@ -1240,11 +1272,9 @@ describe('onInit', () => { template: `<p>test</p>`, }) class MyComponent { - @Input() - input1 = ''; + @Input() input1 = ''; - @Input() - input2 = ''; + @Input() input2 = ''; ngOnInit() { input1Values.push(this.input1); @@ -1285,7 +1315,9 @@ describe('onInit', () => { @Component({template: ``}) class App { - ngOnInit() { onInitCalled++; } + ngOnInit() { + onInitCalled++; + } } TestBed.configureTestingModule({ @@ -1305,14 +1337,18 @@ describe('onInit', () => { template: `<p>child</p>`, }) class ChildComp { - ngOnInit() { initCalls.push('child'); } + ngOnInit() { + initCalls.push('child'); + } } @Component({ template: `<child-comp></child-comp>`, }) class ParentComp { - ngOnInit() { initCalls.push('parent'); } + ngOnInit() { + initCalls.push('parent'); + } } TestBed.configureTestingModule({ @@ -1332,10 +1368,11 @@ describe('onInit', () => { template: `<p>child</p>`, }) class ChildComp { - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { initCalls.push(`child of parent ${this.name}`); } + ngOnInit() { + initCalls.push(`child of parent ${this.name}`); + } } @Component({ @@ -1343,10 +1380,11 @@ describe('onInit', () => { template: `<child-comp [name]="name"></child-comp>`, }) class ParentComp { - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { initCalls.push(`parent ${this.name}`); } + ngOnInit() { + initCalls.push(`parent ${this.name}`); + } } @Component({ @@ -1372,7 +1410,9 @@ describe('onInit', () => { @Component({selector: 'my-comp', template: '<p>test</p>'}) class MyComp { - ngOnInit() { onInitCalls++; } + ngOnInit() { + onInitCalls++; + } } @Component({ @@ -1407,7 +1447,9 @@ describe('onInit', () => { class MyComp { onInitCalled = false; - ngOnInit() { this.onInitCalled = true; } + ngOnInit() { + this.onInitCalled = true; + } } @Component({ @@ -1425,8 +1467,7 @@ describe('onInit', () => { `, }) class App { - @ViewChild('container', {read: ViewContainerRef}) - viewContainerRef !: ViewContainerRef; + @ViewChild('container', {read: ViewContainerRef}) viewContainerRef!: ViewContainerRef; constructor(public compFactoryResolver: ComponentFactoryResolver) {} @@ -1464,7 +1505,9 @@ describe('onInit', () => { template: '', }) class Projected { - ngOnInit() { initialized.push('projected'); } + ngOnInit() { + initialized.push('projected'); + } } @Component({ @@ -1472,7 +1515,9 @@ describe('onInit', () => { template: `<ng-content></ng-content>`, }) class Comp { - ngOnInit() { initialized.push('comp'); } + ngOnInit() { + initialized.push('comp'); + } } @Component({ @@ -1483,7 +1528,9 @@ describe('onInit', () => { ` }) class App { - ngOnInit() { initialized.push('app'); } + ngOnInit() { + initialized.push('app'); + } } TestBed.configureTestingModule({ @@ -1504,10 +1551,11 @@ describe('onInit', () => { template: '', }) class Projected { - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { initialized.push('projected ' + this.name); } + ngOnInit() { + initialized.push('projected ' + this.name); + } } @Component({ @@ -1515,10 +1563,11 @@ describe('onInit', () => { template: `<ng-content></ng-content>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { initialized.push('comp ' + this.name); } + ngOnInit() { + initialized.push('comp ' + this.name); + } } @Component({ @@ -1532,7 +1581,9 @@ describe('onInit', () => { ` }) class App { - ngOnInit() { initialized.push('app'); } + ngOnInit() { + initialized.push('app'); + } } TestBed.configureTestingModule({ @@ -1551,10 +1602,11 @@ describe('onInit', () => { selector: '[dir]', }) class Dir { - @Input('dir-name') - name = ''; + @Input('dir-name') name = ''; - ngOnInit() { initialized.push('dir ' + this.name); } + ngOnInit() { + initialized.push('dir ' + this.name); + } } @Component({ @@ -1562,10 +1614,11 @@ describe('onInit', () => { template: `<p></p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { initialized.push('comp ' + this.name); } + ngOnInit() { + initialized.push('comp ' + this.name); + } } @Component({ @@ -1575,7 +1628,9 @@ describe('onInit', () => { ` }) class App { - ngOnInit() { initialized.push('app'); } + ngOnInit() { + initialized.push('app'); + } } TestBed.configureTestingModule({ @@ -1588,29 +1643,30 @@ describe('onInit', () => { }); it('should be called on multiple directives in injection order', () => { - const events: any[] = []; @Directive({ selector: '[dir]', }) class Dir { - @Input() - dir = ''; + @Input() dir = ''; - ngOnInit() { events.push('dir'); } + ngOnInit() { + events.push('dir'); + } } @Directive({ selector: '[injectionDir]', }) class InjectionDir { - @Input() - injectionDir = ''; + @Input() injectionDir = ''; constructor(public dir: Dir) {} - ngOnInit() { events.push('injectionDir'); } + ngOnInit() { + events.push('injectionDir'); + } } @Component({ @@ -1619,7 +1675,9 @@ describe('onInit', () => { class App { val = 'a'; - ngOnInit() { events.push('app'); } + ngOnInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -1638,10 +1696,11 @@ describe('onInit', () => { selector: '[dir]', }) class Dir { - @Input('dir-name') - name = ''; + @Input('dir-name') name = ''; - ngOnInit() { initialized.push('dir ' + this.name); } + ngOnInit() { + initialized.push('dir ' + this.name); + } } @Component({ @@ -1649,12 +1708,13 @@ describe('onInit', () => { template: `<p></p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; constructor(public dir: Dir) {} - ngOnInit() { initialized.push('comp ' + this.name); } + ngOnInit() { + initialized.push('comp ' + this.name); + } } @Component({ @@ -1664,7 +1724,9 @@ describe('onInit', () => { ` }) class App { - ngOnInit() { initialized.push('app'); } + ngOnInit() { + initialized.push('app'); + } } TestBed.configureTestingModule({ @@ -1683,10 +1745,11 @@ describe('onInit', () => { selector: '[dir]', }) class Dir { - @Input('dir-name') - name = ''; + @Input('dir-name') name = ''; - ngOnInit() { initialized.push('dir ' + this.name); } + ngOnInit() { + initialized.push('dir ' + this.name); + } } @Component({ @@ -1696,7 +1759,9 @@ describe('onInit', () => { ` }) class App { - ngOnInit() { initialized.push('app'); } + ngOnInit() { + initialized.push('app'); + } } TestBed.configureTestingModule({ @@ -1717,10 +1782,11 @@ describe('onInit', () => { template: `<p></p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { initialized.push('comp ' + this.name); } + ngOnInit() { + initialized.push('comp ' + this.name); + } } @Component({ @@ -1754,18 +1820,20 @@ describe('onInit', () => { template: `<p></p>`, }) class Child { - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { initialized.push('child of parent ' + this.name); } + ngOnInit() { + initialized.push('child of parent ' + this.name); + } } @Component({selector: 'parent', template: '<child [name]="name"></child>'}) class Parent { - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { initialized.push('parent ' + this.name); } + ngOnInit() { + initialized.push('parent ' + this.name); + } } @Component({ @@ -1816,7 +1884,9 @@ describe('doCheck', () => { @Component({template: ``}) class App { - ngDoCheck() { doCheckCalled++; } + ngDoCheck() { + doCheckCalled++; + } } TestBed.configureTestingModule({ @@ -1840,7 +1910,9 @@ describe('doCheck', () => { template: `<child></child>`, }) class Parent { - ngDoCheck() { doChecks.push('parent'); } + ngDoCheck() { + doChecks.push('parent'); + } } @Component({ @@ -1848,12 +1920,16 @@ describe('doCheck', () => { template: ``, }) class Child { - ngDoCheck() { doChecks.push('child'); } + ngDoCheck() { + doChecks.push('child'); + } } @Component({template: `<parent></parent>`}) class App { - ngDoCheck() { doChecks.push('app'); } + ngDoCheck() { + doChecks.push('app'); + } } TestBed.configureTestingModule({ @@ -1869,9 +1945,13 @@ describe('doCheck', () => { const events: string[] = []; @Component({template: ``}) class App { - ngOnInit() { events.push('onInit'); } + ngOnInit() { + events.push('onInit'); + } - ngDoCheck() { events.push('doCheck'); } + ngDoCheck() { + events.push('doCheck'); + } } TestBed.configureTestingModule({ @@ -1889,10 +1969,11 @@ describe('doCheck', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngDoCheck() { doChecks.push('dir ' + this.name); } + ngDoCheck() { + doChecks.push('dir ' + this.name); + } } @Component({ @@ -1900,10 +1981,11 @@ describe('doCheck', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngDoCheck() { doChecks.push('comp ' + this.name); } + ngDoCheck() { + doChecks.push('comp ' + this.name); + } } @Component({ @@ -1913,7 +1995,9 @@ describe('doCheck', () => { ` }) class App { - ngDoCheck() { doChecks.push('app'); } + ngDoCheck() { + doChecks.push('app'); + } } TestBed.configureTestingModule({ @@ -1931,10 +2015,11 @@ describe('doCheck', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngDoCheck() { doChecks.push('dir ' + this.name); } + ngDoCheck() { + doChecks.push('dir ' + this.name); + } } @Component({ @@ -1942,12 +2027,13 @@ describe('doCheck', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; constructor(public dir: Dir) {} - ngDoCheck() { doChecks.push('comp ' + this.name); } + ngDoCheck() { + doChecks.push('comp ' + this.name); + } } @Component({ @@ -1957,7 +2043,9 @@ describe('doCheck', () => { ` }) class App { - ngDoCheck() { doChecks.push('app'); } + ngDoCheck() { + doChecks.push('app'); + } } TestBed.configureTestingModule({ @@ -1970,29 +2058,30 @@ describe('doCheck', () => { }); it('should be called on multiple directives in injection order', () => { - const events: any[] = []; @Directive({ selector: '[dir]', }) class Dir { - @Input() - dir = ''; + @Input() dir = ''; - ngDoCheck() { events.push('dir'); } + ngDoCheck() { + events.push('dir'); + } } @Directive({ selector: '[injectionDir]', }) class InjectionDir { - @Input() - injectionDir = ''; + @Input() injectionDir = ''; constructor(public dir: Dir) {} - ngDoCheck() { events.push('injectionDir'); } + ngDoCheck() { + events.push('injectionDir'); + } } @Component({ @@ -2001,7 +2090,9 @@ describe('doCheck', () => { class App { val = 'a'; - ngDoCheck() { events.push('app'); } + ngDoCheck() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2020,10 +2111,11 @@ describe('doCheck', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngDoCheck() { doChecks.push('dir ' + this.name); } + ngDoCheck() { + doChecks.push('dir ' + this.name); + } } @Component({ @@ -2033,7 +2125,9 @@ describe('doCheck', () => { ` }) class App { - ngDoCheck() { doChecks.push('app'); } + ngDoCheck() { + doChecks.push('app'); + } } TestBed.configureTestingModule({ @@ -2055,7 +2149,9 @@ describe('afterContentinit', () => { template: `<p>test</p>`, }) class Comp { - ngAfterContentInit() { afterContentInitCalls++; } + ngAfterContentInit() { + afterContentInitCalls++; + } } @Component({template: `<comp></comp>`}) class App { @@ -2079,7 +2175,9 @@ describe('afterContentinit', () => { @Component({template: `<p>test</p>`}) class App { - ngAfterContentInit() { afterContentInitCalls++; } + ngAfterContentInit() { + afterContentInitCalls++; + } } TestBed.configureTestingModule({ @@ -2103,14 +2201,18 @@ describe('afterContentinit', () => { template: `<p>test</p>`, }) class Comp { - ngAfterContentInit() { events.push('comp afterContentInit'); } + ngAfterContentInit() { + events.push('comp afterContentInit'); + } } @Component({template: `<comp *ngIf="show"></comp>`}) class App { show = true; - ngAfterContentInit() { events.push('app afterContentInit'); } + ngAfterContentInit() { + events.push('app afterContentInit'); + } } TestBed.configureTestingModule({ @@ -2143,10 +2245,11 @@ describe('afterContentinit', () => { template: `<child [name]="name"></child>`, }) class Parent { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('parent ' + this.name); } + ngAfterContentInit() { + events.push('parent ' + this.name); + } } @Component({ @@ -2154,10 +2257,11 @@ describe('afterContentinit', () => { template: `<p>test</p>`, }) class Child { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('child of parent ' + this.name); } + ngAfterContentInit() { + events.push('child of parent ' + this.name); + } } @Component({ @@ -2167,7 +2271,9 @@ describe('afterContentinit', () => { ` }) class App { - ngAfterContentInit() { events.push('app'); } + ngAfterContentInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2188,10 +2294,11 @@ describe('afterContentinit', () => { template: `<p>test</p>`, }) class ProjectedChild { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('projected child ' + this.name); } + ngAfterContentInit() { + events.push('projected child ' + this.name); + } } @Component({ @@ -2199,10 +2306,11 @@ describe('afterContentinit', () => { template: `<div><ng-content></ng-content></div>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('comp ' + this.name); } + ngAfterContentInit() { + events.push('comp ' + this.name); + } } @Component({ @@ -2210,10 +2318,11 @@ describe('afterContentinit', () => { template: `<projected-child [name]=name></projected-child>`, }) class Projected { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('projected ' + this.name); } + ngAfterContentInit() { + events.push('projected ' + this.name); + } } @Component({ @@ -2229,7 +2338,9 @@ describe('afterContentinit', () => { ` }) class App { - ngAfterContentInit() { events.push('app'); } + ngAfterContentInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2267,10 +2378,11 @@ describe('afterContentinit', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('comp ' + this.name); } + ngAfterContentInit() { + events.push('comp ' + this.name); + } } @Component({ @@ -2283,7 +2395,9 @@ describe('afterContentinit', () => { class App { numbers = [0, 1, 2, 3]; - ngAfterContentInit() { events.push('app'); } + ngAfterContentInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2304,10 +2418,11 @@ describe('afterContentinit', () => { template: `<child [name]=name></child>`, }) class Parent { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('parent ' + this.name); } + ngAfterContentInit() { + events.push('parent ' + this.name); + } } @Component({ @@ -2315,10 +2430,11 @@ describe('afterContentinit', () => { template: `<p>test</p>`, }) class Child { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('child of parent ' + this.name); } + ngAfterContentInit() { + events.push('child of parent ' + this.name); + } } @Component({ @@ -2330,7 +2446,9 @@ describe('afterContentinit', () => { }) class App { numbers = [0, 1, 2, 3]; - ngAfterContentInit() { events.push('app'); } + ngAfterContentInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2366,10 +2484,11 @@ describe('afterContentinit', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngAfterContentInit() { events.push('dir ' + this.name); } + ngAfterContentInit() { + events.push('dir ' + this.name); + } } @Component({ @@ -2377,10 +2496,11 @@ describe('afterContentinit', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngAfterContentInit() { events.push('comp ' + this.name); } + ngAfterContentInit() { + events.push('comp ' + this.name); + } } @Component({ @@ -2390,7 +2510,9 @@ describe('afterContentinit', () => { ` }) class App { - ngAfterContentInit() { events.push('app'); } + ngAfterContentInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2418,16 +2540,24 @@ describe('afterContentChecked', () => { template: `<p>test</p>`, }) class Comp { - ngAfterContentInit() { events.push('comp afterContentInit'); } + ngAfterContentInit() { + events.push('comp afterContentInit'); + } - ngAfterContentChecked() { events.push('comp afterContentChecked'); } + ngAfterContentChecked() { + events.push('comp afterContentChecked'); + } } @Component({template: `<comp></comp>`}) class App { - ngAfterContentInit() { events.push('app afterContentInit'); } + ngAfterContentInit() { + events.push('app afterContentInit'); + } - ngAfterContentChecked() { events.push('app afterContentChecked'); } + ngAfterContentChecked() { + events.push('app afterContentChecked'); + } } TestBed.configureTestingModule({ @@ -2454,7 +2584,9 @@ describe('afterViewInit', () => { template: `<p>test</p>`, }) class Comp { - ngAfterViewInit() { afterViewInitCalls++; } + ngAfterViewInit() { + afterViewInitCalls++; + } } @Component({template: `<comp></comp>`}) @@ -2472,7 +2604,6 @@ describe('afterViewInit', () => { fixture.detectChanges(); expect(afterViewInitCalls).toBe(1); - }); it('should be called on root component in creation mode', () => { @@ -2480,7 +2611,9 @@ describe('afterViewInit', () => { @Component({template: `<p>test</p>`}) class App { - ngAfterViewInit() { afterViewInitCalls++; } + ngAfterViewInit() { + afterViewInitCalls++; + } } TestBed.configureTestingModule({ @@ -2504,7 +2637,9 @@ describe('afterViewInit', () => { template: `<p>test</p>`, }) class Comp { - ngAfterViewInit() { events.push('comp'); } + ngAfterViewInit() { + events.push('comp'); + } } @Component({ @@ -2513,7 +2648,9 @@ describe('afterViewInit', () => { class App { show = true; - ngAfterViewInit() { events.push('app'); } + ngAfterViewInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2543,10 +2680,11 @@ describe('afterViewInit', () => { template: `<child [name]=name></child>`, }) class Parent { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('parent ' + this.name); } + ngAfterViewInit() { + events.push('parent ' + this.name); + } } @Component({ @@ -2554,10 +2692,11 @@ describe('afterViewInit', () => { template: `<p>test</p>`, }) class Child { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('child of parent ' + this.name); } + ngAfterViewInit() { + events.push('child of parent ' + this.name); + } } @Component({ @@ -2567,7 +2706,9 @@ describe('afterViewInit', () => { ` }) class App { - ngAfterViewInit() { events.push('app'); } + ngAfterViewInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2593,10 +2734,11 @@ describe('afterViewInit', () => { template: `<p>test</p>`, }) class Projected { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('projected ' + this.name); } + ngAfterViewInit() { + events.push('projected ' + this.name); + } } @Component({ @@ -2604,10 +2746,11 @@ describe('afterViewInit', () => { template: `<ng-content></ng-content>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('comp ' + this.name); } + ngAfterViewInit() { + events.push('comp ' + this.name); + } } @Component({ @@ -2617,7 +2760,9 @@ describe('afterViewInit', () => { ` }) class App { - ngAfterViewInit() { events.push('app'); } + ngAfterViewInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2644,10 +2789,11 @@ describe('afterViewInit', () => { template: `<p>test</p>`, }) class ProjectedChild { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('child of projected ' + this.name); } + ngAfterViewInit() { + events.push('child of projected ' + this.name); + } } @Component({ @@ -2655,10 +2801,11 @@ describe('afterViewInit', () => { template: `<projected-child [name]="name"></projected-child>`, }) class Projected { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('projected ' + this.name); } + ngAfterViewInit() { + events.push('projected ' + this.name); + } } @Component({ @@ -2666,10 +2813,11 @@ describe('afterViewInit', () => { template: `<div><ng-content></ng-content></div>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('comp ' + this.name); } + ngAfterViewInit() { + events.push('comp ' + this.name); + } } @Component({ @@ -2679,7 +2827,9 @@ describe('afterViewInit', () => { ` }) class App { - ngAfterViewInit() { events.push('app'); } + ngAfterViewInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2707,10 +2857,11 @@ describe('afterViewInit', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('comp ' + this.name); } + ngAfterViewInit() { + events.push('comp ' + this.name); + } } @Component({ @@ -2723,7 +2874,9 @@ describe('afterViewInit', () => { class App { numbers = [0, 1, 2, 3]; - ngAfterViewInit() { events.push('app'); } + ngAfterViewInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2752,20 +2905,22 @@ describe('afterViewInit', () => { template: `<p>test</p>`, }) class Child { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('child of parent ' + this.name); } + ngAfterViewInit() { + events.push('child of parent ' + this.name); + } } @Component({ selector: 'parent', template: `<child [name]="name"></child>`, }) class Parent { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('parent ' + this.name); } + ngAfterViewInit() { + events.push('parent ' + this.name); + } } @Component({ @@ -2778,7 +2933,9 @@ describe('afterViewInit', () => { class App { numbers = [0, 1, 2, 3]; - ngAfterViewInit() { events.push('app'); } + ngAfterViewInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2812,10 +2969,11 @@ describe('afterViewInit', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngAfterViewInit() { events.push('dir ' + this.name); } + ngAfterViewInit() { + events.push('dir ' + this.name); + } } @Component({ @@ -2823,10 +2981,11 @@ describe('afterViewInit', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewInit() { events.push('comp ' + this.name); } + ngAfterViewInit() { + events.push('comp ' + this.name); + } } @Component({ @@ -2836,7 +2995,9 @@ describe('afterViewInit', () => { ` }) class App { - ngAfterViewInit() { events.push('app'); } + ngAfterViewInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2861,10 +3022,11 @@ describe('afterViewInit', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngAfterViewInit() { events.push('dir ' + this.name); } + ngAfterViewInit() { + events.push('dir ' + this.name); + } } @Component({ @@ -2874,7 +3036,9 @@ describe('afterViewInit', () => { ` }) class App { - ngAfterViewInit() { events.push('app'); } + ngAfterViewInit() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -2900,7 +3064,9 @@ describe('afterViewChecked', () => { template: `<p>test</p>`, }) class Comp { - ngAfterViewChecked() { afterViewCheckedCalls++; } + ngAfterViewChecked() { + afterViewCheckedCalls++; + } } @Component({template: `<comp></comp>`}) @@ -2927,7 +3093,9 @@ describe('afterViewChecked', () => { @Component({template: `<p>test</p>`}) class App { - ngAfterViewChecked() { afterViewCheckedCalls++; } + ngAfterViewChecked() { + afterViewCheckedCalls++; + } } TestBed.configureTestingModule({ @@ -2953,9 +3121,10 @@ describe('afterViewChecked', () => { template: `<p>{{value}}</p>`, }) class Comp { - @Input() - value = ''; - ngAfterViewChecked() { afterViewCheckedCalls++; } + @Input() value = ''; + ngAfterViewChecked() { + afterViewCheckedCalls++; + } } @Component({template: `<comp [value]="value"></comp>`}) @@ -2983,10 +3152,11 @@ describe('afterViewChecked', () => { template: `<p>test</p>`, }) class Child { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewChecked() { events.push('child of parent ' + this.name); } + ngAfterViewChecked() { + events.push('child of parent ' + this.name); + } } @Component({ @@ -2994,10 +3164,11 @@ describe('afterViewChecked', () => { template: `<child [name]="name"></child>`, }) class Parent { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewChecked() { events.push('parent ' + this.name); } + ngAfterViewChecked() { + events.push('parent ' + this.name); + } } @Component({ @@ -3010,7 +3181,9 @@ describe('afterViewChecked', () => { class App { numbers = [0, 1, 2, 3]; - ngAfterViewChecked() { events.push('app'); } + ngAfterViewChecked() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -3043,10 +3216,11 @@ describe('afterViewChecked', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngAfterViewChecked() { events.push('dir ' + this.name); } + ngAfterViewChecked() { + events.push('dir ' + this.name); + } } @Component({ @@ -3054,10 +3228,11 @@ describe('afterViewChecked', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngAfterViewChecked() { events.push('comp ' + this.name); } + ngAfterViewChecked() { + events.push('comp ' + this.name); + } } @Component({ @@ -3067,7 +3242,9 @@ describe('afterViewChecked', () => { ` }) class App { - ngAfterViewChecked() { events.push('app'); } + ngAfterViewChecked() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -3092,10 +3269,11 @@ describe('afterViewChecked', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngAfterViewChecked() { events.push('dir ' + this.name); } + ngAfterViewChecked() { + events.push('dir ' + this.name); + } } @Component({ @@ -3105,7 +3283,9 @@ describe('afterViewChecked', () => { ` }) class App { - ngAfterViewChecked() { events.push('app'); } + ngAfterViewChecked() { + events.push('app'); + } } TestBed.configureTestingModule({ @@ -3120,12 +3300,9 @@ describe('afterViewChecked', () => { 'app', ]); }); - }); describe('onDestroy', () => { - - it('should call destroy when view is removed', () => { let destroyCalled = 0; @@ -3134,7 +3311,9 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Comp { - ngOnDestroy() { destroyCalled++; } + ngOnDestroy() { + destroyCalled++; + } } @Component({ @@ -3177,10 +3356,11 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('comp ' + this.name); } + ngOnDestroy() { + events.push('comp ' + this.name); + } } @Component({ @@ -3218,10 +3398,11 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Child { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('child of parent ' + this.name); } + ngOnDestroy() { + events.push('child of parent ' + this.name); + } } @Component({ @@ -3229,9 +3410,10 @@ describe('onDestroy', () => { template: `<child [name]="name"></child>`, }) class Parent { - @Input() - name = ''; - ngOnDestroy() { events.push('parent ' + this.name); } + @Input() name = ''; + ngOnDestroy() { + events.push('parent ' + this.name); + } } @Component({ @@ -3274,10 +3456,11 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Child { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('child ' + this.name); } + ngOnDestroy() { + events.push('child ' + this.name); + } } @Component({ @@ -3285,9 +3468,10 @@ describe('onDestroy', () => { template: `<child [name]="name"></child>`, }) class Parent { - @Input() - name = ''; - ngOnDestroy() { events.push('parent ' + this.name); } + @Input() name = ''; + ngOnDestroy() { + events.push('parent ' + this.name); + } } @Component({ @@ -3295,10 +3479,11 @@ describe('onDestroy', () => { template: `<parent [name]="name"></parent>`, }) class Grandparent { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('grandparent ' + this.name); } + ngOnDestroy() { + events.push('grandparent ' + this.name); + } } @Component({ template: ` @@ -3342,10 +3527,11 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Projected { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('projected ' + this.name); } + ngOnDestroy() { + events.push('projected ' + this.name); + } } @Component({ @@ -3353,10 +3539,11 @@ describe('onDestroy', () => { template: `<div><ng-content></ng-content></div>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('comp ' + this.name); } + ngOnDestroy() { + events.push('comp ' + this.name); + } } @Component({ @@ -3403,10 +3590,11 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('comp ' + this.name); } + ngOnDestroy() { + events.push('comp ' + this.name); + } } @Component({ @@ -3473,10 +3661,11 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('comp ' + this.name); } + ngOnDestroy() { + events.push('comp ' + this.name); + } } @Component({ @@ -3551,7 +3740,9 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Comp { - ngOnDestroy() { events.push('comp'); } + ngOnDestroy() { + events.push('comp'); + } } @Component({ template: ` @@ -3569,9 +3760,13 @@ describe('onDestroy', () => { clicksToButton2 = 0; - handleClick1() { this.clicksToButton1++; } + handleClick1() { + this.clicksToButton1++; + } - handleClick2() { this.clicksToButton2++; } + handleClick2() { + this.clicksToButton2++; + } } TestBed.configureTestingModule({ @@ -3604,7 +3799,7 @@ describe('onDestroy', () => { @Component({selector: 'parent', template: `<ng-content></ng-content>`}) class Parent { - @ContentChildren(Child, {descendants: true}) child !: QueryList<Child>; + @ContentChildren(Child, {descendants: true}) child!: QueryList<Child>; } @Component({ @@ -3619,13 +3814,13 @@ describe('onDestroy', () => { ` }) class App { - @ViewChild('container', {read: ViewContainerRef, static: true}) - container !: ViewContainerRef; + @ViewChild('container', {read: ViewContainerRef, static: true}) container!: ViewContainerRef; - @ViewChild('tpl', {read: TemplateRef, static: true}) - tpl !: TemplateRef<any>; + @ViewChild('tpl', {read: TemplateRef, static: true}) tpl!: TemplateRef<any>; - ngOnInit() { this.container.createEmbeddedView(this.tpl); } + ngOnInit() { + this.container.createEmbeddedView(this.tpl); + } } @Directive({selector: '[dir]'}) @@ -3659,10 +3854,11 @@ describe('onDestroy', () => { selector: '[dir]', }) class Dir { - @Input('dir') - name = ''; + @Input('dir') name = ''; - ngOnDestroy() { events.push('dir ' + this.name); } + ngOnDestroy() { + events.push('dir ' + this.name); + } } @Component({ @@ -3670,10 +3866,11 @@ describe('onDestroy', () => { template: `<p>test</p>`, }) class Comp { - @Input() - name = ''; + @Input() name = ''; - ngOnDestroy() { events.push('comp ' + this.name); } + ngOnDestroy() { + events.push('comp ' + this.name); + } } @Component({ @@ -3715,7 +3912,9 @@ describe('onDestroy', () => { selector: '[dir]', }) class Dir { - ngOnDestroy() { events.push('dir'); } + ngOnDestroy() { + events.push('dir'); + } } @Component({template: `<p *ngIf="show" dir></p>`}) @@ -3749,27 +3948,41 @@ describe('hook order', () => { template: `{{value}}<div><ng-content></ng-content></div>`, }) class Comp { - @Input() - value = ''; + @Input() value = ''; - @Input() - name = ''; + @Input() name = ''; - ngOnInit() { events.push(`${this.name} onInit`); } + ngOnInit() { + events.push(`${this.name} onInit`); + } - ngDoCheck() { events.push(`${this.name} doCheck`); } + ngDoCheck() { + events.push(`${this.name} doCheck`); + } - ngOnChanges() { events.push(`${this.name} onChanges`); } + ngOnChanges() { + events.push(`${this.name} onChanges`); + } - ngAfterContentInit() { events.push(`${this.name} afterContentInit`); } + ngAfterContentInit() { + events.push(`${this.name} afterContentInit`); + } - ngAfterContentChecked() { events.push(`${this.name} afterContentChecked`); } + ngAfterContentChecked() { + events.push(`${this.name} afterContentChecked`); + } - ngAfterViewInit() { events.push(`${this.name} afterViewInit`); } + ngAfterViewInit() { + events.push(`${this.name} afterViewInit`); + } - ngAfterViewChecked() { events.push(`${this.name} afterViewChecked`); } + ngAfterViewChecked() { + events.push(`${this.name} afterViewChecked`); + } - ngOnDestroy() { events.push(`${this.name} onDestroy`); } + ngOnDestroy() { + events.push(`${this.name} onDestroy`); + } } @Component({ @@ -4044,7 +4257,9 @@ describe('non-regression', () => { selector: '[onDestroyDir]', }) class OnDestroyDir { - ngOnDestroy() { destroyed = true; } + ngOnDestroy() { + destroyed = true; + } } @Component({ diff --git a/packages/core/test/acceptance/listener_spec.ts b/packages/core/test/acceptance/listener_spec.ts index 8c42981b12b81..7bb045800cd04 100644 --- a/packages/core/test/acceptance/listener_spec.ts +++ b/packages/core/test/acceptance/listener_spec.ts @@ -16,36 +16,40 @@ function getNoOfNativeListeners(): number { } describe('event listeners', () => { - describe('coalescing', () => { - @Component({ selector: 'with-clicks-cmpt', template: `<button likes-clicks (click)="count()" md-button>Click me!</button>` }) class WithClicksCmpt { counter = 0; - count() { this.counter++; } + count() { + this.counter++; + } } @Directive({selector: '[md-button]'}) class MdButton { counter = 0; @HostListener('click') - count() { this.counter++; } + count() { + this.counter++; + } } @Directive({selector: '[likes-clicks]'}) class LikesClicks { counter = 0; @HostListener('click') - count() { this.counter++; } + count() { + this.counter++; + } } @Directive({selector: '[returns-false]'}) class ReturnsFalse { counter = 0; - event !: Event; + event!: Event; handlerShouldReturn: boolean|undefined = undefined; @HostListener('click', ['$event']) @@ -65,7 +69,6 @@ describe('event listeners', () => { onlyInIvy('ngDevMode.rendererAddEventListener counters are only available in ivy') .it('should coalesce multiple event listeners for the same event on the same element', () => { - @Component({ selector: 'test-cmpt', template: @@ -107,7 +110,6 @@ describe('event listeners', () => { onlyInIvy('ngDevMode.rendererAddEventListener counters are only available in ivy') .it('should coalesce multiple event listeners in presence of queries', () => { - @Component({ selector: 'test-cmpt', template: `<button likes-clicks (click)="counter = counter+1">Click me!</button>` @@ -115,7 +117,7 @@ describe('event listeners', () => { class TestCmpt { counter = 0; - @ViewChildren('nothing') nothing !: QueryList<any>; + @ViewChildren('nothing') nothing!: QueryList<any>; } TestBed.configureTestingModule({declarations: [TestCmpt, LikesClicks]}); @@ -136,11 +138,12 @@ describe('event listeners', () => { it('should try to execute remaining coalesced listeners if one of the listeners throws', () => { - @Directive({selector: '[throws-on-clicks]'}) class ThrowsOnClicks { @HostListener('click') - dontCount() { throw new Error('I was clicked and I don\'t like it!'); } + dontCount() { + throw new Error('I was clicked and I don\'t like it!'); + } } @Component( @@ -151,7 +154,9 @@ describe('event listeners', () => { let noOfErrors = 0; class CountingErrorHandler extends ErrorHandler { - handleError(error: any): void { noOfErrors++; } + handleError(error: any): void { + noOfErrors++; + } } TestBed.configureTestingModule({ @@ -209,7 +214,9 @@ describe('event listeners', () => { @Input('foo') model: any; @Output('fooChange') update = new EventEmitter(); - updateValue(value: any) { this.update.emit(value); } + updateValue(value: any) { + this.update.emit(value); + } } @Component({ @@ -222,9 +229,13 @@ describe('event listeners', () => { @ViewChild(FooDirective) fooDirective: FooDirective|null = null; - fooChange() { this.count++; } + fooChange() { + this.count++; + } - triggerUpdate(value: any) { this.fooDirective !.updateValue(value); } + triggerUpdate(value: any) { + this.fooDirective!.updateValue(value); + } } TestBed.configureTestingModule({declarations: [TestComponent, FooDirective]}); @@ -247,19 +258,25 @@ describe('event listeners', () => { }) class MyComp { counter = 0; - count() { log.push('component.click'); } + count() { + log.push('component.click'); + } } @Directive({selector: '[dirA]'}) class DirA { @HostListener('click') - count() { log.push('dirA.click'); } + count() { + log.push('dirA.click'); + } } @Directive({selector: '[dirB]'}) class DirB { @HostListener('click') - count() { log.push('dirB.click'); } + count() { + log.push('dirB.click'); + } } TestBed.configureTestingModule({declarations: [MyComp, DirA, DirB]}); diff --git a/packages/core/test/acceptance/ng_module_spec.ts b/packages/core/test/acceptance/ng_module_spec.ts index e23031c32ed64..9714e37b3ef75 100644 --- a/packages/core/test/acceptance/ng_module_spec.ts +++ b/packages/core/test/acceptance/ng_module_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {CUSTOM_ELEMENTS_SCHEMA, Component, Injectable, InjectionToken, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core'; +import {Component, CUSTOM_ELEMENTS_SCHEMA, Injectable, InjectionToken, NgModule, NgModuleRef, NO_ERRORS_SCHEMA, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element, ɵɵproperty as property} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {modifiedInIvy, onlyInIvy} from '@angular/private/testing'; @@ -84,12 +84,16 @@ describe('NgModule', () => { } @NgModule({providers: [Service]}) class RoutesModule { - constructor(service: Service) { service.initializations.push('RoutesModule'); } + constructor(service: Service) { + service.initializations.push('RoutesModule'); + } } @NgModule({imports: [RoutesModule]}) class AppModule { - constructor(service: Service) { service.initializations.push('AppModule'); } + constructor(service: Service) { + service.initializations.push('AppModule'); + } } TestBed.configureTestingModule({imports: [AppModule]}); @@ -188,7 +192,6 @@ describe('NgModule', () => { onlyInIvy('unknown element check logs a warning rather than throwing') .it('should warn about unknown element without CUSTOM_ELEMENTS_SCHEMA for element with dash in tag name', () => { - @Component({template: `<custom-el></custom-el>`}) class MyComp { } @@ -244,71 +247,121 @@ describe('NgModule', () => { }).toThrowError(/'custom' is not a known element/); }); - onlyInIvy('test relies on Ivy-specific AOT format') - .it('should not log unknown element warning for AOT-compiled components', () => { - const spy = spyOn(console, 'warn'); + onlyInIvy('test relies on Ivy-specific AOT format').describe('AOT-compiled components', () => { + function createComponent( + template: (rf: any) => void, vars: number, consts?: (number|string)[][]) { + class Comp { + static ɵfac = () => new Comp(); + static ɵcmp = defineComponent({ + type: Comp, + selectors: [['comp']], + decls: 1, + vars, + consts, + template, + encapsulation: 2 + }); + } + setClassMetadata( + Comp, [{ + type: Component, + args: [ + {selector: 'comp', template: '...'}, + ] + }], + null, null); + return Comp; + } - /* - * @Component({ - * selector: 'comp', - * template: '<custom-el></custom-el>', - * }) - * class MyComp {} - */ - class MyComp { - static ɵfac = () => new MyComp(); - static ɵcmp = defineComponent({ - type: MyComp, - selectors: [['comp']], - decls: 1, - vars: 0, - template: function MyComp_Template(rf, ctx) { - if (rf & 1) { - element(0, 'custom-el'); - } - }, - encapsulation: 2 - }); + function createNgModule(Comp: any) { + class Module { + static ɵmod = defineNgModule({type: Module}); + static ɵinj = defineInjector({factory: () => new Module()}); + } + setClassMetadata( + Module, [{ + type: NgModule, + args: [{ + declarations: [Comp], + schemas: [NO_ERRORS_SCHEMA], + }] + }], + null, null); + return Module; + } + + it('should not log unknown element warning for AOT-compiled components', () => { + const spy = spyOn(console, 'warn'); + + /* + * @Component({ + * selector: 'comp', + * template: '<custom-el></custom-el>', + * }) + * class MyComp {} + */ + const MyComp = createComponent((rf: any) => { + if (rf & 1) { + element(0, 'custom-el'); } - setClassMetadata( - MyComp, [{ - type: Component, - args: [{ - selector: 'comp', - template: '<custom-el></custom-el>', - }] - }], - null, null); - - /* - * @NgModule({ - * declarations: [MyComp], - * schemas: [NO_ERRORS_SCHEMA], - * }) - * class MyModule {} - */ - class MyModule { - static ɵmod = defineNgModule({type: MyModule}); - static ɵinj = defineInjector({factory: () => new MyModule()}); + }, 0); + + /* + * @NgModule({ + * declarations: [MyComp], + * schemas: [NO_ERRORS_SCHEMA], + * }) + * class MyModule {} + */ + const MyModule = createNgModule(MyComp); + + TestBed.configureTestingModule({ + imports: [MyModule], + }); + + const fixture = TestBed.createComponent(MyComp); + fixture.detectChanges(); + expect(spy).not.toHaveBeenCalled(); + }); + + it('should not log unknown property warning for AOT-compiled components', () => { + const spy = spyOn(console, 'warn'); + + /* + * @Component({ + * selector: 'comp', + * template: '<div [foo]="1"></div>', + * }) + * class MyComp {} + */ + const MyComp = createComponent((rf: any) => { + if (rf & 1) { + element(0, 'div', 0); } - setClassMetadata( - MyModule, [{ - type: NgModule, - args: [{ - declarations: [MyComp], - schemas: [NO_ERRORS_SCHEMA], - }] - }], - null, null); + if (rf & 2) { + property('foo', true); + } + }, 1, [[3, 'foo']]); + + /* + * @NgModule({ + * declarations: [MyComp], + * schemas: [NO_ERRORS_SCHEMA], + * }) + * class MyModule {} + */ + const MyModule = createNgModule(MyComp); + + TestBed.configureTestingModule({ + imports: [MyModule], + }); - TestBed.configureTestingModule({ - imports: [MyModule], - }); + const fixture = TestBed.createComponent(MyComp); + fixture.detectChanges(); - const fixture = TestBed.createComponent(MyComp); - fixture.detectChanges(); - expect(spy).not.toHaveBeenCalled(); - }); + expect(spy).not.toHaveBeenCalled(); + }); + }); onlyInIvy('unknown element check logs a warning rather than throwing') .it('should not warn about unknown elements with CUSTOM_ELEMENTS_SCHEMA', () => { @@ -472,7 +525,6 @@ describe('NgModule', () => { fixture.detectChanges(); }).not.toThrow(); }); - }); it('should be able to use DI through the NgModuleRef inside the module constructor', () => { @@ -484,7 +536,9 @@ describe('NgModule', () => { providers: [{provide: token, useValue: 'foo'}], }) class TestModule { - constructor(ngRef: NgModuleRef<TestModule>) { value = ngRef.injector.get(token); } + constructor(ngRef: NgModuleRef<TestModule>) { + value = ngRef.injector.get(token); + } } TestBed.configureTestingModule({imports: [TestModule], declarations: [TestCmp]}); @@ -514,5 +568,4 @@ describe('NgModule', () => { TestBed.createComponent(TestCmp); expect(componentInstance).toBeAnInstanceOf(TestCmp); }); - -}); +}); \ No newline at end of file diff --git a/packages/core/test/acceptance/ngdevmode_debug_spec.ts b/packages/core/test/acceptance/ngdevmode_debug_spec.ts index 44027dea6e27c..080c532098d5a 100644 --- a/packages/core/test/acceptance/ngdevmode_debug_spec.ts +++ b/packages/core/test/acceptance/ngdevmode_debug_spec.ts @@ -40,10 +40,9 @@ onlyInIvy('Debug information exist in ivy only').describe('ngDevMode debug', () const element: HTMLElement = fixture.nativeElement; fixture.detectChanges(); - const li = element.querySelector('li') !; + const li = element.querySelector('li')!; const embeddedLView = loadLContext(li).lView; expect(embeddedLView.constructor.name).toEqual('LEmbeddedView_MyApp_li_1'); - }); }); }); diff --git a/packages/core/test/acceptance/outputs_spec.ts b/packages/core/test/acceptance/outputs_spec.ts index 873b6633888f3..f99f3f26eff45 100644 --- a/packages/core/test/acceptance/outputs_spec.ts +++ b/packages/core/test/acceptance/outputs_spec.ts @@ -13,29 +13,27 @@ import {TestBed} from '@angular/core/testing'; describe('outputs', () => { @Component({selector: 'button-toggle', template: ''}) class ButtonToggle { - @Output('change') - change = new EventEmitter(); + @Output('change') change = new EventEmitter(); - @Output('reset') - resetStream = new EventEmitter(); + @Output('reset') resetStream = new EventEmitter(); } @Directive({selector: '[otherDir]'}) class OtherDir { - @Output('change') - changeStream = new EventEmitter(); + @Output('change') changeStream = new EventEmitter(); } @Component({selector: 'destroy-comp', template: ''}) class DestroyComp implements OnDestroy { events: string[] = []; - ngOnDestroy() { this.events.push('destroy'); } + ngOnDestroy() { + this.events.push('destroy'); + } } @Directive({selector: '[myButton]'}) class MyButton { - @Output() - click = new EventEmitter(); + @Output() click = new EventEmitter(); } it('should call component output function when event is emitted', () => { @@ -43,8 +41,10 @@ describe('outputs', () => { @Component({template: '<button-toggle (change)="onChange()"></button-toggle>'}) class App { - @ViewChild(ButtonToggle) buttonToggle !: ButtonToggle; - onChange() { counter++; } + @ViewChild(ButtonToggle) buttonToggle!: ButtonToggle; + onChange() { + counter++; + } } TestBed.configureTestingModule({declarations: [App, ButtonToggle]}); const fixture = TestBed.createComponent(App); @@ -64,9 +64,13 @@ describe('outputs', () => { @Component( {template: '<button-toggle (change)="onChange()" (reset)="onReset()"></button-toggle>'}) class App { - @ViewChild(ButtonToggle) buttonToggle !: ButtonToggle; - onChange() { counter++; } - onReset() { resetCounter++; } + @ViewChild(ButtonToggle) buttonToggle!: ButtonToggle; + onChange() { + counter++; + } + onReset() { + resetCounter++; + } } TestBed.configureTestingModule({declarations: [App, ButtonToggle]}); const fixture = TestBed.createComponent(App); @@ -82,7 +86,7 @@ describe('outputs', () => { it('should eval component output expression when event is emitted', () => { @Component({template: '<button-toggle (change)="counter = counter + 1"></button-toggle>'}) class App { - @ViewChild(ButtonToggle) buttonToggle !: ButtonToggle; + @ViewChild(ButtonToggle) buttonToggle!: ButtonToggle; counter = 0; } TestBed.configureTestingModule({declarations: [App, ButtonToggle]}); @@ -102,10 +106,12 @@ describe('outputs', () => { @Component( {template: '<button-toggle *ngIf="condition" (change)="onChange()"></button-toggle>'}) class App { - @ViewChild(ButtonToggle) buttonToggle !: ButtonToggle; + @ViewChild(ButtonToggle) buttonToggle!: ButtonToggle; condition = true; - onChange() { counter++; } + onChange() { + counter++; + } } TestBed.configureTestingModule({imports: [CommonModule], declarations: [App, ButtonToggle]}); const fixture = TestBed.createComponent(App); @@ -133,11 +139,13 @@ describe('outputs', () => { ` }) class App { - @ViewChild(ButtonToggle) buttonToggle !: ButtonToggle; + @ViewChild(ButtonToggle) buttonToggle!: ButtonToggle; condition = true; condition2 = true; - onChange() { counter++; } + onChange() { + counter++; + } } TestBed.configureTestingModule({imports: [CommonModule], declarations: [App, ButtonToggle]}); const fixture = TestBed.createComponent(App); @@ -168,12 +176,16 @@ describe('outputs', () => { ` }) class App { - @ViewChild(ButtonToggle) buttonToggle !: ButtonToggle; - @ViewChild(DestroyComp) destroyComp !: DestroyComp; + @ViewChild(ButtonToggle) buttonToggle!: ButtonToggle; + @ViewChild(DestroyComp) destroyComp!: DestroyComp; condition = true; - onClick() { clickCounter++; } - onChange() { changeCounter++; } + onClick() { + clickCounter++; + } + onChange() { + changeCounter++; + } } TestBed.configureTestingModule( {imports: [CommonModule], declarations: [App, ButtonToggle, DestroyComp]}); @@ -206,8 +218,10 @@ describe('outputs', () => { @Component({template: '<button myButton (click)="onClick()">Click me</button>'}) class App { - @ViewChild(MyButton) buttonDir !: MyButton; - onClick() { counter++; } + @ViewChild(MyButton) buttonDir!: MyButton; + onClick() { + counter++; + } } TestBed.configureTestingModule({declarations: [App, MyButton]}); const fixture = TestBed.createComponent(App); @@ -228,9 +242,11 @@ describe('outputs', () => { @Component({template: '<button-toggle (change)="onChange()" otherDir></button-toggle>'}) class App { - @ViewChild(ButtonToggle) buttonToggle !: ButtonToggle; - @ViewChild(OtherDir) otherDir !: OtherDir; - onChange() { counter++; } + @ViewChild(ButtonToggle) buttonToggle!: ButtonToggle; + @ViewChild(OtherDir) otherDir!: OtherDir; + onChange() { + counter++; + } } TestBed.configureTestingModule({declarations: [App, ButtonToggle, OtherDir]}); const fixture = TestBed.createComponent(App); @@ -248,8 +264,7 @@ describe('outputs', () => { @Directive({selector: '[otherChangeDir]'}) class OtherChangeDir { - @Input() - change !: boolean; + @Input() change!: boolean; } @Component({ @@ -257,11 +272,13 @@ describe('outputs', () => { '<button-toggle (change)="onChange()" otherChangeDir [change]="change"></button-toggle>' }) class App { - @ViewChild(ButtonToggle) buttonToggle !: ButtonToggle; - @ViewChild(OtherChangeDir) otherDir !: OtherChangeDir; + @ViewChild(ButtonToggle) buttonToggle!: ButtonToggle; + @ViewChild(OtherChangeDir) otherDir!: OtherChangeDir; change = true; - onChange() { counter++; } + onChange() { + counter++; + } } TestBed.configureTestingModule({declarations: [App, ButtonToggle, OtherChangeDir]}); const fixture = TestBed.createComponent(App); @@ -278,5 +295,4 @@ describe('outputs', () => { buttonToggle.change.next(); expect(counter).toBe(1); }); - }); diff --git a/packages/core/test/acceptance/pipe_spec.ts b/packages/core/test/acceptance/pipe_spec.ts index 49c020337b9c3..f0e610921f10c 100644 --- a/packages/core/test/acceptance/pipe_spec.ts +++ b/packages/core/test/acceptance/pipe_spec.ts @@ -14,7 +14,9 @@ describe('pipe', () => { @Pipe({name: 'countingPipe'}) class CountingPipe implements PipeTransform { state: number = 0; - transform(value: any) { return `${value} state:${this.state++}`; } + transform(value: any) { + return `${value} state:${this.state++}`; + } } @Pipe({name: 'multiArgPipe'}) @@ -57,20 +59,21 @@ describe('pipe', () => { it('should support bindings', () => { @Directive({selector: '[my-dir]'}) class Dir { - @Input() - dirProp: string = ''; + @Input() dirProp: string = ''; } @Pipe({name: 'double'}) class DoublePipe implements PipeTransform { - transform(value: any) { return `${value}${value}`; } + transform(value: any) { + return `${value}${value}`; + } } @Component({ template: `<div my-dir [dirProp]="'a'|double"></div>`, }) class App { - @ViewChild(Dir) directive !: Dir; + @ViewChild(Dir) directive!: Dir; } TestBed.configureTestingModule({declarations: [App, DoublePipe, Dir]}); @@ -113,7 +116,9 @@ describe('pipe', () => { it('should pick a Pipe defined in `declarations` over imported Pipes', () => { @Pipe({name: 'number'}) class PipeA implements PipeTransform { - transform(value: any) { return `PipeA: ${value}`; } + transform(value: any) { + return `PipeA: ${value}`; + } } @NgModule({ @@ -125,7 +130,9 @@ describe('pipe', () => { @Pipe({name: 'number'}) class PipeB implements PipeTransform { - transform(value: any) { return `PipeB: ${value}`; } + transform(value: any) { + return `PipeB: ${value}`; + } } @Component({ @@ -150,7 +157,9 @@ describe('pipe', () => { () => { @Pipe({name: 'number'}) class PipeA implements PipeTransform { - transform(value: any) { return `PipeA: ${value}`; } + transform(value: any) { + return `PipeA: ${value}`; + } } @NgModule({ @@ -162,7 +171,9 @@ describe('pipe', () => { @Pipe({name: 'number'}) class PipeB implements PipeTransform { - transform(value: any) { return `PipeB: ${value}`; } + transform(value: any) { + return `PipeB: ${value}`; + } } @NgModule({ @@ -222,12 +233,16 @@ describe('pipe', () => { it('should support duplicates by using the later entry', () => { @Pipe({name: 'duplicatePipe'}) class DuplicatePipe1 implements PipeTransform { - transform(value: any) { return `${value} from duplicate 1`; } + transform(value: any) { + return `${value} from duplicate 1`; + } } @Pipe({name: 'duplicatePipe'}) class DuplicatePipe2 implements PipeTransform { - transform(value: any) { return `${value} from duplicate 2`; } + transform(value: any) { + return `${value} from duplicate 2`; + } } @Component({ @@ -247,7 +262,9 @@ describe('pipe', () => { it('should support pipe in context of ternary operator', () => { @Pipe({name: 'pipe'}) class MyPipe implements PipeTransform { - transform(value: any): any { return value; } + transform(value: any): any { + return value; + } } @Component({ @@ -313,8 +330,12 @@ describe('pipe', () => { @Pipe({name: 'countingImpurePipe', pure: false}) class CountingImpurePipe implements PipeTransform { state: number = 0; - transform(value: any) { return `${value} state:${this.state++}`; } - constructor() { impurePipeInstances.push(this); } + transform(value: any) { + return `${value} state:${this.state++}`; + } + constructor() { + impurePipeInstances.push(this); + } } beforeEach(() => impurePipeInstances = []); @@ -372,8 +393,12 @@ describe('pipe', () => { @Pipe({name: 'pipeWithOnDestroy'}) class PipeWithOnDestroy implements PipeTransform, OnDestroy { - ngOnDestroy() { destroyCalls++; } - transform(value: any): any { return null; } + ngOnDestroy() { + destroyCalls++; + } + transform(value: any): any { + return null; + } } @Component({ @@ -401,7 +426,9 @@ describe('pipe', () => { @Pipe({name: 'myConcatPipe'}) class ConcatPipe implements PipeTransform { constructor(public service: Service) {} - transform(value: string): string { return `${value} - ${this.service.title}`; } + transform(value: string): string { + return `${value} - ${this.service.title}`; + } } @Component({ @@ -428,7 +455,9 @@ describe('pipe', () => { @Pipe({name: 'myConcatPipe'}) class ConcatPipe implements PipeTransform { constructor(@Inject(token) public service: Service) {} - transform(value: string): string { return `${value} - ${this.service.title}`; } + transform(value: string): string { + return `${value} - ${this.service.title}`; + } } @Component({ @@ -461,7 +490,9 @@ describe('pipe', () => { @Pipe({name: 'myConcatPipe'}) class ConcatPipe implements PipeTransform { constructor(public service: Service) {} - transform(value: string): string { return `${value} - ${this.service.title}`; } + transform(value: string): string { + return `${value} - ${this.service.title}`; + } } @Component({ @@ -501,7 +532,7 @@ describe('pipe', () => { }) class App { @Input() something: any; - @ViewChild(SomeComp) comp !: SomeComp; + @ViewChild(SomeComp) comp!: SomeComp; pipeValue = 10; displayValue = 0; } @@ -512,7 +543,9 @@ describe('pipe', () => { pipeChangeDetectorRef = changeDetectorRef; } - transform() { return ''; } + transform() { + return ''; + } } TestBed.configureTestingModule({declarations: [App, SomeComp, TestPipe]}); @@ -521,7 +554,7 @@ describe('pipe', () => { fixture.componentInstance.displayValue = 1; fixture.componentInstance.comp.displayValue = 1; - pipeChangeDetectorRef !.markForCheck(); + pipeChangeDetectorRef!.markForCheck(); fixture.detectChanges(); expect(fixture.nativeElement.textContent).toContain('Outer value: "1"'); @@ -553,7 +586,7 @@ describe('pipe', () => { }) class App { @Input() something: any; - @ViewChild(SomeComp) comp !: SomeComp; + @ViewChild(SomeComp) comp!: SomeComp; pipeValue = 10; displayValue = 0; } @@ -564,7 +597,9 @@ describe('pipe', () => { pipeChangeDetectorRef = changeDetectorRef; } - transform() { return ''; } + transform() { + return ''; + } } TestBed.configureTestingModule({declarations: [App, SomeComp, TestPipe]}); @@ -573,22 +608,21 @@ describe('pipe', () => { fixture.componentInstance.displayValue = 1; fixture.componentInstance.comp.displayValue = 1; - pipeChangeDetectorRef !.markForCheck(); + pipeChangeDetectorRef!.markForCheck(); fixture.detectChanges(); expect(fixture.nativeElement.textContent).toContain('Outer value: "1"'); expect(fixture.nativeElement.textContent).toContain('Inner value: "0"'); }); - }); describe('pure pipe error handling', () => { - it('should not re-invoke pure pipes if it fails initially', () => { - @Pipe({name: 'throwPipe', pure: true}) class ThrowPipe implements PipeTransform { - transform(): never { throw new Error('ThrowPipeError'); } + transform(): never { + throw new Error('ThrowPipeError'); + } } @Component({template: `{{val | throwPipe}}`}) class App { @@ -607,7 +641,6 @@ describe('pipe', () => { it('should display the last known result from a pure pipe when it throws', () => { - @Pipe({name: 'throwPipe', pure: true}) class ThrowPipe implements PipeTransform { transform(value: string): string { @@ -647,7 +680,8 @@ describe('pipe', () => { describe('pure pipe error handling with multiple arguments', () => { const args: string[] = new Array(10).fill(':0'); for (let numberOfPipeArgs = 0; numberOfPipeArgs < args.length; numberOfPipeArgs++) { - it(`should not invoke ${numberOfPipeArgs} argument pure pipe second time if it throws unless input changes`, + it(`should not invoke ${ + numberOfPipeArgs} argument pure pipe second time if it throws unless input changes`, () => { // https://stackblitz.com/edit/angular-mbx2pg const log: string[] = []; @@ -685,8 +719,5 @@ describe('pipe', () => { }); } }); - }); - - }); diff --git a/packages/core/test/acceptance/property_binding_spec.ts b/packages/core/test/acceptance/property_binding_spec.ts index a2343c9009ca2..5b465d1c05e73 100644 --- a/packages/core/test/acceptance/property_binding_spec.ts +++ b/packages/core/test/acceptance/property_binding_spec.ts @@ -91,7 +91,6 @@ describe('property bindings', () => { it('should not map properties whose names do not correspond to their attribute names, ' + 'if they correspond to inputs', () => { - @Component({template: '', selector: 'my-comp'}) class MyComp { @Input() for !:string; @@ -393,7 +392,6 @@ describe('property bindings', () => { }); describe('attributes and input properties', () => { - @Directive({selector: '[myDir]', exportAs: 'myDir'}) class MyDir { @Input() role: string|undefined; @@ -596,7 +594,6 @@ describe('property bindings', () => { expect(comp2.children[0].getAttribute('role')).toBe('button'); expect(comp2.textContent).toBe('role: button'); }); - }); it('should not throw on synthetic property bindings when a directive on the same element injects ViewContainerRef', @@ -626,5 +623,4 @@ describe('property bindings', () => { fixture.detectChanges(); }).not.toThrow(); }); - }); diff --git a/packages/core/test/acceptance/property_interpolation_spec.ts b/packages/core/test/acceptance/property_interpolation_spec.ts index 0330f08a2a8b7..c3b5dfb03014b 100644 --- a/packages/core/test/acceptance/property_interpolation_spec.ts +++ b/packages/core/test/acceptance/property_interpolation_spec.ts @@ -8,7 +8,7 @@ import {Component} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; -import {of } from 'rxjs'; +import {of} from 'rxjs'; describe('property interpolation', () => { it('should handle all flavors of interpolated properties', () => { @@ -67,7 +67,7 @@ describe('property interpolation', () => { ` }) class App { - details = of ({ + details = of({ title: 'cool image', url: 'http://somecooldomain:1234/cool_image.png', }); @@ -93,7 +93,11 @@ describe('property interpolation', () => { /** Clearly this is a doctor of heavy metals. */ leadSurgeon = { getCommonInfo() { - return {getPhotoUrl() { return 'http://somecooldomain:1234/cool_image.png'; }}; + return { + getPhotoUrl() { + return 'http://somecooldomain:1234/cool_image.png'; + } + }; } }; } diff --git a/packages/core/test/acceptance/providers_spec.ts b/packages/core/test/acceptance/providers_spec.ts index 0c46b74098718..90a40decf60a7 100644 --- a/packages/core/test/acceptance/providers_spec.ts +++ b/packages/core/test/acceptance/providers_spec.ts @@ -6,16 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, Inject, Injectable, InjectionToken, Injector, NgModule, Optional, forwardRef} from '@angular/core'; -import {TestBed, async, inject} from '@angular/core/testing'; +import {Component, Directive, forwardRef, Inject, Injectable, InjectionToken, Injector, NgModule, Optional} from '@angular/core'; +import {async, inject, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; -import {onlyInIvy} from '@angular/private/testing'; +import {modifiedInIvy, onlyInIvy} from '@angular/private/testing'; describe('providers', () => { - describe('inheritance', () => { - it('should NOT inherit providers', () => { const SOME_DIRS = new InjectionToken('someDirs'); @@ -52,7 +50,6 @@ describe('providers', () => { expect(otherDir.dirs.length).toEqual(1); expect(otherDir.dirs[0] instanceof SubDirective).toBe(true); }); - }); describe('lifecycles', () => { @@ -61,7 +58,9 @@ describe('providers', () => { @Injectable() class SuperInjectableWithDestroyHook { - ngOnDestroy() { logs.push('OnDestroy'); } + ngOnDestroy() { + logs.push('OnDestroy'); + } } @Injectable() @@ -86,7 +85,9 @@ describe('providers', () => { @Injectable() class InjectableWithDestroyHook { - ngOnDestroy() { logs.push('OnDestroy'); } + ngOnDestroy() { + logs.push('OnDestroy'); + } } @Component({template: '', providers: [InjectableWithDestroyHook]}) @@ -106,7 +107,9 @@ describe('providers', () => { @Injectable() class InjectableWithDestroyHook { - ngOnDestroy() { logs.push('OnDestroy'); } + ngOnDestroy() { + logs.push('OnDestroy'); + } } @Component({selector: 'my-cmp', template: ''}) @@ -137,7 +140,9 @@ describe('providers', () => { @Injectable() class InjectableWithDestroyHook { - ngOnDestroy() { logs.push('OnDestroy'); } + ngOnDestroy() { + logs.push('OnDestroy'); + } } @Component({ @@ -162,12 +167,16 @@ describe('providers', () => { @Injectable() class InjectableWithDestroyHookToken { - ngOnDestroy() { logs.push('OnDestroy Token'); } + ngOnDestroy() { + logs.push('OnDestroy Token'); + } } @Injectable() class InjectableWithDestroyHookValue { - ngOnDestroy() { logs.push('OnDestroy Value'); } + ngOnDestroy() { + logs.push('OnDestroy Value'); + } } @Component({ @@ -193,21 +202,23 @@ describe('providers', () => { @Injectable() class InjectableWithDestroyHookToken { - ngOnDestroy() { logs.push('OnDestroy Token'); } + ngOnDestroy() { + logs.push('OnDestroy Token'); + } } @Injectable() class InjectableWithDestroyHookExisting { - ngOnDestroy() { logs.push('OnDestroy Existing'); } + ngOnDestroy() { + logs.push('OnDestroy Existing'); + } } @Component({ template: '', providers: [ - InjectableWithDestroyHookExisting, { - provide: InjectableWithDestroyHookToken, - useExisting: InjectableWithDestroyHookExisting - } + InjectableWithDestroyHookExisting, + {provide: InjectableWithDestroyHookToken, useExisting: InjectableWithDestroyHookExisting} ] }) class App { @@ -225,30 +236,40 @@ describe('providers', () => { it('should invoke ngOnDestroy with the correct context when providing a type provider multiple times on the same node', () => { - const resolvedServices: (DestroyService | undefined)[] = []; - const destroyContexts: (DestroyService | undefined)[] = []; + const resolvedServices: (DestroyService|undefined)[] = []; + const destroyContexts: (DestroyService|undefined)[] = []; let parentService: DestroyService|undefined; let childService: DestroyService|undefined; @Injectable() class DestroyService { - constructor() { resolvedServices.push(this); } - ngOnDestroy() { destroyContexts.push(this); } + constructor() { + resolvedServices.push(this); + } + ngOnDestroy() { + destroyContexts.push(this); + } } @Directive({selector: '[dir-one]', providers: [DestroyService]}) class DirOne { - constructor(service: DestroyService) { childService = service; } + constructor(service: DestroyService) { + childService = service; + } } @Directive({selector: '[dir-two]', providers: [DestroyService]}) class DirTwo { - constructor(service: DestroyService) { childService = service; } + constructor(service: DestroyService) { + childService = service; + } } @Component({template: '<div dir-one dir-two></div>', providers: [DestroyService]}) class App { - constructor(service: DestroyService) { parentService = service; } + constructor(service: DestroyService) { + parentService = service; + } } TestBed.configureTestingModule({declarations: [App, DirOne, DirTwo]}); @@ -266,28 +287,36 @@ describe('providers', () => { onlyInIvy('Destroy hook of useClass provider is invoked correctly') .it('should invoke ngOnDestroy with the correct context when providing a class provider multiple times on the same node', () => { - const resolvedServices: (DestroyService | undefined)[] = []; - const destroyContexts: (DestroyService | undefined)[] = []; + const resolvedServices: (DestroyService|undefined)[] = []; + const destroyContexts: (DestroyService|undefined)[] = []; const token = new InjectionToken<any>('token'); let parentService: DestroyService|undefined; let childService: DestroyService|undefined; @Injectable() class DestroyService { - constructor() { resolvedServices.push(this); } - ngOnDestroy() { destroyContexts.push(this); } + constructor() { + resolvedServices.push(this); + } + ngOnDestroy() { + destroyContexts.push(this); + } } @Directive( {selector: '[dir-one]', providers: [{provide: token, useClass: DestroyService}]}) class DirOne { - constructor(@Inject(token) service: DestroyService) { childService = service; } + constructor(@Inject(token) service: DestroyService) { + childService = service; + } } @Directive( {selector: '[dir-two]', providers: [{provide: token, useClass: DestroyService}]}) class DirTwo { - constructor(@Inject(token) service: DestroyService) { childService = service; } + constructor(@Inject(token) service: DestroyService) { + childService = service; + } } @Component({ @@ -295,7 +324,9 @@ describe('providers', () => { providers: [{provide: token, useClass: DestroyService}] }) class App { - constructor(@Inject(token) service: DestroyService) { parentService = service; } + constructor(@Inject(token) service: DestroyService) { + parentService = service; + } } TestBed.configureTestingModule({declarations: [App, DirOne, DirTwo]}); @@ -310,21 +341,151 @@ describe('providers', () => { expect(destroyContexts).toEqual([parentService, childService]); }); + onlyInIvy('ngOnDestroy hooks for multi providers were not supported in ViewEngine') + .describe('ngOnDestroy on multi providers', () => { + it('should invoke ngOnDestroy on multi providers with the correct context', () => { + const destroyCalls: any[] = []; + const SERVICES = new InjectionToken<any>('SERVICES'); + + @Injectable() + class DestroyService { + ngOnDestroy() { + destroyCalls.push(this); + } + } + + @Injectable() + class OtherDestroyService { + ngOnDestroy() { + destroyCalls.push(this); + } + } + + @Component({ + template: '<div></div>', + providers: [ + {provide: SERVICES, useClass: DestroyService, multi: true}, + {provide: SERVICES, useClass: OtherDestroyService, multi: true}, + ] + }) + class App { + constructor(@Inject(SERVICES) s: any) {} + } + + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + fixture.destroy(); + + expect(destroyCalls).toEqual([ + jasmine.any(DestroyService), jasmine.any(OtherDestroyService) + ]); + }); + + it('should invoke destroy hooks on multi providers with the correct context, if only some have a destroy hook', + () => { + const destroyCalls: any[] = []; + const SERVICES = new InjectionToken<any>('SERVICES'); + + @Injectable() + class Service1 { + } + + @Injectable() + class Service2 { + ngOnDestroy() { + destroyCalls.push(this); + } + } + + @Injectable() + class Service3 { + } + + @Injectable() + class Service4 { + ngOnDestroy() { + destroyCalls.push(this); + } + } + + @Component({ + template: '<div></div>', + providers: [ + {provide: SERVICES, useClass: Service1, multi: true}, + {provide: SERVICES, useClass: Service2, multi: true}, + {provide: SERVICES, useClass: Service3, multi: true}, + {provide: SERVICES, useClass: Service4, multi: true}, + ] + }) + class App { + constructor(@Inject(SERVICES) s: any) {} + } + + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + fixture.destroy(); + + expect(destroyCalls).toEqual([jasmine.any(Service2), jasmine.any(Service4)]); + }); + + it('should not invoke ngOnDestroy on multi providers created via useFactory', () => { + let destroyCalls = 0; + const SERVICES = new InjectionToken<any>('SERVICES'); + + @Injectable() + class DestroyService { + ngOnDestroy() { + destroyCalls++; + } + } + + @Injectable() + class OtherDestroyService { + ngOnDestroy() { + destroyCalls++; + } + } + + @Component({ + template: '<div></div>', + providers: [ + {provide: SERVICES, useFactory: () => new DestroyService(), multi: true}, + {provide: SERVICES, useFactory: () => new OtherDestroyService(), multi: true}, + ] + }) + class App { + constructor(@Inject(SERVICES) s: any) {} + } + + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + fixture.destroy(); + + expect(destroyCalls).toBe(0); + }); + }); + + modifiedInIvy('ViewEngine did not support destroy hooks on multi providers') .it('should not invoke ngOnDestroy on multi providers', () => { - // TODO(FW-1866): currently we only assert that the hook was called, - // but we should also be checking that the correct context was passed in. let destroyCalls = 0; const SERVICES = new InjectionToken<any>('SERVICES'); @Injectable() class DestroyService { - ngOnDestroy() { destroyCalls++; } + ngOnDestroy() { + destroyCalls++; + } } @Injectable() class OtherDestroyService { - ngOnDestroy() { destroyCalls++; } + ngOnDestroy() { + destroyCalls++; + } } @Component({ @@ -343,13 +504,11 @@ describe('providers', () => { fixture.detectChanges(); fixture.destroy(); - expect(destroyCalls).toBe(2); + expect(destroyCalls).toBe(0); }); - }); describe('components and directives', () => { - class MyService { value = 'some value'; } @@ -409,7 +568,6 @@ describe('providers', () => { }); describe('forward refs', () => { - it('should support forward refs in provider deps', () => { class MyService { constructor(public dep: {value: string}) {} @@ -444,7 +602,6 @@ describe('providers', () => { }); it('should support forward refs in useClass when impl version is also provided', () => { - @Injectable({providedIn: 'root', useClass: forwardRef(() => SomeProviderImpl)}) abstract class SomeProvider { } @@ -490,11 +647,9 @@ describe('providers', () => { expect(fixture.componentInstance.foo).toBeAnInstanceOf(SomeProviderImpl); }); - }); describe('flags', () => { - class MyService { constructor(public value: OtherService|null) {} } diff --git a/packages/core/test/acceptance/pure_function_spec.ts b/packages/core/test/acceptance/pure_function_spec.ts index 8b03174a16681..406e8b185a7af 100644 --- a/packages/core/test/acceptance/pure_function_spec.ts +++ b/packages/core/test/acceptance/pure_function_spec.ts @@ -18,8 +18,7 @@ describe('components using pure function instructions internally', () => { template: ``, }) class MyComp { - @Input() - names: string[] = []; + @Input() names: string[] = []; } @@ -60,7 +59,7 @@ describe('components using pure function instructions internally', () => { myComp.names = ['should not be overwritten']; fixture.detectChanges(); - expect(myComp !.names).toEqual(['should not be overwritten']); + expect(myComp!.names).toEqual(['should not be overwritten']); }); @@ -91,11 +90,9 @@ describe('components using pure function instructions internally', () => { template: ``, }) class ManyPropComp { - @Input() - names1: string[] = []; + @Input() names1: string[] = []; - @Input() - names2: string[] = []; + @Input() names2: string[] = []; } @Component({ @@ -117,14 +114,14 @@ describe('components using pure function instructions internally', () => { fixture.detectChanges(); const manyPropComp = fixture.debugElement.query(By.directive(ManyPropComp)).componentInstance; - expect(manyPropComp !.names1).toEqual(['Nancy', 'Carson']); - expect(manyPropComp !.names2).toEqual(['George']); + expect(manyPropComp!.names1).toEqual(['Nancy', 'Carson']); + expect(manyPropComp!.names2).toEqual(['George']); fixture.componentInstance.customName = 'George'; fixture.componentInstance.customName2 = 'Carson'; fixture.detectChanges(); - expect(manyPropComp !.names1).toEqual(['Nancy', 'George']); - expect(manyPropComp !.names2).toEqual(['Carson']); + expect(manyPropComp!.names1).toEqual(['Nancy', 'George']); + expect(manyPropComp!.names2).toEqual(['Carson']); }); @@ -222,7 +219,6 @@ describe('components using pure function instructions internally', () => { it('should work up to 8 bindings', () => { - @Component({ template: ` <my-comp [names]="['a', 'b', 'c', 'd', 'e', 'f', 'g', v8]"></my-comp> @@ -346,8 +342,7 @@ describe('components using pure function instructions internally', () => { template: ``, }) class ObjectComp { - @Input() - config: any = []; + @Input() config: any = []; } it('should support an object literal', () => { @@ -495,7 +490,7 @@ describe('components using pure function instructions internally', () => { ` }) class App { - @ViewChildren(Dir) directives !: QueryList<Dir>; + @ViewChildren(Dir) directives!: QueryList<Dir>; } TestBed.configureTestingModule({declarations: [Dir, App]}); @@ -514,7 +509,7 @@ describe('components using pure function instructions internally', () => { ` }) class App { - @ViewChildren(Dir) directives !: QueryList<Dir>; + @ViewChildren(Dir) directives!: QueryList<Dir>; } TestBed.configureTestingModule({declarations: [Dir, App]}); @@ -528,7 +523,7 @@ describe('components using pure function instructions internally', () => { it('should not share object literals across component instances', () => { @Component({template: `<div [dir]="{}"></div>`}) class App { - @ViewChild(Dir) directive !: Dir; + @ViewChild(Dir) directive!: Dir; } TestBed.configureTestingModule({declarations: [Dir, App]}); @@ -545,7 +540,7 @@ describe('components using pure function instructions internally', () => { it('should not share array literals across component instances', () => { @Component({template: `<div [dir]="[]"></div>`}) class App { - @ViewChild(Dir) directive !: Dir; + @ViewChild(Dir) directive!: Dir; } TestBed.configureTestingModule({declarations: [Dir, App]}); @@ -567,7 +562,7 @@ describe('components using pure function instructions internally', () => { ` }) class App { - @ViewChildren(Dir) directives !: QueryList<Dir>; + @ViewChildren(Dir) directives!: QueryList<Dir>; } TestBed.configureTestingModule({declarations: [Dir, App]}); @@ -586,7 +581,7 @@ describe('components using pure function instructions internally', () => { ` }) class App { - @ViewChildren(Dir) directives !: QueryList<Dir>; + @ViewChildren(Dir) directives!: QueryList<Dir>; } TestBed.configureTestingModule({declarations: [Dir, App]}); @@ -605,8 +600,10 @@ describe('components using pure function instructions internally', () => { ` }) class App { - @ViewChildren(Dir) directives !: QueryList<Dir>; - getFoo() { return 'foo!'; } + @ViewChildren(Dir) directives!: QueryList<Dir>; + getFoo() { + return 'foo!'; + } } TestBed.configureTestingModule({declarations: [Dir, App]}); @@ -616,8 +613,5 @@ describe('components using pure function instructions internally', () => { expect(values).toEqual([{foo: null}, {foo: 'foo!'}]); }); - - - }); }); diff --git a/packages/core/test/acceptance/query_spec.ts b/packages/core/test/acceptance/query_spec.ts index 8ebe7aba626ef..c36f6027fb18b 100644 --- a/packages/core/test/acceptance/query_spec.ts +++ b/packages/core/test/acceptance/query_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {AfterViewInit, Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, ViewRef, forwardRef} from '@angular/core'; +import {AfterViewInit, Component, ContentChild, ContentChildren, Directive, ElementRef, forwardRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, ViewRef} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -215,7 +215,7 @@ describe('query logic', () => { } class MyComp { - @ViewChildren(SomeDir) foo !: QueryList<SomeDir>; + @ViewChildren(SomeDir) foo!: QueryList<SomeDir>; } @Component({ @@ -242,7 +242,7 @@ describe('query logic', () => { } class MySuperComp { - @ViewChildren(SomeDir) foo !: QueryList<SomeDir>; + @ViewChildren(SomeDir) foo!: QueryList<SomeDir>; } class MyComp extends MySuperComp {} @@ -275,7 +275,7 @@ describe('query logic', () => { template: `<ng-container [ngTemplateOutlet]="content"></ng-container>` }) class Insertion { - @Input() content !: TemplateRef<{}>; + @Input() content!: TemplateRef<{}>; } @Component({ @@ -287,7 +287,7 @@ describe('query logic', () => { ` }) class App { - @ViewChild(Required) requiredEl !: Required; + @ViewChild(Required) requiredEl!: Required; viewChildAvailableInAfterViewInit?: boolean; ngAfterViewInit() { @@ -300,7 +300,6 @@ describe('query logic', () => { fixture.detectChanges(); expect(fixture.componentInstance.viewChildAvailableInAfterViewInit).toBe(true); }); - }); describe('content queries', () => { @@ -538,7 +537,7 @@ describe('query logic', () => { @Component({template: '<sub-comp><div #foo></div></sub-comp>'}) class App { - @ViewChild(SubComp) subComp !: SubComp; + @ViewChild(SubComp) subComp!: SubComp; } TestBed.configureTestingModule({declarations: [App, SubComp]}); @@ -561,7 +560,7 @@ describe('query logic', () => { @Component({template: '<sub-comp><div #foo></div></sub-comp>'}) class App { - @ViewChild(SubComp) subComp !: SubComp; + @ViewChild(SubComp) subComp!: SubComp; } TestBed.configureTestingModule({declarations: [App, SubComp]}); @@ -577,7 +576,7 @@ describe('query logic', () => { } class MyComp { - @ContentChildren(SomeDir) foo !: QueryList<SomeDir>; + @ContentChildren(SomeDir) foo!: QueryList<SomeDir>; } @Component({selector: 'sub-comp', template: '<ng-content></ng-content>'}) @@ -593,7 +592,7 @@ describe('query logic', () => { ` }) class App { - @ViewChild(SubComp) subComp !: SubComp; + @ViewChild(SubComp) subComp!: SubComp; } TestBed.configureTestingModule({declarations: [App, SubComp, SomeDir]}); @@ -610,7 +609,7 @@ describe('query logic', () => { } class MySuperComp { - @ContentChildren(SomeDir) foo !: QueryList<SomeDir>; + @ContentChildren(SomeDir) foo!: QueryList<SomeDir>; } class MyComp extends MySuperComp {} @@ -628,7 +627,7 @@ describe('query logic', () => { ` }) class App { - @ViewChild(SubComp) subComp !: SubComp; + @ViewChild(SubComp) subComp!: SubComp; } TestBed.configureTestingModule({declarations: [App, SubComp, SomeDir]}); @@ -657,7 +656,7 @@ describe('query logic', () => { template: '', }) class ShallowComp { - @ContentChildren('foo', {descendants: false}) foos !: QueryList<ElementRef>; + @ContentChildren('foo', {descendants: false}) foos!: QueryList<ElementRef>; } TestBed.configureTestingModule( @@ -666,7 +665,7 @@ describe('query logic', () => { fixture.detectChanges(); const shallowComp = fixture.debugElement.query(By.directive(ShallowComp)).componentInstance; - const queryList = shallowComp !.foos; + const queryList = shallowComp!.foos; expect(queryList.length).toBe(0); fixture.componentInstance.showing = true; @@ -691,24 +690,24 @@ describe('query logic', () => { }); describe('descendants: false (default)', () => { - /** * A helper function to check if a given object looks like ElementRef. It is used in place of * the `instanceof ElementRef` check since ivy returns a type that looks like ElementRef (have * the same properties but doesn't pass the instanceof ElementRef test) */ - function isElementRefLike(result: any): boolean { return result.nativeElement != null; } + function isElementRefLike(result: any): boolean { + return result.nativeElement != null; + } it('should match directives on elements that used to be wrapped by a required parent in HTML parser', () => { - @Directive({selector: '[myDef]'}) class MyDef { } @Component({selector: 'my-container', template: ``}) class MyContainer { - @ContentChildren(MyDef) myDefs !: QueryList<MyDef>; + @ContentChildren(MyDef) myDefs!: QueryList<MyDef>; } @Component( {selector: 'test-cmpt', template: `<my-container><tr myDef></tr></my-container>`}) @@ -724,10 +723,9 @@ describe('query logic', () => { }); it('should match elements with local refs inside <ng-container>', () => { - @Component({selector: 'needs-target', template: ``}) class NeedsTarget { - @ContentChildren('target') targets !: QueryList<ElementRef>; + @ContentChildren('target') targets!: QueryList<ElementRef>; } @Component({ selector: 'test-cmpt', @@ -752,10 +750,9 @@ describe('query logic', () => { }); it('should match elements with local refs inside nested <ng-container>', () => { - @Component({selector: 'needs-target', template: ``}) class NeedsTarget { - @ContentChildren('target') targets !: QueryList<ElementRef>; + @ContentChildren('target') targets!: QueryList<ElementRef>; } @Component({ @@ -791,7 +788,7 @@ describe('query logic', () => { @Component({selector: 'needs-target', template: ``}) class NeedsTarget { - @ContentChildren(TargetDir) targets !: QueryList<HTMLElement>; + @ContentChildren(TargetDir) targets!: QueryList<HTMLElement>; } @Component({ @@ -823,7 +820,7 @@ describe('query logic', () => { @Component({selector: 'needs-target', template: ``}) class NeedsTarget { - @ContentChildren(TargetDir) targets !: QueryList<HTMLElement>; + @ContentChildren(TargetDir) targets!: QueryList<HTMLElement>; } @Component({ @@ -859,7 +856,7 @@ describe('query logic', () => { @Directive({selector: '[needs-target]'}) class NeedsTarget { - @ContentChildren(TargetDir) targets !: QueryList<HTMLElement>; + @ContentChildren(TargetDir) targets!: QueryList<HTMLElement>; } @Component({ @@ -893,8 +890,8 @@ describe('query logic', () => { @Component({selector: 'needs-target', template: ``}) class NeedsTarget { - @ContentChildren(TargetDir) dirTargets !: QueryList<TargetDir>; - @ContentChildren('target') localRefsTargets !: QueryList<ElementRef>; + @ContentChildren(TargetDir) dirTargets!: QueryList<TargetDir>; + @ContentChildren('target') localRefsTargets!: QueryList<ElementRef>; } @Component({ @@ -931,7 +928,7 @@ describe('query logic', () => { @Component({selector: 'needs-target', template: ``}) class NeedsTarget { - @ContentChildren(TargetDir) targets !: QueryList<HTMLElement>; + @ContentChildren(TargetDir) targets!: QueryList<HTMLElement>; } @Component({ @@ -963,7 +960,6 @@ describe('query logic', () => { describe('observable interface', () => { - it('should allow observing changes to query list', () => { const fixture = TestBed.createComponent(QueryCompWithChanges); let changes = 0; @@ -983,13 +979,10 @@ describe('query logic', () => { fixture.detectChanges(); expect(changes).toBe(1); }); - }); describe('view boundaries', () => { - describe('ViewContainerRef', () => { - @Directive({selector: '[vc]', exportAs: 'vc'}) class ViewContainerManipulatorDirective { constructor(private _vcRef: ViewContainerRef) {} @@ -998,9 +991,13 @@ describe('query logic', () => { return this._vcRef.createEmbeddedView(tpl, ctx, idx); } - remove(index?: number) { this._vcRef.remove(index); } + remove(index?: number) { + this._vcRef.remove(index); + } - move(viewRef: ViewRef, index: number) { this._vcRef.move(viewRef, index); } + move(viewRef: ViewRef, index: number) { + this._vcRef.move(viewRef, index); + } } it('should report results in views inserted / removed by ngIf', () => { @@ -1014,7 +1011,7 @@ describe('query logic', () => { }) class TestComponent { value: boolean = false; - @ViewChildren('foo') query !: QueryList<any>; + @ViewChildren('foo') query!: QueryList<any>; } TestBed.configureTestingModule({declarations: [TestComponent]}); @@ -1045,7 +1042,7 @@ describe('query logic', () => { }) class TestComponent { value: string[]|undefined; - @ViewChildren('foo') query !: QueryList<any>; + @ViewChildren('foo') query!: QueryList<any>; } TestBed.configureTestingModule({declarations: [TestComponent]}); @@ -1082,7 +1079,6 @@ describe('query logic', () => { */ it('should notify on changes when a given view is removed and re-inserted at the same index', () => { - @Component({ selector: 'test-comp', template: ` @@ -1093,10 +1089,9 @@ describe('query logic', () => { class TestComponent implements AfterViewInit { queryListNotificationCounter = 0; - @ViewChild(ViewContainerManipulatorDirective) - vc !: ViewContainerManipulatorDirective; - @ViewChild('tpl') tpl !: TemplateRef<any>; - @ViewChildren('foo') query !: QueryList<any>; + @ViewChild(ViewContainerManipulatorDirective) vc!: ViewContainerManipulatorDirective; + @ViewChild('tpl') tpl!: TemplateRef<any>; + @ViewChildren('foo') query!: QueryList<any>; ngAfterViewInit() { this.query.changes.subscribe(() => this.queryListNotificationCounter++); @@ -1125,13 +1120,13 @@ describe('query logic', () => { it('should support a mix of content queries from the declaration and embedded view', () => { @Directive({selector: '[query-for-lots-of-content]'}) class QueryForLotsOfContent { - @ContentChildren('foo', {descendants: true}) foos1 !: QueryList<ElementRef>; - @ContentChildren('foo', {descendants: true}) foos2 !: QueryList<ElementRef>; + @ContentChildren('foo', {descendants: true}) foos1!: QueryList<ElementRef>; + @ContentChildren('foo', {descendants: true}) foos2!: QueryList<ElementRef>; } @Directive({selector: '[query-for-content]'}) class QueryForContent { - @ContentChildren('foo') foos !: QueryList<ElementRef>; + @ContentChildren('foo') foos!: QueryList<ElementRef>; } @Component({ @@ -1192,11 +1187,10 @@ describe('query logic', () => { `, }) class TestComponent { - @ViewChild(ViewContainerManipulatorDirective) - vc !: ViewContainerManipulatorDirective; - @ViewChild('tpl1') tpl1 !: TemplateRef<any>; - @ViewChild('tpl2') tpl2 !: TemplateRef<any>; - @ViewChildren('foo') query !: QueryList<any>; + @ViewChild(ViewContainerManipulatorDirective) vc!: ViewContainerManipulatorDirective; + @ViewChild('tpl1') tpl1!: TemplateRef<any>; + @ViewChild('tpl2') tpl2!: TemplateRef<any>; + @ViewChildren('foo') query!: QueryList<any>; } TestBed.configureTestingModule( @@ -1210,8 +1204,8 @@ describe('query logic', () => { expect(queryList.length).toBe(1); expect(queryList.first.nativeElement.getAttribute('id')).toBe('middle'); - vc.insertTpl(tpl1 !, {idx: 0}, 0); - vc.insertTpl(tpl2 !, {idx: 1}, 1); + vc.insertTpl(tpl1!, {idx: 0}, 0); + vc.insertTpl(tpl2!, {idx: 1}, 1); fixture.detectChanges(); expect(queryList.length).toBe(3); @@ -1220,7 +1214,7 @@ describe('query logic', () => { expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle'); expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1'); - vc.insertTpl(tpl1 !, {idx: 1}, 1); + vc.insertTpl(tpl1!, {idx: 1}, 1); fixture.detectChanges(); expect(queryList.length).toBe(4); @@ -1264,10 +1258,10 @@ describe('query logic', () => { `, }) class TestComponent { - @ViewChild('tpl') tpl !: TemplateRef<any>; - @ViewChild('vi0') vi0 !: ViewContainerManipulatorDirective; - @ViewChild('vi1') vi1 !: ViewContainerManipulatorDirective; - @ViewChildren('foo') query !: QueryList<any>; + @ViewChild('tpl') tpl!: TemplateRef<any>; + @ViewChild('vi0') vi0!: ViewContainerManipulatorDirective; + @ViewChild('vi1') vi1!: ViewContainerManipulatorDirective; + @ViewChildren('foo') query!: QueryList<any>; } TestBed.configureTestingModule( @@ -1280,8 +1274,8 @@ describe('query logic', () => { expect(queryList.length).toBe(0); - vi0.insertTpl(tpl !, {idx: 0, container_idx: 0}, 0); - vi1.insertTpl(tpl !, {idx: 0, container_idx: 1}, 0); + vi0.insertTpl(tpl!, {idx: 0, container_idx: 0}, 0); + vi1.insertTpl(tpl!, {idx: 0, container_idx: 1}, 0); fixture.detectChanges(); expect(queryList.length).toBe(2); @@ -1315,7 +1309,7 @@ describe('query logic', () => { }) class MyApp { show = false; - @ViewChildren('foo') query !: QueryList<any>; + @ViewChildren('foo') query!: QueryList<any>; } TestBed.configureTestingModule({declarations: [MyApp], imports: [CommonModule]}); @@ -1334,14 +1328,11 @@ describe('query logic', () => { fixture.detectChanges(); expect(queryList.length).toBe(0); }); - }); }); describe('non-regression', () => { - it('should query by provider super-type in an embedded view', () => { - @Directive({selector: '[child]'}) class Child { } @@ -1356,7 +1347,7 @@ describe('query logic', () => { `<ng-template [ngIf]="true"><ng-template [ngIf]="true"><div parent></div></ng-template></ng-template>` }) class TestCmpt { - @ViewChildren(Child) instances !: QueryList<Child>; + @ViewChildren(Child) instances!: QueryList<Child>; } TestBed.configureTestingModule({declarations: [TestCmpt, Parent, Child]}); @@ -1367,7 +1358,6 @@ describe('query logic', () => { }); it('should flatten multi-provider results', () => { - class MyClass {} @Component({ @@ -1381,7 +1371,7 @@ describe('query logic', () => { @Component({selector: 'test-cmpt', template: `<with-multi-provider></with-multi-provider>`}) class TestCmpt { - @ViewChildren(MyClass) queryResults !: QueryList<WithMultiProvider>; + @ViewChildren(MyClass) queryResults!: QueryList<WithMultiProvider>; } TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]}); @@ -1393,7 +1383,6 @@ describe('query logic', () => { }); it('should flatten multi-provider results when crossing ng-template', () => { - class MyClass {} @Component({ @@ -1413,7 +1402,7 @@ describe('query logic', () => { ` }) class TestCmpt { - @ViewChildren(MyClass) queryResults !: QueryList<WithMultiProvider>; + @ViewChildren(MyClass) queryResults!: QueryList<WithMultiProvider>; } TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]}); @@ -1444,7 +1433,7 @@ describe('query logic', () => { ` }) class App { - @ViewChild(GroupDir) group !: GroupDir; + @ViewChild(GroupDir) group!: GroupDir; } TestBed.configureTestingModule( @@ -1481,7 +1470,7 @@ describe('query logic', () => { ` }) class App { - @ViewChildren(GroupDir) groups !: QueryList<GroupDir>; + @ViewChildren(GroupDir) groups!: QueryList<GroupDir>; } TestBed.configureTestingModule( @@ -1497,9 +1486,7 @@ describe('query logic', () => { expect(groups[1]).toBeAnInstanceOf(GroupDir); expect(groups[2]).toBeUndefined(); }); - }); - }); function initWithTemplate(compType: Type<any>, template: string) { @@ -1511,11 +1498,11 @@ function initWithTemplate(compType: Type<any>, template: string) { @Component({selector: 'local-ref-query-component', template: '<ng-content></ng-content>'}) class QueryComp { - @ViewChild('viewQuery') viewChild !: any; - @ContentChild('contentQuery') contentChild !: any; + @ViewChild('viewQuery') viewChild!: any; + @ContentChild('contentQuery') contentChild!: any; - @ViewChildren('viewQuery') viewChildren !: QueryList<any>; - @ContentChildren('contentQuery') contentChildren !: QueryList<any>; + @ViewChildren('viewQuery') viewChildren!: QueryList<any>; + @ContentChildren('contentQuery') contentChildren!: QueryList<any>; } @Component({selector: 'app-comp', template: ``}) @@ -1543,12 +1530,14 @@ class TextDirective { ` }) class StaticViewQueryComp { - private _textDir !: TextDirective; - private _foo !: ElementRef; + private _textDir!: TextDirective; + private _foo!: ElementRef; setEvents: string[] = []; @ViewChild(TextDirective, {static: true}) - get textDir(): TextDirective { return this._textDir; } + get textDir(): TextDirective { + return this._textDir; + } set textDir(value: TextDirective) { this.setEvents.push('textDir set'); @@ -1556,7 +1545,9 @@ class StaticViewQueryComp { } @ViewChild('foo') - get foo(): ElementRef { return this._foo; } + get foo(): ElementRef { + return this._foo; + } set foo(value: ElementRef) { this.setEvents.push('foo set'); @@ -1577,22 +1568,22 @@ class StaticViewQueryComp { ` }) class SubclassStaticViewQueryComp extends StaticViewQueryComp { - @ViewChild('bar', {static: true}) - bar !: ElementRef; + @ViewChild('bar', {static: true}) bar!: ElementRef; - @ViewChild('baz') - baz !: ElementRef; + @ViewChild('baz') baz!: ElementRef; } @Component({selector: 'static-content-query-comp', template: `<ng-content></ng-content>`}) class StaticContentQueryComp { - private _textDir !: TextDirective; - private _foo !: ElementRef; + private _textDir!: TextDirective; + private _foo!: ElementRef; setEvents: string[] = []; @ContentChild(TextDirective, {static: true}) - get textDir(): TextDirective { return this._textDir; } + get textDir(): TextDirective { + return this._textDir; + } set textDir(value: TextDirective) { this.setEvents.push('textDir set'); @@ -1600,7 +1591,9 @@ class StaticContentQueryComp { } @ContentChild('foo') - get foo(): ElementRef { return this._foo; } + get foo(): ElementRef { + return this._foo; + } set foo(value: ElementRef) { this.setEvents.push('foo set'); @@ -1610,12 +1603,14 @@ class StaticContentQueryComp { @Directive({selector: '[staticContentQueryDir]'}) class StaticContentQueryDir { - private _textDir !: TextDirective; - private _foo !: ElementRef; + private _textDir!: TextDirective; + private _foo!: ElementRef; setEvents: string[] = []; @ContentChild(TextDirective, {static: true}) - get textDir(): TextDirective { return this._textDir; } + get textDir(): TextDirective { + return this._textDir; + } set textDir(value: TextDirective) { this.setEvents.push('textDir set'); @@ -1623,7 +1618,9 @@ class StaticContentQueryDir { } @ContentChild('foo') - get foo(): ElementRef { return this._foo; } + get foo(): ElementRef { + return this._foo; + } set foo(value: ElementRef) { this.setEvents.push('foo set'); @@ -1633,11 +1630,9 @@ class StaticContentQueryDir { @Component({selector: 'subclass-static-content-query-comp', template: `<ng-content></ng-content>`}) class SubclassStaticContentQueryComp extends StaticContentQueryComp { - @ContentChild('bar', {static: true}) - bar !: ElementRef; + @ContentChild('bar', {static: true}) bar!: ElementRef; - @ContentChild('baz') - baz !: ElementRef; + @ContentChild('baz') baz!: ElementRef; } @Component({ @@ -1647,7 +1642,7 @@ class SubclassStaticContentQueryComp extends StaticContentQueryComp { ` }) export class QueryCompWithChanges { - @ViewChildren('foo') foos !: QueryList<any>; + @ViewChildren('foo') foos!: QueryList<any>; showing = false; } @@ -1658,7 +1653,7 @@ class SuperDirectiveQueryTarget { @Directive({selector: '[super-directive]'}) class SuperDirective { - @ViewChildren(SuperDirectiveQueryTarget) headers !: QueryList<SuperDirectiveQueryTarget>; + @ViewChildren(SuperDirectiveQueryTarget) headers!: QueryList<SuperDirectiveQueryTarget>; } @Component({ diff --git a/packages/core/test/acceptance/renderer_factory_spec.ts b/packages/core/test/acceptance/renderer_factory_spec.ts index f10269cf63b91..856997f47e8dc 100644 --- a/packages/core/test/acceptance/renderer_factory_spec.ts +++ b/packages/core/test/acceptance/renderer_factory_spec.ts @@ -24,19 +24,29 @@ describe('renderer factory lifecycle', () => { @Component({selector: 'some-component', template: `foo`}) class SomeComponent implements DoCheck { - ngOnInit() { logs.push('some_component create'); } - ngDoCheck() { logs.push('some_component update'); } + ngOnInit() { + logs.push('some_component create'); + } + ngDoCheck() { + logs.push('some_component update'); + } } @Component({selector: 'some-component-with-error', template: `With error`}) class SomeComponentWhichThrows { - ngOnInit() { throw new Error('SomeComponentWhichThrows threw'); } + ngOnInit() { + throw new Error('SomeComponentWhichThrows threw'); + } } @Component({selector: 'lol', template: `<some-component></some-component>`}) class TestComponent implements DoCheck { - ngOnInit() { logs.push('test_component create'); } - ngDoCheck() { logs.push('test_component update'); } + ngOnInit() { + logs.push('test_component create'); + } + ngDoCheck() { + logs.push('test_component update'); + } } /** Creates a patched renderer factory that pushes entries to the test log */ @@ -44,7 +54,7 @@ describe('renderer factory lifecycle', () => { let rendererFactory = getRendererFactory2(document); const createRender = rendererFactory.createRenderer; - rendererFactory.createRenderer = (hostElement: any, type: RendererType2 | null) => { + rendererFactory.createRenderer = (hostElement: any, type: RendererType2|null) => { logs.push('create'); return createRender.apply(rendererFactory, [hostElement, type]); }; @@ -168,7 +178,7 @@ describe('animation renderer factory', () => { ]); player.finish(); - rendererFactory !.whenRenderingDone !().then(() => { + rendererFactory!.whenRenderingDone!().then(() => { expect(eventLogs).toEqual(['void - start', 'void - done', 'on - start', 'on - done']); done(); }); diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts index 02397afb41e43..8f1fc30b54320 100644 --- a/packages/core/test/acceptance/styling_spec.ts +++ b/packages/core/test/acceptance/styling_spec.ts @@ -440,11 +440,12 @@ describe('styling', () => { @Directive({selector: '[dir-shadows-class-input]'}) class DirectiveShadowsClassInput { - @Input('class') - klass: string|undefined; + @Input('class') klass: string|undefined; @HostBinding('class') - get hostClasses() { return `${this.klass} SUFFIX`; } + get hostClasses() { + return `${this.klass} SUFFIX`; + } } TestBed.configureTestingModule({declarations: [Cmp, DirectiveShadowsClassInput]}); @@ -671,7 +672,7 @@ describe('styling', () => { it('should be able to bind zero', () => { @Component({template: '<div #div [style.opacity]="opacity"></div>'}) class App { - @ViewChild('div') div !: ElementRef<HTMLElement>; + @ViewChild('div') div!: ElementRef<HTMLElement>; opacity = 0; } @@ -685,7 +686,7 @@ describe('styling', () => { it('should be able to bind a SafeValue to backgroundImage', () => { @Component({template: '<div [style.backgroundImage]="image"></div>'}) class Cmp { - image !: SafeStyle; + image!: SafeStyle; } TestBed.configureTestingModule({declarations: [Cmp]}); @@ -724,7 +725,7 @@ describe('styling', () => { it('should be able to bind a SafeValue to clip-path', () => { @Component({template: '<div [style.clip-path]="path"></div>'}) class Cmp { - path !: SafeStyle; + path!: SafeStyle; } TestBed.configureTestingModule({declarations: [Cmp]}); @@ -1011,15 +1012,15 @@ describe('styling', () => { expect(capturedClassBindingCount).toEqual(1); expect(capturedClassBindingValue as any).toEqual(null); expect(capturedMyClassBindingCount).toEqual(1); - expect(capturedMyClassBindingValue !).toEqual('foo'); + expect(capturedMyClassBindingValue!).toEqual('foo'); fixture.componentInstance.c = 'dynamic-value'; fixture.detectChanges(); expect(capturedClassBindingCount).toEqual(2); - expect(capturedClassBindingValue !).toEqual('dynamic-value'); + expect(capturedClassBindingValue!).toEqual('dynamic-value'); expect(capturedMyClassBindingCount).toEqual(1); - expect(capturedMyClassBindingValue !).toEqual('foo'); + expect(capturedMyClassBindingValue!).toEqual('foo'); fixture.componentInstance.c = null; fixture.detectChanges(); @@ -1027,7 +1028,7 @@ describe('styling', () => { expect(capturedClassBindingCount).toEqual(3); expect(capturedClassBindingValue as any).toEqual(null); expect(capturedMyClassBindingCount).toEqual(1); - expect(capturedMyClassBindingValue !).toEqual('foo'); + expect(capturedMyClassBindingValue!).toEqual('foo'); fixture.componentInstance.c = ''; fixture.detectChanges(); @@ -1035,7 +1036,7 @@ describe('styling', () => { expect(capturedClassBindingCount).toEqual(4); expect(capturedClassBindingValue as any).toEqual(''); expect(capturedMyClassBindingCount).toEqual(1); - expect(capturedMyClassBindingValue !).toEqual('foo'); + expect(capturedMyClassBindingValue!).toEqual('foo'); }); it('should write to [class] binding during `update` mode if there is an instantiation-level value', @@ -1069,7 +1070,7 @@ describe('styling', () => { fixture.detectChanges(); expect(capturedClassBindingCount).toEqual(2); - expect(capturedClassBindingValue !).toEqual('dynamic-bar'); + expect(capturedClassBindingValue!).toEqual('dynamic-bar'); }); it('should write to a `class` input binding if there is a static class value', () => { @@ -1102,9 +1103,9 @@ describe('styling', () => { const fixture = TestBed.createComponent(Cmp); fixture.detectChanges(); - expect(capturedClassBindingValue !).toEqual('static-val'); + expect(capturedClassBindingValue!).toEqual('static-val'); expect(capturedClassBindingCount).toEqual(1); - expect(capturedMyClassBindingValue !).toEqual('foo'); + expect(capturedMyClassBindingValue!).toEqual('foo'); expect(capturedMyClassBindingCount).toEqual(1); }); @@ -1195,19 +1196,19 @@ describe('styling', () => { // would check if we possibly have this situation on every `advance()` // instruction. We don't think this is worth it, and we are just going to live // with this. - ); - expect(capturedClassBindingValue !).toEqual('static-val'); + ); + expect(capturedClassBindingValue!).toEqual('static-val'); expect(capturedMyClassBindingCount).toEqual(1); - expect(capturedMyClassBindingValue !).toEqual('foo'); + expect(capturedMyClassBindingValue!).toEqual('foo'); capturedClassBindingCount = 0; fixture.componentInstance.c = 'dynamic-val'; fixture.detectChanges(); expect(capturedClassBindingCount).toEqual(1); - expect(capturedClassBindingValue !).toEqual('static-val dynamic-val'); + expect(capturedClassBindingValue!).toEqual('static-val dynamic-val'); expect(capturedMyClassBindingCount).toEqual(1); - expect(capturedMyClassBindingValue !).toEqual('foo'); + expect(capturedMyClassBindingValue!).toEqual('foo'); }); onlyInIvy('only ivy balances styling across directives and component host bindings') @@ -1240,7 +1241,6 @@ describe('styling', () => { }); describe('NgClass', () => { - // We had a bug where NgClass would not allocate sufficient slots for host bindings, // so it would overwrite information about other directives nearby. This test checks // that TestDir's injector is not overwritten by NgClass, so TestDir should still @@ -1297,7 +1297,7 @@ describe('styling', () => { template: '<span dir [classesInSchool]="classes" [styleOfClothing]="style"></span>', }) class App { - @ViewChild(Dir) dir !: Dir; + @ViewChild(Dir) dir!: Dir; classes = 'math'; style = '80s'; @@ -1315,8 +1315,7 @@ describe('styling', () => { it('should be able to bind to `className`', () => { @Component({template: ''}) class App { - @HostBinding('className') - klass = 'one two'; + @HostBinding('className') klass = 'one two'; } TestBed.configureTestingModule({declarations: [App]}); @@ -1471,8 +1470,7 @@ describe('styling', () => { opacity: string|null = '0.5'; @ViewChild(CompWithStyling, {static: true}) compWithStyling: CompWithStyling|null = null; - @ViewChild(DirWithStyling, {static: true}) - dirWithStyling: DirWithStyling|null = null; + @ViewChild(DirWithStyling, {static: true}) dirWithStyling: DirWithStyling|null = null; } TestBed.configureTestingModule({declarations: [Cmp, DirWithStyling, CompWithStyling]}); @@ -1488,13 +1486,13 @@ describe('styling', () => { expect(element.style.fontSize).toEqual('100px'); // once for the template flush and again for the host bindings - expect(ngDevMode !.rendererSetStyle).toEqual(4); + expect(ngDevMode!.rendererSetStyle).toEqual(4); ngDevModeResetPerfCounters(); component.opacity = '0.6'; - component.compWithStyling !.height = '100px'; - component.compWithStyling !.width = '100px'; - component.dirWithStyling !.fontSize = '50px'; + component.compWithStyling!.height = '100px'; + component.compWithStyling!.width = '100px'; + component.dirWithStyling!.fontSize = '50px'; fixture.detectChanges(); expect(element.style.opacity).toEqual('0.6'); @@ -1503,7 +1501,7 @@ describe('styling', () => { expect(element.style.fontSize).toEqual('50px'); // once for the template flush and again for the host bindings - expect(ngDevMode !.rendererSetStyle).toEqual(4); + expect(ngDevMode!.rendererSetStyle).toEqual(4); }); onlyInIvy('ivy resolves styling across directives, components and templates in unison') @@ -1634,8 +1632,12 @@ describe('styling', () => { public c: {[key: string]: any}|null = null; updateClasses(classes: string) { const c = this.c || (this.c = {}); - Object.keys(this.c).forEach(className => { c[className] = false; }); - classes.split(/\s+/).forEach(className => { c[className] = true; }); + Object.keys(this.c).forEach(className => { + c[className] = false; + }); + classes.split(/\s+/).forEach(className => { + c[className] = true; + }); } public s: {[key: string]: any}|null = null; @@ -1694,8 +1696,7 @@ describe('styling', () => { map: any = {width: '111px', opacity: '0.5'}; width: string|null|undefined = '555px'; - @ViewChild('dir', {read: DirThatSetsStyling, static: true}) - dir !: DirThatSetsStyling; + @ViewChild('dir', {read: DirThatSetsStyling, static: true}) dir!: DirThatSetsStyling; } TestBed.configureTestingModule({declarations: [Cmp, DirThatSetsStyling]}); @@ -1763,8 +1764,7 @@ describe('styling', () => { map: any = {width: '555px', height: '555px'}; - @ViewChild('dir', {read: DirThatSetsStyling, static: true}) - dir !: DirThatSetsStyling; + @ViewChild('dir', {read: DirThatSetsStyling, static: true}) dir!: DirThatSetsStyling; } TestBed.configureTestingModule({declarations: [Cmp, DirThatSetsStyling]}); @@ -1991,7 +1991,7 @@ describe('styling', () => { it('should be able to bind a SafeValue to clip-path', () => { @Component({template: '<div [style.clip-path]="path"></div>'}) class Cmp { - path !: SafeStyle; + path!: SafeStyle; } TestBed.configureTestingModule({declarations: [Cmp]}); @@ -2028,12 +2028,22 @@ describe('styling', () => { public background: string = '1.png'; public color: string = 'red'; - private getSafeStyle(value: string) { return this.sanitizer.bypassSecurityTrustStyle(value); } + private getSafeStyle(value: string) { + return this.sanitizer.bypassSecurityTrustStyle(value); + } - getBackgroundSafe() { return this.getSafeStyle(`url("/${this.background}")`); } - getWidthSafe() { return this.getSafeStyle(this.width); } - getHeightSafe() { return this.getSafeStyle(this.height); } - getColorUnsafe() { return this.color; } + getBackgroundSafe() { + return this.getSafeStyle(`url("/${this.background}")`); + } + getWidthSafe() { + return this.getSafeStyle(this.width); + } + getHeightSafe() { + return this.getSafeStyle(this.height); + } + getColorUnsafe() { + return this.color; + } } TestBed.configureTestingModule({ @@ -2089,11 +2099,9 @@ describe('styling', () => { class Cmp { map: any = null; - @ViewChild('div', {read: DirWithStyleMap, static: true}) - dir1 !: DirWithStyleMap; + @ViewChild('div', {read: DirWithStyleMap, static: true}) dir1!: DirWithStyleMap; - @ViewChild('div', {read: DirWithStyleMapPart2, static: true}) - dir2 !: DirWithStyleMapPart2; + @ViewChild('div', {read: DirWithStyleMapPart2, static: true}) dir2!: DirWithStyleMapPart2; } TestBed.configureTestingModule( @@ -2352,8 +2360,8 @@ describe('styling', () => { @Component({template: '<div #target one two></div>'}) class Cmp { - @ViewChild('target', {read: DirOne, static: true}) one !: DirOne; - @ViewChild('target', {read: DirTwo, static: true}) two !: DirTwo; + @ViewChild('target', {read: DirOne, static: true}) one!: DirOne; + @ViewChild('target', {read: DirTwo, static: true}) two!: DirTwo; } TestBed.configureTestingModule({declarations: [Cmp, DirOne, DirTwo]}); @@ -2443,7 +2451,9 @@ describe('styling', () => { class Cmp { s: any = {opacity: '1'}; - clearStyle(): void { this.s = null; } + clearStyle(): void { + this.s = null; + } } TestBed.configureTestingModule({declarations: [Cmp]}); @@ -2471,8 +2481,7 @@ describe('styling', () => { class ChildCmp { readyTpl = false; - @HostBinding('class.ready-host') - readyHost = false; + @HostBinding('class.ready-host') readyHost = false; } @Component({ @@ -2490,10 +2499,9 @@ describe('styling', () => { class ParentCmp { private _prop = ''; - @ViewChild('template', {read: ViewContainerRef}) - vcr: ViewContainerRef = null !; + @ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!; - private child: ComponentRef<ChildCmp> = null !; + private child: ComponentRef<ChildCmp> = null!; @Input() set prop(value: string) { @@ -2505,7 +2513,9 @@ describe('styling', () => { } } - get prop() { return this._prop; } + get prop() { + return this._prop; + } ngAfterViewInit() { const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp); @@ -2559,8 +2569,7 @@ describe('styling', () => { class ChildCmp { readyTpl = false; - @HostBinding('class.ready-host') - readyHost = false; + @HostBinding('class.ready-host') readyHost = false; } @Component({ @@ -2575,10 +2584,9 @@ describe('styling', () => { class ParentCmp { updateChild = false; - @ViewChild('template', {read: ViewContainerRef}) - vcr: ViewContainerRef = null !; + @ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!; - private child: ComponentRef<ChildCmp> = null !; + private child: ComponentRef<ChildCmp> = null!; ngDoCheck() { if (this.updateChild) { @@ -2619,7 +2627,7 @@ describe('styling', () => { expect(readyHost).toBeFalsy(); expect(readyChild).toBeFalsy(); - const parent = fixture.componentInstance.parent !; + const parent = fixture.componentInstance.parent!; parent.updateChild = true; fixture.detectChanges(false); @@ -2675,7 +2683,9 @@ describe('styling', () => { selector: '[dir]', }) class Dir { - constructor(public elementRef: ElementRef, public renderer: Renderer2) { dirInstance = this; } + constructor(public elementRef: ElementRef, public renderer: Renderer2) { + dirInstance = this; + } setStyles() { const nativeEl = this.elementRef.nativeElement; @@ -2730,11 +2740,9 @@ describe('styling', () => { () => { @Directive({selector: '[blockStyles]'}) class StylesDirective { - @HostBinding('style.border') - border = '1px solid red'; + @HostBinding('style.border') border = '1px solid red'; - @HostBinding('style.background') - background = 'white'; + @HostBinding('style.background') background = 'white'; } @Component({ @@ -2863,7 +2871,7 @@ describe('styling', () => { TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp); fixture.detectChanges(); - const div = fixture.nativeElement.querySelector('div') !; + const div = fixture.nativeElement.querySelector('div')!; div.className += ' abc'; expect(splitSortJoin(div.className)).toEqual('abc'); @@ -2877,7 +2885,9 @@ describe('styling', () => { expect(splitSortJoin(div.className)).toEqual('4 5 6 7 abc'); - function splitSortJoin(s: string) { return s.split(/\s+/).sort().join(' ').trim(); } + function splitSortJoin(s: string) { + return s.split(/\s+/).sort().join(' ').trim(); + } }); describe('ExpressionChangedAfterItHasBeenCheckedError', () => { @@ -2885,7 +2895,9 @@ describe('styling', () => { @Component({template: `<div [style.background-image]="iconSafe"></div>`}) class MyComp { icon = 'https://i.imgur.com/4AiXzf8.jpg'; - get iconSafe() { return this.sanitizer.bypassSecurityTrustStyle(`url("${this.icon}")`); } + get iconSafe() { + return this.sanitizer.bypassSecurityTrustStyle(`url("${this.icon}")`); + } constructor(private sanitizer: DomSanitizer) {} } @@ -2951,7 +2963,7 @@ describe('styling', () => { TestBed.configureTestingModule({declarations: [MyComp, DirThatSetsStyling]}) .createComponent(MyComp); fixture.detectChanges(); - const div = fixture.nativeElement.querySelector('div') !; + const div = fixture.nativeElement.querySelector('div')!; expect(div.style.getPropertyValue('width')).toEqual('100px'); expect(div.style.getPropertyValue('height')).toEqual('200px'); expect(div.style.getPropertyValue('font-size')).toEqual('300px'); @@ -3051,20 +3063,16 @@ describe('styling', () => { () => { @Component({selector: 'my-comp-with-styling', template: ''}) class MyCompWithStyling { - @HostBinding('style') - myStyles: any = {width: '300px'}; + @HostBinding('style') myStyles: any = {width: '300px'}; - @HostBinding('style.height') - myHeight: any = '305px'; + @HostBinding('style.height') myHeight: any = '305px'; } @Directive({selector: '[my-dir-with-styling]'}) class MyDirWithStyling { - @HostBinding('style') - myStyles: any = {width: '200px'}; + @HostBinding('style') myStyles: any = {width: '200px'}; - @HostBinding('style.height') - myHeight: any = '205px'; + @HostBinding('style.height') myHeight: any = '205px'; } @Component({ @@ -3081,15 +3089,15 @@ describe('styling', () => { myStyles: {width?: string} = {width: '100px'}; myHeight: string|null|undefined = '100px'; - @ViewChild(MyDirWithStyling) dir !: MyDirWithStyling; - @ViewChild(MyCompWithStyling) comp !: MyCompWithStyling; + @ViewChild(MyDirWithStyling) dir!: MyDirWithStyling; + @ViewChild(MyCompWithStyling) comp!: MyCompWithStyling; } TestBed.configureTestingModule( {declarations: [MyComp, MyCompWithStyling, MyDirWithStyling]}); const fixture = TestBed.createComponent(MyComp); const comp = fixture.componentInstance; - const elm = fixture.nativeElement.querySelector('my-comp-with-styling') !; + const elm = fixture.nativeElement.querySelector('my-comp-with-styling')!; fixture.detectChanges(); expect(elm.style.width).toEqual('100px'); @@ -3131,7 +3139,7 @@ describe('styling', () => { TestBed.configureTestingModule( {declarations: [MyComp, MyCompWithStyling, MyDirWithStyling]}); const fixture = TestBed.createComponent(MyComp); - const elm = fixture.nativeElement.querySelector('my-comp-with-styling') !; + const elm = fixture.nativeElement.querySelector('my-comp-with-styling')!; fixture.detectChanges(); expect(elm.style.color).toEqual('red'); @@ -3139,7 +3147,6 @@ describe('styling', () => { it('should combine host class.foo bindings from multiple directives', () => { - @Directive({ selector: '[dir-that-sets-one-two]', exportAs: 'one', @@ -3197,8 +3204,8 @@ describe('styling', () => { expect(div2.className).toBe(''); const comp = fixture.componentInstance; - comp.dirOneA !.one = comp.dirOneB !.one = true; - comp.dirOneA !.two = comp.dirOneB !.two = true; + comp.dirOneA!.one = comp.dirOneB!.one = true; + comp.dirOneA!.two = comp.dirOneB!.two = true; fixture.detectChanges(); expect(div1.classList.contains('one')).toBeTruthy(); @@ -3211,8 +3218,8 @@ describe('styling', () => { expect(div2.classList.contains('four')).toBeFalsy(); expect(div2.classList.contains('zero')).toBeFalsy(); - comp.dirTwoA !.three = comp.dirTwoB !.three = true; - comp.dirTwoA !.four = comp.dirTwoB !.four = true; + comp.dirTwoA!.three = comp.dirTwoB!.three = true; + comp.dirTwoA!.four = comp.dirTwoB!.four = true; fixture.detectChanges(); expect(div1.classList.contains('one')).toBeTruthy(); @@ -3242,7 +3249,9 @@ describe('styling', () => { it('should combine static host classes with component "class" host attribute', () => { @Component({selector: 'comp-with-classes', template: '', host: {'class': 'host'}}) class CompWithClasses { - constructor(ref: ElementRef) { ref.nativeElement.classList.add('custom'); } + constructor(ref: ElementRef) { + ref.nativeElement.classList.add('custom'); + } } @Component({ @@ -3282,8 +3291,7 @@ describe('styling', () => { @Directive({selector: '[single-host-style-dir]'}) class SingleHostStyleDir { - @HostBinding('style.width') - width = '100px'; + @HostBinding('style.width') width = '100px'; } TestBed.configureTestingModule({declarations: [Cmp, SingleHostStyleDir]}); @@ -3340,7 +3348,9 @@ describe('styling', () => { @Directive({selector: '[test]'}) class MyDir { @Input('class') - set className(value: string) { logs.push(value); } + set className(value: string) { + logs.push(value); + } } @Component({ @@ -3456,8 +3466,7 @@ describe('styling', () => { template: `className = {{className}}`, }) class MyCmp { - @Input() - className: string = 'unbound'; + @Input() className: string = 'unbound'; } @Component({template: `<my-cmp [class]="'bound'"></my-cmp>`}) class MyApp { @@ -3475,8 +3484,7 @@ describe('styling', () => { template: `className = {{className}}`, }) class MyCmp { - @Input() - className: string = 'unbound'; + @Input() className: string = 'unbound'; } @Component({template: `<my-cmp class="bound"></my-cmp>`}) class MyApp { @@ -3491,8 +3499,8 @@ describe('styling', () => { }); function assertStyleCounters(countForSet: number, countForRemove: number) { - expect(ngDevMode !.rendererSetStyle).toEqual(countForSet); - expect(ngDevMode !.rendererRemoveStyle).toEqual(countForRemove); + expect(ngDevMode!.rendererSetStyle).toEqual(countForSet); + expect(ngDevMode!.rendererRemoveStyle).toEqual(countForRemove); } function assertStyle(element: HTMLElement, prop: string, value: any) { diff --git a/packages/core/test/acceptance/template_ref_spec.ts b/packages/core/test/acceptance/template_ref_spec.ts index b55018e8ec91f..026dcc2c5a464 100644 --- a/packages/core/test/acceptance/template_ref_spec.ts +++ b/packages/core/test/acceptance/template_ref_spec.ts @@ -13,11 +13,9 @@ import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; describe('TemplateRef', () => { describe('rootNodes', () => { - @Component({template: `<ng-template #templateRef></ng-template>`}) class App { - @ViewChild('templateRef', {static: true}) - templateRef !: TemplateRef<any>; + @ViewChild('templateRef', {static: true}) templateRef!: TemplateRef<any>; minutes = 0; } @@ -80,7 +78,7 @@ describe('TemplateRef', () => { ` }) class App { - @ViewChild(MenuContent) content !: MenuContent; + @ViewChild(MenuContent) content!: MenuContent; constructor(public viewContainerRef: ViewContainerRef) {} } diff --git a/packages/core/test/acceptance/text_spec.ts b/packages/core/test/acceptance/text_spec.ts index 9f666ef49a9ac..ff16353b99595 100644 --- a/packages/core/test/acceptance/text_spec.ts +++ b/packages/core/test/acceptance/text_spec.ts @@ -7,7 +7,7 @@ */ import {Component} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {of } from 'rxjs'; +import {of} from 'rxjs'; describe('text instructions', () => { it('should handle all flavors of interpolated text', () => { @@ -66,8 +66,8 @@ describe('text instructions', () => { ` }) class App { - who = of ('Sally'); - item = of ({ + who = of('Sally'); + item = of({ what: 'seashells', where: 'seashore', }); diff --git a/packages/core/test/acceptance/view_container_ref_spec.ts b/packages/core/test/acceptance/view_container_ref_spec.ts index 13f1a2c137dfb..cfa09f498bf37 100644 --- a/packages/core/test/acceptance/view_container_ref_spec.ts +++ b/packages/core/test/acceptance/view_container_ref_spec.ts @@ -8,7 +8,7 @@ import {CommonModule, DOCUMENT} from '@angular/common'; import {computeMsgId} from '@angular/compiler'; -import {Compiler, Component, ComponentFactoryResolver, Directive, DoCheck, ElementRef, EmbeddedViewRef, ErrorHandler, Injector, NO_ERRORS_SCHEMA, NgModule, OnInit, Pipe, PipeTransform, QueryList, RendererFactory2, RendererType2, Sanitizer, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, ɵsetDocument} from '@angular/core'; +import {Compiler, Component, ComponentFactoryResolver, Directive, DoCheck, ElementRef, EmbeddedViewRef, ErrorHandler, Injector, NgModule, NO_ERRORS_SCHEMA, OnInit, Pipe, PipeTransform, QueryList, RendererFactory2, RendererType2, Sanitizer, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, ɵsetDocument} from '@angular/core'; import {Input} from '@angular/core/src/metadata'; import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode'; import {TestBed, TestComponentRenderer} from '@angular/core/testing'; @@ -18,7 +18,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; describe('ViewContainerRef', () => { - /** * Gets the inner HTML of the given element with all HTML comments and Angular internal * reflect attributes omitted. This makes HTML comparisons easier and less verbose. @@ -40,7 +39,6 @@ describe('ViewContainerRef', () => { afterEach(() => clearTranslations()); describe('create', () => { - it('should support view queries inside embedded views created in dir constructors', () => { const fixture = TestBed.createComponent(ConstructorApp); fixture.detectChanges(); @@ -87,7 +85,7 @@ describe('ViewContainerRef', () => { ` }) class TestComp { - @ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective; + @ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective; } TestBed.configureTestingModule( @@ -141,7 +139,7 @@ describe('ViewContainerRef', () => { ` }) class TestComp { - @ViewChild('container', {read: ViewContainerRef}) vcRef !: ViewContainerRef; + @ViewChild('container', {read: ViewContainerRef}) vcRef!: ViewContainerRef; constructor(public cfr: ComponentFactoryResolver) {} @@ -196,8 +194,8 @@ describe('ViewContainerRef', () => { ` }) class TestComp { - @ViewChild('svg', {read: ViewContainerRef}) svgVCRef !: ViewContainerRef; - @ViewChild('mathml', {read: ViewContainerRef}) mathMLVCRef !: ViewContainerRef; + @ViewChild('svg', {read: ViewContainerRef}) svgVCRef!: ViewContainerRef; + @ViewChild('mathml', {read: ViewContainerRef}) mathMLVCRef!: ViewContainerRef; constructor(public cfr: ComponentFactoryResolver) {} @@ -273,7 +271,7 @@ describe('ViewContainerRef', () => { ` }) class TestComp { - @ViewChild('container', {read: ViewContainerRef}) vcRef !: ViewContainerRef; + @ViewChild('container', {read: ViewContainerRef}) vcRef!: ViewContainerRef; private helloCompFactory = this.cfr.resolveComponentFactory(HelloComp); @@ -357,7 +355,9 @@ describe('ViewContainerRef', () => { // Insert the view again at the same index viewContainerRef.insert(ref0, 0); - expect(() => { fixture.destroy(); }).not.toThrow(); + expect(() => { + fixture.destroy(); + }).not.toThrow(); expect(fixture.nativeElement.textContent).toEqual('0'); }); @@ -405,7 +405,6 @@ describe('ViewContainerRef', () => { }); it('should insert a view already inserted into another container', () => { - @Component({ selector: 'test-cmpt', template: ` @@ -414,9 +413,9 @@ describe('ViewContainerRef', () => { ` }) class TestComponent { - @ViewChild('t', {static: true}) t !: TemplateRef<{}>; - @ViewChild('c1', {static: true, read: ViewContainerRef}) c1 !: ViewContainerRef; - @ViewChild('c2', {static: true, read: ViewContainerRef}) c2 !: ViewContainerRef; + @ViewChild('t', {static: true}) t!: TemplateRef<{}>; + @ViewChild('c1', {static: true, read: ViewContainerRef}) c1!: ViewContainerRef; + @ViewChild('c2', {static: true, read: ViewContainerRef}) c2!: ViewContainerRef; } TestBed.configureTestingModule({declarations: [TestComponent]}); @@ -660,7 +659,6 @@ describe('ViewContainerRef', () => { }); describe('get and indexOf', () => { - beforeEach(() => { TestBed.configureTestingModule({declarations: [EmbeddedViewInsertionComp, VCRefDirective]}); }); @@ -677,13 +675,13 @@ describe('ViewContainerRef', () => { fixture.detectChanges(); let viewRef = vcRefDir.vcref.get(0); - expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(0); + expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(0); viewRef = vcRefDir.vcref.get(1); - expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(1); + expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(1); viewRef = vcRefDir.vcref.get(2); - expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(2); + expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(2); }); it('should handle out of bounds cases', () => { @@ -700,7 +698,7 @@ describe('ViewContainerRef', () => { const viewRef = vcRefDir.vcref.get(0); vcRefDir.vcref.remove(0); - expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(-1); + expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(-1); }); it('should return -1 as indexOf when no views were inserted', () => { @@ -742,21 +740,21 @@ describe('ViewContainerRef', () => { expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>**A**BC'); let viewRef = vcRefDir.vcref.get(0); - vcRefDir.vcref.move(viewRef !, 2); + vcRefDir.vcref.move(viewRef!, 2); fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>BC**A**'); - vcRefDir.vcref.move(viewRef !, 0); + vcRefDir.vcref.move(viewRef!, 0); fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>**A**BC'); - vcRefDir.vcref.move(viewRef !, 1); + vcRefDir.vcref.move(viewRef!, 1); fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>B**A**C'); // Invalid indices when detaching throws an exception in Ivy: FW-1330. - ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef !, -1)).toThrow(); - ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef !, 42)).toThrow(); + ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef!, -1)).toThrow(); + ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef!, 42)).toThrow(); }); }); @@ -769,7 +767,7 @@ describe('ViewContainerRef', () => { ` }) class TestComponent { - @ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective; + @ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective; } TestBed.configureTestingModule({declarations: [VCRefDirective, TestComponent]}); @@ -789,7 +787,6 @@ describe('ViewContainerRef', () => { }); describe('detach', () => { - beforeEach(() => { TestBed.configureTestingModule({declarations: [EmbeddedViewInsertionComp, VCRefDirective]}); @@ -825,7 +822,7 @@ describe('ViewContainerRef', () => { // Invalid indices when detaching throws an exception in Ivy: FW-1330. ivyEnabled && expect(() => vcRefDir.vcref.detach(-1)).toThrow(); ivyEnabled && expect(() => vcRefDir.vcref.detach(42)).toThrow(); - ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(0); + ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(0); }); it('should detach the last embedded view when no index is specified', () => { @@ -846,7 +843,7 @@ describe('ViewContainerRef', () => { fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>ABCD'); expect(viewE.destroyed).toBeFalsy(); - ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(0); + ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(0); }); }); @@ -895,7 +892,7 @@ describe('ViewContainerRef', () => { // Invalid indices when detaching throws an exception in Ivy: FW-1330. ivyEnabled && expect(() => vcRefDir.vcref.remove(-1)).toThrow(); ivyEnabled && expect(() => vcRefDir.vcref.remove(42)).toThrow(); - ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(2); + ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(2); }); it('should remove the last embedded view when no index is specified', () => { @@ -916,7 +913,7 @@ describe('ViewContainerRef', () => { fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>ABCD'); expect(viewE.destroyed).toBeTruthy(); - ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(1); + ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(1); }); it('should throw when trying to insert a removed or destroyed view', () => { @@ -1063,7 +1060,7 @@ describe('ViewContainerRef', () => { ` }) class TestComponent { - @ViewChild(VCRefDirective, {static: true}) vcRef !: VCRefDirective; + @ViewChild(VCRefDirective, {static: true}) vcRef!: VCRefDirective; } TestBed.configureTestingModule({declarations: [TestComponent, VCRefDirective]}); @@ -1087,8 +1084,8 @@ describe('ViewContainerRef', () => { expect(getElementHtml(fixture.nativeElement)).toEqual('YABC<footer></footer>'); // Invalid indices when detaching throws an exception in Ivy: FW-1330. - ivyEnabled && expect(() => vcRef !.createView('Z', -1)).toThrow(); - ivyEnabled && expect(() => vcRef !.createView('Z', 5)).toThrow(); + ivyEnabled && expect(() => vcRef!.createView('Z', -1)).toThrow(); + ivyEnabled && expect(() => vcRef!.createView('Z', 5)).toThrow(); }); it('should apply directives and pipes of the host view to the TemplateRef', () => { @@ -1099,7 +1096,9 @@ describe('ViewContainerRef', () => { @Pipe({name: 'starPipe'}) class StarPipe implements PipeTransform { - transform(value: any) { return `**${value}**`; } + transform(value: any) { + return `**${value}**`; + } } @Component({ @@ -1121,15 +1120,14 @@ describe('ViewContainerRef', () => { fixture.debugElement.query(By.directive(VCRefDirective)).injector.get(VCRefDirective); fixture.detectChanges(); - vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !); - vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !); + vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!); + vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!); fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)) .toEqual( '<child vcref="">**A**</child><child>**C**</child><child>**C**</child><child>**B**</child>'); }); - }); describe('createComponent', () => { @@ -1140,9 +1138,13 @@ describe('ViewContainerRef', () => { it('should work without Injector and NgModuleRef', () => { @Component({selector: 'embedded-cmp', template: `foo`}) class EmbeddedComponent implements DoCheck, OnInit { - ngOnInit() { templateExecutionCounter++; } + ngOnInit() { + templateExecutionCounter++; + } - ngDoCheck() { templateExecutionCounter++; } + ngDoCheck() { + templateExecutionCounter++; + } } @NgModule({entryComponents: [EmbeddedComponent], declarations: [EmbeddedComponent]}) @@ -1185,13 +1187,16 @@ describe('ViewContainerRef', () => { selector: 'embedded-cmp', template: `foo`, }) - class EmbeddedComponent implements DoCheck, - OnInit { + class EmbeddedComponent implements DoCheck, OnInit { constructor(public s: String) {} - ngOnInit() { templateExecutionCounter++; } + ngOnInit() { + templateExecutionCounter++; + } - ngDoCheck() { templateExecutionCounter++; } + ngDoCheck() { + templateExecutionCounter++; + } } @NgModule({entryComponents: [EmbeddedComponent], declarations: [EmbeddedComponent]}) @@ -1423,7 +1428,7 @@ describe('ViewContainerRef', () => { const makeComponentFactory = (componentType: any) => ({ create: () => TestBed.createComponent(componentType).componentRef, }); - this.viewContainerRef !.createComponent(makeComponentFactory(Child) as any); + this.viewContainerRef!.createComponent(makeComponentFactory(Child) as any); } } @@ -1493,8 +1498,8 @@ describe('ViewContainerRef', () => { `, }) class LoopComp { - @Input() tpl !: TemplateRef<any>; - @Input() rows !: any[]; + @Input() tpl!: TemplateRef<any>; + @Input() rows!: any[]; name = 'Loop'; } @@ -1715,7 +1720,6 @@ describe('ViewContainerRef', () => { }); describe('lifecycle hooks', () => { - // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref const log: string[] = []; @@ -1723,19 +1727,37 @@ describe('ViewContainerRef', () => { class ComponentWithHooks { @Input() name: string|undefined; - private log(msg: string) { log.push(msg); } + private log(msg: string) { + log.push(msg); + } - ngOnChanges() { this.log('onChanges-' + this.name); } - ngOnInit() { this.log('onInit-' + this.name); } - ngDoCheck() { this.log('doCheck-' + this.name); } + ngOnChanges() { + this.log('onChanges-' + this.name); + } + ngOnInit() { + this.log('onInit-' + this.name); + } + ngDoCheck() { + this.log('doCheck-' + this.name); + } - ngAfterContentInit() { this.log('afterContentInit-' + this.name); } - ngAfterContentChecked() { this.log('afterContentChecked-' + this.name); } + ngAfterContentInit() { + this.log('afterContentInit-' + this.name); + } + ngAfterContentChecked() { + this.log('afterContentChecked-' + this.name); + } - ngAfterViewInit() { this.log('afterViewInit-' + this.name); } - ngAfterViewChecked() { this.log('afterViewChecked-' + this.name); } + ngAfterViewInit() { + this.log('afterViewInit-' + this.name); + } + ngAfterViewChecked() { + this.log('afterViewChecked-' + this.name); + } - ngOnDestroy() { this.log('onDestroy-' + this.name); } + ngOnDestroy() { + this.log('onDestroy-' + this.name); + } } @NgModule({ @@ -1784,7 +1806,7 @@ describe('ViewContainerRef', () => { ]); log.length = 0; - vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !); + vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!); expect(getElementHtml(fixture.nativeElement)) .toEqual('<hooks vcref="">A</hooks><hooks></hooks><hooks>B</hooks>'); expect(log).toEqual([]); @@ -1815,7 +1837,7 @@ describe('ViewContainerRef', () => { ]); log.length = 0; - vcRefDir.vcref.insert(viewRef !); + vcRefDir.vcref.insert(viewRef!); fixture.detectChanges(); expect(log).toEqual([ 'doCheck-A', 'doCheck-B', 'doCheck-C', 'afterContentChecked-C', 'afterViewChecked-C', @@ -1898,7 +1920,7 @@ describe('ViewContainerRef', () => { ]); log.length = 0; - vcRefDir.vcref.insert(viewRef !); + vcRefDir.vcref.insert(viewRef!); fixture.detectChanges(); expect(log).toEqual([ 'doCheck-A', 'doCheck-B', 'doCheck-D', 'afterContentChecked-D', 'afterViewChecked-D', @@ -1916,7 +1938,6 @@ describe('ViewContainerRef', () => { }); describe('host bindings', () => { - it('should support host bindings on dynamically created components', () => { @Component( {selector: 'host-bindings', host: {'id': 'attribute', '[title]': 'title'}, template: ``}) @@ -1926,7 +1947,7 @@ describe('ViewContainerRef', () => { @Component({template: `<ng-template vcref></ng-template>`}) class TestComponent { - @ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective; + @ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective; } @NgModule({declarations: [HostBindingCmpt], entryComponents: [HostBindingCmpt]}) @@ -1956,11 +1977,9 @@ describe('ViewContainerRef', () => { expect(fixture.nativeElement.children[0].getAttribute('id')).toBe('attribute'); expect(fixture.nativeElement.children[0].getAttribute('title')).toBe('changed'); }); - }); describe('projection', () => { - it('should project the ViewContainerRef content along its host, in an element', () => { @Component({selector: 'child', template: '<div><ng-content></ng-content></div>'}) class Child { @@ -1990,7 +2009,7 @@ describe('ViewContainerRef', () => { expect(getElementHtml(fixture.nativeElement)) .toEqual('<child><div><header vcref="">blah</header></div></child>'); - vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !); + vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!); fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)) .toEqual('<child><div><header vcref="">blah</header><span>bar</span></div></child>'); @@ -2031,7 +2050,7 @@ describe('ViewContainerRef', () => { .toEqual( '<child-with-view>Before (inside)- Before projected <header vcref="">blah</header> After projected -After (inside)</child-with-view>'); - vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !); + vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!); fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)) @@ -2067,7 +2086,6 @@ describe('ViewContainerRef', () => { }); describe('with select', () => { - @Component({ selector: 'child-with-selector', template: ` @@ -2105,7 +2123,7 @@ describe('ViewContainerRef', () => { .toEqual( '<child-with-selector><p class="a"><header vcref="">blah</header></p><p class="b"></p></child-with-selector>'); - vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !); + vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!); fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)) @@ -2132,13 +2150,13 @@ describe('ViewContainerRef', () => { ` }) class MyComp { - @ViewChild('source', {static: true}) - source !: TemplateRef<{}>; + @ViewChild('source', {static: true}) source!: TemplateRef<{}>; - @ViewChild('target', {read: ViewContainerRef, static: true}) - target !: ViewContainerRef; + @ViewChild('target', {read: ViewContainerRef, static: true}) target!: ViewContainerRef; - ngOnInit() { this.target.createEmbeddedView(this.source); } + ngOnInit() { + this.target.createEmbeddedView(this.source); + } } TestBed.configureTestingModule({declarations: [MyComp, ContentComp]}); @@ -2175,7 +2193,7 @@ describe('ViewContainerRef', () => { .toEqual( '<child-with-selector><p class="a"></p><p class="b"><footer vcref="">blah</footer></p></child-with-selector>'); - vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !); + vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!); fixture.detectChanges(); expect(getElementHtml(fixture.nativeElement)) @@ -2183,7 +2201,6 @@ describe('ViewContainerRef', () => { '<child-with-selector><p class="a"></p><p class="b"><footer vcref="">blah</footer><span>bar</span></p></child-with-selector>'); }); }); - }); describe('root view container ref', () => { @@ -2204,7 +2221,7 @@ describe('ViewContainerRef', () => { containerEl = document.createElement('div'); document.body.appendChild(containerEl); - containerEl !.appendChild(rootEl); + containerEl!.appendChild(rootEl); } }; } @@ -2223,7 +2240,9 @@ describe('ViewContainerRef', () => { class DynamicCompWithBindings implements DoCheck { checkCount = 0; - ngDoCheck() { this.checkCount++; } + ngDoCheck() { + this.checkCount++; + } } @Component({template: ``}) @@ -2248,21 +2267,21 @@ describe('ViewContainerRef', () => { // Ivy inserts a comment for the root view container ref instance. This is not // the case for view engine and we need to adjust the assertions. - expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 2 : 1); - ivyEnabled && expect(containerEl !.childNodes[1].nodeType).toBe(Node.COMMENT_NODE); + expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 2 : 1); + ivyEnabled && expect(containerEl!.childNodes[1].nodeType).toBe(Node.COMMENT_NODE); - expect((containerEl !.childNodes[0] as Element).tagName).toBe('DIV'); + expect((containerEl!.childNodes[0] as Element).tagName).toBe('DIV'); vcRef.createComponent(cfResolver.resolveComponentFactory(DynamicCompWithBindings)); fixture.detectChanges(); - expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2); - expect(containerEl !.childNodes[1].textContent).toBe('check count: 1'); + expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2); + expect(containerEl!.childNodes[1].textContent).toBe('check count: 1'); fixture.detectChanges(); - expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2); - expect(containerEl !.childNodes[1].textContent).toBe('check count: 2'); + expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2); + expect(containerEl!.childNodes[1].textContent).toBe('check count: 2'); }); it('should create deep DOM tree immediately for dynamically created components', () => { @@ -2299,21 +2318,21 @@ describe('ViewContainerRef', () => { // Ivy inserts a comment for the root view container ref instance. This is not // the case for view engine and we need to adjust the assertions. - expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 2 : 1); - ivyEnabled && expect(containerEl !.childNodes[1].nodeType).toBe(Node.COMMENT_NODE); + expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 2 : 1); + ivyEnabled && expect(containerEl!.childNodes[1].nodeType).toBe(Node.COMMENT_NODE); - expect((containerEl !.childNodes[0] as Element).tagName).toBe('DIV'); + expect((containerEl!.childNodes[0] as Element).tagName).toBe('DIV'); vcRef.createComponent(cfResolver.resolveComponentFactory(DynamicCompWithChildren)); - expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2); - expect(getElementHtml(containerEl !.childNodes[1] as Element)) + expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2); + expect(getElementHtml(containerEl!.childNodes[1] as Element)) .toBe('<child><div></div></child>'); fixture.detectChanges(); - expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2); - expect(getElementHtml(containerEl !.childNodes[1] as Element)) + expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2); + expect(getElementHtml(containerEl!.childNodes[1] as Element)) .toBe(`<child><div>text</div></child>`); }); }); @@ -2374,7 +2393,7 @@ class EmbeddedComponentWithNgZoneModule { ` }) class ViewContainerRefComp { - @ViewChildren(TemplateRef) templates !: QueryList<TemplateRef<any>>; + @ViewChildren(TemplateRef) templates!: QueryList<TemplateRef<any>>; constructor(public vcr: ViewContainerRef) {} } @@ -2386,21 +2405,25 @@ class ViewContainerRefComp { ` }) class ViewContainerRefApp { - @ViewChild(ViewContainerRefComp) vcrComp !: ViewContainerRefComp; + @ViewChild(ViewContainerRefComp) vcrComp!: ViewContainerRefComp; } @Directive({selector: '[structDir]'}) export class StructDir { constructor(private vcref: ViewContainerRef, private tplRef: TemplateRef<any>) {} - create() { this.vcref.createEmbeddedView(this.tplRef); } + create() { + this.vcref.createEmbeddedView(this.tplRef); + } - destroy() { this.vcref.clear(); } + destroy() { + this.vcref.clear(); + } } @Component({selector: 'destroy-cases', template: ` `}) class DestroyCasesComp { - @ViewChildren(StructDir) structDirs !: QueryList<StructDir>; + @ViewChildren(StructDir) structDirs!: QueryList<StructDir>; } @Directive({selector: '[constructorDir]'}) @@ -2419,7 +2442,7 @@ class ConstructorDir { ` }) class ConstructorApp { - @ViewChild('foo', {static: true}) foo !: ElementRef; + @ViewChild('foo', {static: true}) foo!: ElementRef; } @Component({ @@ -2431,5 +2454,5 @@ class ConstructorApp { ` }) class ConstructorAppWithQueries { - @ViewChild('foo', {static: true}) foo !: TemplateRef<any>; + @ViewChild('foo', {static: true}) foo!: TemplateRef<any>; } diff --git a/packages/core/test/acceptance/view_insertion_spec.ts b/packages/core/test/acceptance/view_insertion_spec.ts index 41099fd2478ab..265816486503b 100644 --- a/packages/core/test/acceptance/view_insertion_spec.ts +++ b/packages/core/test/acceptance/view_insertion_spec.ts @@ -33,15 +33,14 @@ describe('view insertion', () => { }) class App { @ViewChild('container', {read: ViewContainerRef, static: true}) - container: ViewContainerRef = null !; + container: ViewContainerRef = null!; - @ViewChild('simple', {read: TemplateRef, static: true}) - simple: TemplateRef<any> = null !; + @ViewChild('simple', {read: TemplateRef, static: true}) simple: TemplateRef<any> = null!; - view0: EmbeddedViewRef<any> = null !; - view1: EmbeddedViewRef<any> = null !; - view2: EmbeddedViewRef<any> = null !; - view3: EmbeddedViewRef<any> = null !; + view0: EmbeddedViewRef<any> = null!; + view1: EmbeddedViewRef<any> = null!; + view2: EmbeddedViewRef<any> = null!; + view3: EmbeddedViewRef<any> = null!; constructor(public changeDetector: ChangeDetectorRef) {} @@ -90,16 +89,14 @@ describe('view insertion', () => { ` }) class App { - @ViewChild('container', {read: ViewContainerRef}) - container: ViewContainerRef = null !; + @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!; - @ViewChild('empty', {read: TemplateRef}) - empty: TemplateRef<any> = null !; + @ViewChild('empty', {read: TemplateRef}) empty: TemplateRef<any> = null!; - view0: EmbeddedViewRef<any> = null !; - view1: EmbeddedViewRef<any> = null !; - view2: EmbeddedViewRef<any> = null !; - view3: EmbeddedViewRef<any> = null !; + view0: EmbeddedViewRef<any> = null!; + view1: EmbeddedViewRef<any> = null!; + view2: EmbeddedViewRef<any> = null!; + view3: EmbeddedViewRef<any> = null!; ngAfterViewInit() { // insert at the front @@ -140,16 +137,14 @@ describe('view insertion', () => { ` }) class Comp { - @ViewChild('container', {read: ViewContainerRef}) - container: ViewContainerRef = null !; + @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!; - @ViewChild('projection', {read: TemplateRef}) - projection: TemplateRef<any> = null !; + @ViewChild('projection', {read: TemplateRef}) projection: TemplateRef<any> = null!; - view0: EmbeddedViewRef<any> = null !; - view1: EmbeddedViewRef<any> = null !; - view2: EmbeddedViewRef<any> = null !; - view3: EmbeddedViewRef<any> = null !; + view0: EmbeddedViewRef<any> = null!; + view1: EmbeddedViewRef<any> = null!; + view2: EmbeddedViewRef<any> = null!; + view3: EmbeddedViewRef<any> = null!; ngAfterViewInit() { // insert at the front @@ -201,16 +196,14 @@ describe('view insertion', () => { ` }) class App { - @ViewChild('container', {read: ViewContainerRef}) - container: ViewContainerRef = null !; + @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!; - @ViewChild('subContainer', {read: TemplateRef}) - subContainer: TemplateRef<any> = null !; + @ViewChild('subContainer', {read: TemplateRef}) subContainer: TemplateRef<any> = null!; - view0: EmbeddedViewRef<any> = null !; - view1: EmbeddedViewRef<any> = null !; - view2: EmbeddedViewRef<any> = null !; - view3: EmbeddedViewRef<any> = null !; + view0: EmbeddedViewRef<any> = null!; + view1: EmbeddedViewRef<any> = null!; + view2: EmbeddedViewRef<any> = null!; + view3: EmbeddedViewRef<any> = null!; constructor(public changeDetectorRef: ChangeDetectorRef) {} @@ -265,9 +258,9 @@ describe('view insertion', () => { describe('before embedded view', () => { @Component({selector: 'test-cmpt', template: ''}) class TestCmpt { - @ViewChild('before', {static: true}) beforeTpl !: TemplateRef<{}>; - @ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>; - @ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir; + @ViewChild('before', {static: true}) beforeTpl!: TemplateRef<{}>; + @ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>; + @ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir; minutes = 10; @@ -303,8 +296,9 @@ describe('view insertion', () => { } - it('should insert before a view with the text node as the first root node', - () => { expect(createAndInsertViews('|before').textContent).toBe('insert|before'); }); + it('should insert before a view with the text node as the first root node', () => { + expect(createAndInsertViews('|before').textContent).toBe('insert|before'); + }); it('should insert before a view with the element as the first root node', () => { expect(createAndInsertViews('<span>|before</span>').textContent).toBe('insert|before'); @@ -336,13 +330,11 @@ describe('view insertion', () => { it('should insert before a view with a container as the first root node', () => { expect(createAndInsertViews(`<ng-template [ngIf]="true">|before</ng-template>`).textContent) .toBe('insert|before'); - }); it('should insert before a view with an empty container as the first root node', () => { expect(createAndInsertViews(`<ng-template [ngIf]="true"></ng-template>|before`).textContent) .toBe('insert|before'); - }); onlyInIvy('VE incorrectly inserts views before ng-container content') @@ -353,7 +345,6 @@ describe('view insertion', () => { <ng-template #after>|after</ng-template> `).textContent) .toBe('insert|before|after'); - }); @@ -363,7 +354,6 @@ describe('view insertion', () => { <ng-template #after>|after</ng-template> `).textContent) .toBe('insert|before|after'); - }); it('should insert before a view with an empty projection as the first root node', () => { @@ -414,11 +404,9 @@ describe('view insertion', () => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement.textContent).toBe('start|testtest|end'); }); - }); describe('before embedded view with projection', () => { - @Component({ selector: 'with-content', template: ` @@ -428,9 +416,9 @@ describe('view insertion', () => { ` }) class WithContentCmpt { - @ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>; - @ViewChild('before', {static: true}) beforeTpl !: TemplateRef<{}>; - @ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir; + @ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>; + @ViewChild('before', {static: true}) beforeTpl!: TemplateRef<{}>; + @ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir; insert() { const beforeView = this.beforeTpl.createEmbeddedView({}); @@ -442,7 +430,7 @@ describe('view insertion', () => { @Component({selector: 'test-cmpt', template: ''}) class TestCmpt { - @ViewChild('wc', {static: true}) withContentCmpt !: WithContentCmpt; + @ViewChild('wc', {static: true}) withContentCmpt!: WithContentCmpt; } beforeEach(() => { @@ -476,7 +464,6 @@ describe('view insertion', () => { expect(fixture.nativeElement.textContent).toBe('insert|before'); }); - }); describe('before component view', () => { @@ -503,8 +490,8 @@ describe('view insertion', () => { ` }) class TestCmpt { - @ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>; - @ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir; + @ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>; + @ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir; constructor(private _cfr: ComponentFactoryResolver, private _injector: Injector) {} @@ -537,16 +524,13 @@ describe('view insertion', () => { expect(fixture.nativeElement.textContent).toBe('insert|before'); }); - }); }); describe('non-regression', () => { - // https://github.com/angular/angular/issues/31971 it('should insert component views into ViewContainerRef injected by querying <ng-container>', () => { - @Component({selector: 'dynamic-cmpt', template: 'dynamic'}) class DynamicComponent { } @@ -562,8 +546,7 @@ describe('view insertion', () => { ` }) class AppComponent { - @ViewChild('container', {read: ViewContainerRef, static: true}) - vcr !: ViewContainerRef; + @ViewChild('container', {read: ViewContainerRef, static: true}) vcr!: ViewContainerRef; constructor(private _cfr: ComponentFactoryResolver) {} @@ -595,7 +578,6 @@ describe('view insertion', () => { // https://github.com/angular/angular/issues/33679 it('should insert embedded views into ViewContainerRef injected by querying <ng-container>', () => { - @Component({ selector: 'app-root', template: ` @@ -608,12 +590,13 @@ describe('view insertion', () => { ` }) class AppComponent { - @ViewChild('container', {read: ViewContainerRef, static: true}) - vcr !: ViewContainerRef; + @ViewChild('container', {read: ViewContainerRef, static: true}) vcr!: ViewContainerRef; @ViewChild('template', {read: TemplateRef, static: true}) template !: TemplateRef<any>; - click() { this.vcr.createEmbeddedView(this.template, undefined, 0); } + click() { + this.vcr.createEmbeddedView(this.template, undefined, 0); + } } TestBed.configureTestingModule({ @@ -659,6 +642,5 @@ describe('view insertion', () => { expect(fixture.nativeElement.textContent.trim()).toContain('2 1'); }); - }); }); diff --git a/packages/core/test/acceptance/view_ref_spec.ts b/packages/core/test/acceptance/view_ref_spec.ts index fcb1cf28f635b..04e2104ba902f 100644 --- a/packages/core/test/acceptance/view_ref_spec.ts +++ b/packages/core/test/acceptance/view_ref_spec.ts @@ -13,7 +13,6 @@ import {TestBed} from '@angular/core/testing'; describe('ViewRef', () => { it('should remove nodes from DOM when the view is detached from app ref', () => { - @Component({selector: 'dynamic-cpt', template: '<div></div>'}) class DynamicComponent { constructor(public elRef: ElementRef) {} @@ -21,7 +20,7 @@ describe('ViewRef', () => { @Component({template: `<span></span>`}) class App { - componentRef !: ComponentRef<DynamicComponent>; + componentRef!: ComponentRef<DynamicComponent>; constructor( public appRef: ApplicationRef, private cfr: ComponentFactoryResolver, private injector: Injector) {} @@ -33,7 +32,9 @@ describe('ViewRef', () => { document.body.appendChild(this.componentRef.instance.elRef.nativeElement); } - destroy() { (this.componentRef.hostView as InternalViewRef).detachFromAppRef(); } + destroy() { + (this.componentRef.hostView as InternalViewRef).detachFromAppRef(); + } } @NgModule({declarations: [App, DynamicComponent], entryComponents: [DynamicComponent]}) diff --git a/packages/core/test/animation/animation_integration_spec.ts b/packages/core/test/animation/animation_integration_spec.ts index d2034fb6a28d2..15ebcef106e24 100644 --- a/packages/core/test/animation/animation_integration_spec.ts +++ b/packages/core/test/animation/animation_integration_spec.ts @@ -5,12 +5,12 @@ * 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 {AUTO_STYLE, AnimationEvent, AnimationOptions, animate, animateChild, group, keyframes, query, state, style, transition, trigger, ɵPRE_STYLE as PRE_STYLE} from '@angular/animations'; +import {animate, animateChild, AnimationEvent, AnimationOptions, AUTO_STYLE, group, keyframes, query, state, style, transition, trigger, ɵPRE_STYLE as PRE_STYLE} from '@angular/animations'; import {AnimationDriver, ɵAnimationEngine, ɵNoopAnimationDriver as NoopAnimationDriver} from '@angular/animations/browser'; import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing'; import {ɵgetDOM as getDOM} from '@angular/common'; import {ChangeDetectorRef, Component, HostBinding, HostListener, Inject, RendererFactory2, ViewChild} from '@angular/core'; -import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing'; +import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing'; import {ɵDomRendererFactory2} from '@angular/platform-browser'; import {ANIMATION_MODULE_TYPE, BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations'; import {hasStyle} from '@angular/platform-browser/testing/src/browser_util'; @@ -20,792 +20,853 @@ const DEFAULT_NAMESPACE_ID = 'id'; const DEFAULT_COMPONENT_ID = '1'; (function() { - // these tests are only mean't to be run within the DOM (for now) - if (isNode) return; +// these tests are only mean't to be run within the DOM (for now) +if (isNode) return; - describe('animation tests', function() { - function getLog(): MockAnimationPlayer[] { - return MockAnimationDriver.log as MockAnimationPlayer[]; - } +describe('animation tests', function() { + function getLog(): MockAnimationPlayer[] { + return MockAnimationDriver.log as MockAnimationPlayer[]; + } - function resetLog() { MockAnimationDriver.log = []; } + function resetLog() { + MockAnimationDriver.log = []; + } - beforeEach(() => { - resetLog(); - TestBed.configureTestingModule({ - providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}], - imports: [BrowserAnimationsModule] - }); + beforeEach(() => { + resetLog(); + TestBed.configureTestingModule({ + providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}], + imports: [BrowserAnimationsModule] }); + }); - describe('animation modules', function() { - it('should hint at BrowserAnimationsModule being used', () => { - TestBed.resetTestingModule(); - TestBed.configureTestingModule( - {declarations: [SharedAnimationCmp], imports: [BrowserAnimationsModule]}); + describe('animation modules', function() { + it('should hint at BrowserAnimationsModule being used', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule( + {declarations: [SharedAnimationCmp], imports: [BrowserAnimationsModule]}); - const fixture = TestBed.createComponent(SharedAnimationCmp); - const cmp = fixture.componentInstance; - expect(cmp.animationType).toEqual('BrowserAnimations'); - }); + const fixture = TestBed.createComponent(SharedAnimationCmp); + const cmp = fixture.componentInstance; + expect(cmp.animationType).toEqual('BrowserAnimations'); + }); - it('should hint at NoopAnimationsModule being used', () => { - TestBed.resetTestingModule(); - TestBed.configureTestingModule( - {declarations: [SharedAnimationCmp], imports: [NoopAnimationsModule]}); + it('should hint at NoopAnimationsModule being used', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule( + {declarations: [SharedAnimationCmp], imports: [NoopAnimationsModule]}); - const fixture = TestBed.createComponent(SharedAnimationCmp); - const cmp = fixture.componentInstance; - expect(cmp.animationType).toEqual('NoopAnimations'); - }); + const fixture = TestBed.createComponent(SharedAnimationCmp); + const cmp = fixture.componentInstance; + expect(cmp.animationType).toEqual('NoopAnimations'); }); + }); - @Component({template: '<p>template text</p>'}) - class SharedAnimationCmp { - constructor(@Inject(ANIMATION_MODULE_TYPE) public animationType: 'NoopAnimations'| - 'BrowserAnimations') {} - } + @Component({template: '<p>template text</p>'}) + class SharedAnimationCmp { + constructor(@Inject(ANIMATION_MODULE_TYPE) public animationType: 'NoopAnimations'| + 'BrowserAnimations') {} + } - describe('fakeAsync testing', () => { - it('should only require one flushMicrotasks call to kick off animation callbacks', - fakeAsync(() => { + describe('fakeAsync testing', () => { + it('should only require one flushMicrotasks call to kick off animation callbacks', + fakeAsync(() => { + @Component({ + selector: 'cmp', + template: ` + <div [@myAnimation]="exp" (@myAnimation.start)="cb('start')" (@myAnimation.done)="cb('done')"></div> + `, + animations: [trigger( + 'myAnimation', + [transition('* => on, * => off', [animate(1000, style({opacity: 1}))])])] + }) + class Cmp { + exp: any = false; + status: string = ''; + cb(status: string) { + this.status = status; + } + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'on'; + fixture.detectChanges(); + expect(cmp.status).toEqual(''); + + flushMicrotasks(); + expect(cmp.status).toEqual('start'); + + let player = MockAnimationDriver.log.pop()!; + player.finish(); + expect(cmp.status).toEqual('done'); + + cmp.status = ''; + cmp.exp = 'off'; + fixture.detectChanges(); + expect(cmp.status).toEqual(''); + + player = MockAnimationDriver.log.pop()!; + player.finish(); + expect(cmp.status).toEqual(''); + flushMicrotasks(); + expect(cmp.status).toEqual('done'); + })); + + it('should always run .start callbacks before .done callbacks even for noop animations', + fakeAsync(() => { + @Component({ + selector: 'cmp', + template: ` + <div [@myAnimation]="exp" (@myAnimation.start)="cb('start')" (@myAnimation.done)="cb('done')"></div> + `, + animations: [ + trigger( + 'myAnimation', + [ + transition('* => go', []), + ]), + ] + }) + class Cmp { + exp: any = false; + log: string[] = []; + cb(status: string) { + this.log.push(status); + } + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'go'; + fixture.detectChanges(); + expect(cmp.log).toEqual([]); + + flushMicrotasks(); + expect(cmp.log).toEqual(['start', 'done']); + })); + + it('should emit the correct totalTime value for a noop-animation', fakeAsync(() => { + @Component({ + selector: 'cmp', + template: ` + <div [@myAnimation]="exp" (@myAnimation.start)="cb($event)" (@myAnimation.done)="cb($event)"></div> + `, + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + animate('1s', style({opacity: 0})), + ]), + ]), + ] + }) + class Cmp { + exp: any = false; + log: AnimationEvent[] = []; + cb(event: AnimationEvent) { + this.log.push(event); + } + } + + TestBed.configureTestingModule({ + declarations: [Cmp], + providers: [ + {provide: AnimationDriver, useClass: NoopAnimationDriver}, + ], + }); + + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'go'; + fixture.detectChanges(); + expect(cmp.log).toEqual([]); + + flushMicrotasks(); + expect(cmp.log.length).toEqual(2); + const [start, end] = cmp.log; + expect(start.totalTime).toEqual(1000); + expect(end.totalTime).toEqual(1000); + })); + }); + + describe('component fixture integration', () => { + describe('whenRenderingDone', () => { + it('should wait until the animations are finished until continuing', fakeAsync(() => { @Component({ selector: 'cmp', template: ` - <div [@myAnimation]="exp" (@myAnimation.start)="cb('start')" (@myAnimation.done)="cb('done')"></div> - `, + <div [@myAnimation]="exp"></div> + `, animations: [trigger( - 'myAnimation', - [transition('* => on, * => off', [animate(1000, style({opacity: 1}))])])] + 'myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])])] }) class Cmp { exp: any = false; - status: string = ''; - cb(status: string) { this.status = status; } } TestBed.configureTestingModule({declarations: [Cmp]}); + const engine = TestBed.inject(ɵAnimationEngine); const fixture = TestBed.createComponent(Cmp); const cmp = fixture.componentInstance; - cmp.exp = 'on'; - fixture.detectChanges(); - expect(cmp.status).toEqual(''); - - flushMicrotasks(); - expect(cmp.status).toEqual('start'); - let player = MockAnimationDriver.log.pop() !; - player.finish(); - expect(cmp.status).toEqual('done'); + let isDone = false; + fixture.whenRenderingDone().then(() => isDone = true); + expect(isDone).toBe(false); - cmp.status = ''; - cmp.exp = 'off'; + cmp.exp = 'on'; fixture.detectChanges(); - expect(cmp.status).toEqual(''); + engine.flush(); + expect(isDone).toBe(false); + + const players = engine.players; + expect(players.length).toEqual(1); + players[0].finish(); + expect(isDone).toBe(false); - player = MockAnimationDriver.log.pop() !; - player.finish(); - expect(cmp.status).toEqual(''); flushMicrotasks(); - expect(cmp.status).toEqual('done'); + expect(isDone).toBe(true); })); - it('should always run .start callbacks before .done callbacks even for noop animations', - fakeAsync(() => { + it('should wait for a noop animation to finish before continuing', fakeAsync(() => { @Component({ selector: 'cmp', template: ` - <div [@myAnimation]="exp" (@myAnimation.start)="cb('start')" (@myAnimation.done)="cb('done')"></div> - `, - animations: [ - trigger( - 'myAnimation', - [ - transition('* => go', []), - ]), - ] + <div [@myAnimation]="exp"></div> + `, + animations: [trigger( + 'myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])])] }) class Cmp { exp: any = false; - log: string[] = []; - cb(status: string) { this.log.push(status); } } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({ + providers: [{provide: AnimationDriver, useClass: NoopAnimationDriver}], + declarations: [Cmp] + }); + + const engine = TestBed.inject(ɵAnimationEngine); const fixture = TestBed.createComponent(Cmp); const cmp = fixture.componentInstance; - cmp.exp = 'go'; + + let isDone = false; + fixture.whenRenderingDone().then(() => isDone = true); + expect(isDone).toBe(false); + + cmp.exp = 'off'; fixture.detectChanges(); - expect(cmp.log).toEqual([]); + engine.flush(); + expect(isDone).toBe(false); flushMicrotasks(); - expect(cmp.log).toEqual(['start', 'done']); + expect(isDone).toBe(true); })); - it('should emit the correct totalTime value for a noop-animation', fakeAsync(() => { + it('should wait for active animations to finish even if they have already started', + fakeAsync(() => { @Component({ selector: 'cmp', template: ` - <div [@myAnimation]="exp" (@myAnimation.start)="cb($event)" (@myAnimation.done)="cb($event)"></div> - `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - animate('1s', style({opacity: 0})), - ]), - ]), - ] + <div [@myAnimation]="exp"></div> + `, + animations: [trigger( + 'myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])])] }) class Cmp { exp: any = false; - log: AnimationEvent[] = []; - cb(event: AnimationEvent) { this.log.push(event); } } - TestBed.configureTestingModule({ - declarations: [Cmp], - providers: [ - {provide: AnimationDriver, useClass: NoopAnimationDriver}, - ], - }); - + TestBed.configureTestingModule({declarations: [Cmp]}); + const engine = TestBed.inject(ɵAnimationEngine); const fixture = TestBed.createComponent(Cmp); const cmp = fixture.componentInstance; - cmp.exp = 'go'; + cmp.exp = 'on'; fixture.detectChanges(); - expect(cmp.log).toEqual([]); - - flushMicrotasks(); - expect(cmp.log.length).toEqual(2); - const [start, end] = cmp.log; - expect(start.totalTime).toEqual(1000); - expect(end.totalTime).toEqual(1000); - })); - }); - - describe('component fixture integration', () => { - describe('whenRenderingDone', () => { - it('should wait until the animations are finished until continuing', fakeAsync(() => { - @Component({ - selector: 'cmp', - template: ` - <div [@myAnimation]="exp"></div> - `, - animations: [trigger( - 'myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])])] - }) - class Cmp { - exp: any = false; - } + engine.flush(); - TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - let isDone = false; - fixture.whenRenderingDone().then(() => isDone = true); - expect(isDone).toBe(false); - - cmp.exp = 'on'; - fixture.detectChanges(); - engine.flush(); - expect(isDone).toBe(false); - - const players = engine.players; - expect(players.length).toEqual(1); - players[0].finish(); - expect(isDone).toBe(false); - - flushMicrotasks(); - expect(isDone).toBe(true); - })); - - it('should wait for a noop animation to finish before continuing', fakeAsync(() => { - @Component({ - selector: 'cmp', - template: ` - <div [@myAnimation]="exp"></div> - `, - animations: [trigger( - 'myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])])] - }) - class Cmp { - exp: any = false; - } + const players = engine.players; + expect(players.length).toEqual(1); - TestBed.configureTestingModule({ - providers: [{provide: AnimationDriver, useClass: NoopAnimationDriver}], - declarations: [Cmp] - }); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - let isDone = false; - fixture.whenRenderingDone().then(() => isDone = true); - expect(isDone).toBe(false); - - cmp.exp = 'off'; - fixture.detectChanges(); - engine.flush(); - expect(isDone).toBe(false); - - flushMicrotasks(); - expect(isDone).toBe(true); - })); - - it('should wait for active animations to finish even if they have already started', - fakeAsync(() => { - @Component({ - selector: 'cmp', - template: ` - <div [@myAnimation]="exp"></div> - `, - animations: [trigger( - 'myAnimation', [transition('* => on', [animate(1000, style({opacity: 1}))])])] - }) - class Cmp { - exp: any = false; - } + let isDone = false; + fixture.whenRenderingDone().then(() => isDone = true); + flushMicrotasks(); + expect(isDone).toBe(false); - TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'on'; - fixture.detectChanges(); - engine.flush(); - - const players = engine.players; - expect(players.length).toEqual(1); - - let isDone = false; - fixture.whenRenderingDone().then(() => isDone = true); - flushMicrotasks(); - expect(isDone).toBe(false); - - players[0].finish(); - flushMicrotasks(); - expect(isDone).toBe(true); - })); - }); + players[0].finish(); + flushMicrotasks(); + expect(isDone).toBe(true); + })); }); + }); - describe('animation triggers', () => { - it('should trigger a state change animation from void => state', () => { - @Component({ - selector: 'if-cmp', - template: ` + describe('animation triggers', () => { + it('should trigger a state change animation from void => state', () => { + @Component({ + selector: 'if-cmp', + template: ` <div *ngIf="exp" [@myAnimation]="exp"></div> `, - animations: [trigger( - 'myAnimation', - [transition( - 'void => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], - }) - class Cmp { - exp: any = false; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); + animations: [trigger( + 'myAnimation', + [transition( + 'void => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], + }) + class Cmp { + exp: any = false; + } - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = true; - fixture.detectChanges(); - engine.flush(); + TestBed.configureTestingModule({declarations: [Cmp]}); - expect(getLog().length).toEqual(1); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'} - ]); - }); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = true; + fixture.detectChanges(); + engine.flush(); + + expect(getLog().length).toEqual(1); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'} + ]); + }); - // https://github.com/angular/angular/issues/32794 - it('should support nested animation triggers', () => { - const REUSABLE_ANIMATION = [trigger( - 'myAnimation', - [transition( - 'void => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])]; + // https://github.com/angular/angular/issues/32794 + it('should support nested animation triggers', () => { + const REUSABLE_ANIMATION = [trigger('myAnimation', [ + transition('void => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))]) + ])]; - @Component({ - selector: 'if-cmp', - template: ` + @Component({ + selector: 'if-cmp', + template: ` <div @myAnimation></div> `, - animations: [REUSABLE_ANIMATION], - }) - class Cmp { - } - - TestBed.configureTestingModule({declarations: [Cmp]}); + animations: [REUSABLE_ANIMATION], + }) + class Cmp { + } - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - fixture.detectChanges(); - engine.flush(); + TestBed.configureTestingModule({declarations: [Cmp]}); - expect(getLog().length).toEqual(1); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'} - ]); - }); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); + engine.flush(); - it('should allow a transition to use a function to determine what method to run', () => { - let valueToMatch = ''; - let capturedElement: any; - const transitionFn = (fromState: string, toState: string, element: any) => { - capturedElement = element; - return toState == valueToMatch; - }; + expect(getLog().length).toEqual(1); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'} + ]); + }); - @Component({ - selector: 'if-cmp', - template: '<div #element [@myAnimation]="exp"></div>', - animations: [ - trigger('myAnimation', [transition( - transitionFn, - [style({opacity: 0}), animate(1234, style({opacity: 1}))])]), - ] - }) - class Cmp { - @ViewChild('element') - element: any; - exp: any = ''; - } + it('should allow a transition to use a function to determine what method to run', () => { + let valueToMatch = ''; + let capturedElement: any; + const transitionFn = (fromState: string, toState: string, element: any) => { + capturedElement = element; + return toState == valueToMatch; + }; - TestBed.configureTestingModule({declarations: [Cmp]}); + @Component({ + selector: 'if-cmp', + template: '<div #element [@myAnimation]="exp"></div>', + animations: [ + trigger('myAnimation', [transition( + transitionFn, + [style({opacity: 0}), animate(1234, style({opacity: 1}))])]), + ] + }) + class Cmp { + @ViewChild('element') element: any; + exp: any = ''; + } - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - valueToMatch = cmp.exp = 'something'; - fixture.detectChanges(); - const element = cmp.element.nativeElement; + TestBed.configureTestingModule({declarations: [Cmp]}); - let players = getLog(); - expect(players.length).toEqual(1); - let [p1] = players; - expect(p1.totalTime).toEqual(1234); - expect(capturedElement).toEqual(element); - resetLog(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + valueToMatch = cmp.exp = 'something'; + fixture.detectChanges(); + const element = cmp.element.nativeElement; + + let players = getLog(); + expect(players.length).toEqual(1); + let [p1] = players; + expect(p1.totalTime).toEqual(1234); + expect(capturedElement).toEqual(element); + resetLog(); - valueToMatch = 'something-else'; - cmp.exp = 'this-wont-match'; - fixture.detectChanges(); + valueToMatch = 'something-else'; + cmp.exp = 'this-wont-match'; + fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(0); - }); + players = getLog(); + expect(players.length).toEqual(0); + }); - it('should allow a transition to use a function to determine what method to run and expose any parameter values', - () => { - const transitionFn = - (fromState: string, toState: string, element?: any, - params?: {[key: string]: any}) => { return params !['doMatch'] == true; }; + it('should allow a transition to use a function to determine what method to run and expose any parameter values', + () => { + const transitionFn = + (fromState: string, toState: string, element?: any, params?: {[key: string]: any}) => { + return params!['doMatch'] == true; + }; - @Component({ - selector: 'if-cmp', - template: '<div [@myAnimation]="{value:exp, params: {doMatch:doMatch}}"></div>', - animations: [ - trigger( - 'myAnimation', - [transition( - transitionFn, [style({opacity: 0}), animate(3333, style({opacity: 1}))])]), - ] - }) - class Cmp { - doMatch = false; - exp: any = ''; - } + @Component({ + selector: 'if-cmp', + template: '<div [@myAnimation]="{value:exp, params: {doMatch:doMatch}}"></div>', + animations: [ + trigger( + 'myAnimation', + [transition( + transitionFn, [style({opacity: 0}), animate(3333, style({opacity: 1}))])]), + ] + }) + class Cmp { + doMatch = false; + exp: any = ''; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.doMatch = true; - fixture.detectChanges(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.doMatch = true; + fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(1); - let [p1] = players; - expect(p1.totalTime).toEqual(3333); - resetLog(); + let players = getLog(); + expect(players.length).toEqual(1); + let [p1] = players; + expect(p1.totalTime).toEqual(3333); + resetLog(); - cmp.doMatch = false; - cmp.exp = 'this-wont-match'; - fixture.detectChanges(); + cmp.doMatch = false; + cmp.exp = 'this-wont-match'; + fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(0); - }); + players = getLog(); + expect(players.length).toEqual(0); + }); - it('should allow a state value to be `0`', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should allow a state value to be `0`', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [trigger( - 'myAnimation', - [ - transition( - '0 => 1', [style({height: '0px'}), animate(1234, style({height: '100px'}))]), - transition( - '* => 1', [style({width: '0px'}), animate(4567, style({width: '100px'}))]) - ])] - }) - class Cmp { - exp: any = false; - } + animations: [trigger( + 'myAnimation', + [ + transition( + '0 => 1', [style({height: '0px'}), animate(1234, style({height: '100px'}))]), + transition('* => 1', [style({width: '0px'}), animate(4567, style({width: '100px'}))]) + ])] + }) + class Cmp { + exp: any = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 0; - fixture.detectChanges(); - engine.flush(); - resetLog(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 0; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp = 1; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 1; + fixture.detectChanges(); + engine.flush(); - expect(getLog().length).toEqual(1); - const player = getLog().pop() !; - expect(player.duration).toEqual(1234); - }); + expect(getLog().length).toEqual(1); + const player = getLog().pop()!; + expect(player.duration).toEqual(1234); + }); - it('should always cancel the previous transition if a follow-up transition is not matched', - fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + it('should always cancel the previous transition if a follow-up transition is not matched', + fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: ` <div [@myAnimation]="exp" (@myAnimation.start)="callback($event)" (@myAnimation.done)="callback($event)"></div> `, - animations: [trigger( - 'myAnimation', - [transition( - 'a => b', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], - }) - class Cmp { - exp: any; - startEvent: any; - doneEvent: any; - - callback(event: any) { - if (event.phaseName == 'done') { - this.doneEvent = event; - } else { - this.startEvent = event; - } + animations: [trigger( + 'myAnimation', + [transition( + 'a => b', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], + }) + class Cmp { + exp: any; + startEvent: any; + doneEvent: any; + + callback(event: any) { + if (event.phaseName == 'done') { + this.doneEvent = event; + } else { + this.startEvent = event; } } + } - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'a'; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(0); - expect(engine.players.length).toEqual(0); + TestBed.configureTestingModule({declarations: [Cmp]}); - flushMicrotasks(); - expect(cmp.startEvent.toState).toEqual('a'); - expect(cmp.startEvent.totalTime).toEqual(0); - expect(cmp.startEvent.toState).toEqual('a'); - expect(cmp.startEvent.totalTime).toEqual(0); - resetLog(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'a'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + expect(engine.players.length).toEqual(0); + + flushMicrotasks(); + expect(cmp.startEvent.toState).toEqual('a'); + expect(cmp.startEvent.totalTime).toEqual(0); + expect(cmp.startEvent.toState).toEqual('a'); + expect(cmp.startEvent.totalTime).toEqual(0); + resetLog(); + + cmp.exp = 'b'; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(1); - expect(engine.players.length).toEqual(1); + const players = getLog(); + expect(players.length).toEqual(1); + expect(engine.players.length).toEqual(1); - flushMicrotasks(); - expect(cmp.startEvent.toState).toEqual('b'); - expect(cmp.startEvent.totalTime).toEqual(500); - expect(cmp.startEvent.toState).toEqual('b'); - expect(cmp.startEvent.totalTime).toEqual(500); - resetLog(); + flushMicrotasks(); + expect(cmp.startEvent.toState).toEqual('b'); + expect(cmp.startEvent.totalTime).toEqual(500); + expect(cmp.startEvent.toState).toEqual('b'); + expect(cmp.startEvent.totalTime).toEqual(500); + resetLog(); - let completed = false; - players[0].onDone(() => completed = true); + let completed = false; + players[0].onDone(() => completed = true); - cmp.exp = 'c'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'c'; + fixture.detectChanges(); + engine.flush(); - expect(engine.players.length).toEqual(0); - expect(getLog().length).toEqual(0); + expect(engine.players.length).toEqual(0); + expect(getLog().length).toEqual(0); - flushMicrotasks(); - expect(cmp.startEvent.toState).toEqual('c'); - expect(cmp.startEvent.totalTime).toEqual(0); - expect(cmp.startEvent.toState).toEqual('c'); - expect(cmp.startEvent.totalTime).toEqual(0); + flushMicrotasks(); + expect(cmp.startEvent.toState).toEqual('c'); + expect(cmp.startEvent.totalTime).toEqual(0); + expect(cmp.startEvent.toState).toEqual('c'); + expect(cmp.startEvent.totalTime).toEqual(0); - expect(completed).toBe(true); - })); + expect(completed).toBe(true); + })); - it('should always fire inner callbacks even if no animation is fired when a view is inserted', - fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + it('should always fire inner callbacks even if no animation is fired when a view is inserted', + fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: ` <div *ngIf="exp"> <div @myAnimation (@myAnimation.start)="track($event)" (@myAnimation.done)="track($event)"></div> </div> `, - animations: [ - trigger('myAnimation', []), - ] - }) - class Cmp { - exp: any = false; - log: string[] = []; - track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); } + animations: [ + trigger('myAnimation', []), + ] + }) + class Cmp { + exp: any = false; + log: string[] = []; + track(event: any) { + this.log.push(`${event.triggerName}-${event.phaseName}`); } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - flushMicrotasks(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.log).toEqual([]); + expect(cmp.log).toEqual([]); - cmp.exp = true; - fixture.detectChanges(); - flushMicrotasks(); + cmp.exp = true; + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.log).toEqual(['myAnimation-start', 'myAnimation-done']); - })); + expect(cmp.log).toEqual(['myAnimation-start', 'myAnimation-done']); + })); - it('should only turn a view removal as into `void` state transition', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should only turn a view removal as into `void` state transition', () => { + @Component({ + selector: 'if-cmp', + template: ` <div *ngIf="exp1" [@myAnimation]="exp2"></div> `, - animations: [trigger( - 'myAnimation', - [ - transition( - 'void <=> *', [style({width: '0px'}), animate(1000, style({width: '100px'}))]), - transition( - '* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]), - ])] - }) - class Cmp { - exp1: any = false; - exp2: any = false; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + animations: [trigger( + 'myAnimation', + [ + transition( + 'void <=> *', [style({width: '0px'}), animate(1000, style({width: '100px'}))]), + transition( + '* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]), + ])] + }) + class Cmp { + exp1: any = false; + exp2: any = false; + } - function resetState() { - cmp.exp2 = 'something'; - fixture.detectChanges(); - engine.flush(); - resetLog(); - } + TestBed.configureTestingModule({declarations: [Cmp]}); - cmp.exp1 = true; - cmp.exp2 = null; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + function resetState() { + cmp.exp2 = 'something'; fixture.detectChanges(); engine.flush(); + resetLog(); + } - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, width: '0px'}, {offset: 1, width: '100px'} - ]); + cmp.exp1 = true; + cmp.exp2 = null; - resetState(); - cmp.exp2 = false; + fixture.detectChanges(); + engine.flush(); - fixture.detectChanges(); - engine.flush(); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, width: '0px'}, {offset: 1, width: '100px'} + ]); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, height: '0px'}, {offset: 1, height: '100px'} - ]); + resetState(); + cmp.exp2 = false; - resetState(); - cmp.exp2 = 0; + fixture.detectChanges(); + engine.flush(); - fixture.detectChanges(); - engine.flush(); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, height: '0px'}, {offset: 1, height: '100px'} + ]); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, height: '0px'}, {offset: 1, height: '100px'} - ]); + resetState(); + cmp.exp2 = 0; - resetState(); - cmp.exp2 = ''; + fixture.detectChanges(); + engine.flush(); - fixture.detectChanges(); - engine.flush(); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, height: '0px'}, {offset: 1, height: '100px'} + ]); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, height: '0px'}, {offset: 1, height: '100px'} - ]); + resetState(); + cmp.exp2 = ''; - resetState(); - cmp.exp2 = undefined; + fixture.detectChanges(); + engine.flush(); - fixture.detectChanges(); - engine.flush(); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, height: '0px'}, {offset: 1, height: '100px'} + ]); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, height: '0px'}, {offset: 1, height: '100px'} - ]); + resetState(); + cmp.exp2 = undefined; - resetState(); - cmp.exp1 = false; - cmp.exp2 = 'abc'; + fixture.detectChanges(); + engine.flush(); - fixture.detectChanges(); - engine.flush(); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, height: '0px'}, {offset: 1, height: '100px'} + ]); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, width: '0px'}, {offset: 1, width: '100px'} - ]); - }); + resetState(); + cmp.exp1 = false; + cmp.exp2 = 'abc'; - it('should stringify boolean triggers to `1` and `0`', () => { - @Component({ - selector: 'if-cmp', - template: ` + fixture.detectChanges(); + engine.flush(); + + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, width: '0px'}, {offset: 1, width: '100px'} + ]); + }); + + it('should stringify boolean triggers to `1` and `0`', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [trigger( - 'myAnimation', - [ - transition('void => 1', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), - transition('1 => 0', [style({opacity: 1}), animate(1000, style({opacity: 0}))]) - ])] - }) - class Cmp { - exp: any = false; - } + animations: [trigger( + 'myAnimation', + [ + transition('void => 1', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), + transition('1 => 0', [style({opacity: 1}), animate(1000, style({opacity: 0}))]) + ])] + }) + class Cmp { + exp: any = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = true; - fixture.detectChanges(); - engine.flush(); + cmp.exp = true; + fixture.detectChanges(); + engine.flush(); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'} - ]); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'} + ]); - cmp.exp = false; - fixture.detectChanges(); - engine.flush(); + cmp.exp = false; + fixture.detectChanges(); + engine.flush(); - expect(getLog().pop() !.keyframes).toEqual([ - {offset: 0, opacity: '1'}, {offset: 1, opacity: '0'} - ]); - }); + expect(getLog().pop()!.keyframes).toEqual([ + {offset: 0, opacity: '1'}, {offset: 1, opacity: '0'} + ]); + }); - it('should understand boolean values as `true` and `false` for transition animations', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should understand boolean values as `true` and `false` for transition animations', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - 'true => false', - [ - style({opacity: 0}), - animate(1234, style({opacity: 1})), - ]), - transition( - 'false => true', - [ - style({opacity: 1}), - animate(4567, style({opacity: 0})), - ]) - ]), - ] - }) - class Cmp { - exp: any = false; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + 'true => false', + [ + style({opacity: 0}), + animate(1234, style({opacity: 1})), + ]), + transition( + 'false => true', + [ + style({opacity: 1}), + animate(4567, style({opacity: 0})), + ]) + ]), + ] + }) + class Cmp { + exp: any = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = true; - fixture.detectChanges(); + cmp.exp = true; + fixture.detectChanges(); - cmp.exp = false; - fixture.detectChanges(); + cmp.exp = false; + fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(1); - let [player] = players; + let players = getLog(); + expect(players.length).toEqual(1); + let [player] = players; - expect(player.duration).toEqual(1234); - }); + expect(player.duration).toEqual(1234); + }); - it('should understand boolean values as `true` and `false` for transition animations and apply the corresponding state() value', - () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should understand boolean values as `true` and `false` for transition animations and apply the corresponding state() value', + () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - state('true', style({color: 'red'})), - state('false', style({color: 'blue'})), - transition( - 'true <=> false', - [ - animate(1000, style({color: 'gold'})), - animate(1000), - ]), - ]), - ] + animations: [ + trigger( + 'myAnimation', + [ + state('true', style({color: 'red'})), + state('false', style({color: 'blue'})), + transition( + 'true <=> false', + [ + animate(1000, style({color: 'gold'})), + animate(1000), + ]), + ]), + ] + }) + class Cmp { + exp: any = false; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = false; + fixture.detectChanges(); + + cmp.exp = true; + fixture.detectChanges(); + + let players = getLog(); + expect(players.length).toEqual(1); + let [player] = players; + + expect(player.keyframes).toEqual([ + {color: 'blue', offset: 0}, + {color: 'gold', offset: 0.5}, + {color: 'red', offset: 1}, + ]); + }); + + it('should not throw an error if a trigger with the same name exists in separate components', + () => { + @Component({selector: 'cmp1', template: '...', animations: [trigger('trig', [])]}) + class Cmp1 { + } + + @Component({selector: 'cmp2', template: '...', animations: [trigger('trig', [])]}) + class Cmp2 { + } + + TestBed.configureTestingModule({declarations: [Cmp1, Cmp2]}); + const cmp1 = TestBed.createComponent(Cmp1); + const cmp2 = TestBed.createComponent(Cmp2); + }); + + describe('host bindings', () => { + it('should trigger a state change animation from state => state on the component host element', + fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: '...', + animations: [trigger( + 'myAnimation', + [transition( + 'a => b', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], }) class Cmp { - exp: any = false; + @HostBinding('@myAnimation') exp = 'a'; } TestBed.configureTestingModule({declarations: [Cmp]}); @@ -813,435 +874,375 @@ const DEFAULT_COMPONENT_ID = '1'; const engine = TestBed.inject(ɵAnimationEngine); const fixture = TestBed.createComponent(Cmp); const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); - cmp.exp = false; + cmp.exp = 'b'; fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(1); + + const data = getLog().pop()!; + expect(data.element).toEqual(fixture.elementRef.nativeElement); + expect(data.keyframes).toEqual([{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}]); + })); + + it('should trigger a leave animation when the inner components host binding updates', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + template: ` + <child-cmp *ngIf="exp"></child-cmp> + ` + }) + class ParentCmp { + public exp = true; + } + + @Component({ + selector: 'child-cmp', + template: '...', + animations: [trigger( + 'host', + [transition(':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])])] + }) + class ChildCmp { + @HostBinding('@host') public hostAnimation = true; + } - cmp.exp = true; + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); - let players = getLog(); - expect(players.length).toEqual(1); - let [player] = players; + cmp.exp = false; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.children.length).toBe(1); + engine.flush(); + expect(getLog().length).toEqual(1); + + const [player] = getLog(); expect(player.keyframes).toEqual([ - {color: 'blue', offset: 0}, - {color: 'gold', offset: 0.5}, - {color: 'red', offset: 1}, + {opacity: '1', offset: 0}, + {opacity: '0', offset: 1}, ]); - }); - it('should not throw an error if a trigger with the same name exists in separate components', - () => { - @Component({selector: 'cmp1', template: '...', animations: [trigger('trig', [])]}) - class Cmp1 { + player.finish(); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + })); + + it('should wait for child animations before removing parent', fakeAsync(() => { + @Component({ + template: '<child-cmp *ngIf="exp" @parentTrigger></child-cmp>', + animations: [trigger( + 'parentTrigger', [transition(':leave', [group([query('@*', animateChild())])])])] + }) + class ParentCmp { + exp = true; } - @Component({selector: 'cmp2', template: '...', animations: [trigger('trig', [])]}) - class Cmp2 { + @Component({ + selector: 'child-cmp', + template: '<p @childTrigger>Hello there</p>', + animations: [trigger( + 'childTrigger', + [transition( + ':leave', [style({opacity: 1}), animate('200ms', style({opacity: 0}))])])] + }) + class ChildCmp { } - TestBed.configureTestingModule({declarations: [Cmp1, Cmp2]}); - const cmp1 = TestBed.createComponent(Cmp1); - const cmp2 = TestBed.createComponent(Cmp2); - }); + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); - describe('host bindings', () => { - it('should trigger a state change animation from state => state on the component host element', - fakeAsync(() => { - @Component({ - selector: 'my-cmp', - template: '...', - animations: [trigger( - 'myAnimation', - [transition( - 'a => b', - [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], - }) - class Cmp { - @HostBinding('@myAnimation') - exp = 'a'; - } + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toBe(0); - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(0); - - cmp.exp = 'b'; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(1); - - const data = getLog().pop() !; - expect(data.element).toEqual(fixture.elementRef.nativeElement); - expect(data.keyframes).toEqual([{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}]); - })); - - it('should trigger a leave animation when the inner components host binding updates', - fakeAsync(() => { - @Component({ - selector: 'parent-cmp', - template: ` - <child-cmp *ngIf="exp"></child-cmp> - ` - }) - class ParentCmp { - public exp = true; - } + fixture.componentInstance.exp = false; + fixture.detectChanges(); + expect(fixture.nativeElement.children.length).toBe(1); - @Component({ - selector: 'child-cmp', - template: '...', - animations: [trigger( - 'host', - [transition( - ':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])])] - }) - class ChildCmp { - @HostBinding('@host') public hostAnimation = true; - } + engine.flush(); + expect(getLog().length).toBe(1); - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(0); - - cmp.exp = false; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.children.length).toBe(1); - - engine.flush(); - expect(getLog().length).toEqual(1); - - const [player] = getLog(); - expect(player.keyframes).toEqual([ - {opacity: '1', offset: 0}, - {opacity: '0', offset: 1}, - ]); - - player.finish(); - expect(fixture.debugElement.nativeElement.children.length).toBe(0); - })); - - it('should wait for child animations before removing parent', fakeAsync(() => { - @Component({ - template: '<child-cmp *ngIf="exp" @parentTrigger></child-cmp>', - animations: [trigger( - 'parentTrigger', [transition(':leave', [group([query('@*', animateChild())])])])] - }) - class ParentCmp { - exp = true; - } + const player = getLog()[0]; + expect(player.keyframes).toEqual([ + {opacity: '1', offset: 0}, + {opacity: '0', offset: 1}, + ]); - @Component({ - selector: 'child-cmp', - template: '<p @childTrigger>Hello there</p>', - animations: [trigger( - 'childTrigger', - [transition( - ':leave', [style({opacity: 1}), animate('200ms', style({opacity: 0}))])])] - }) - class ChildCmp { - } + player.finish(); + flushMicrotasks(); + expect(fixture.nativeElement.children.length).toBe(0); + })); - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toBe(0); - - fixture.componentInstance.exp = false; - fixture.detectChanges(); - expect(fixture.nativeElement.children.length).toBe(1); - - engine.flush(); - expect(getLog().length).toBe(1); - - const player = getLog()[0]; - expect(player.keyframes).toEqual([ - {opacity: '1', offset: 0}, - {opacity: '0', offset: 1}, - ]); - - player.finish(); - flushMicrotasks(); - expect(fixture.nativeElement.children.length).toBe(0); - })); - - // animationRenderer => nonAnimationRenderer - it('should trigger a leave animation when the outer components element binding updates on the host component element', - fakeAsync(() => { - @Component({ - selector: 'parent-cmp', - animations: [trigger( - 'host', - [transition( - ':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])])], - template: ` + // animationRenderer => nonAnimationRenderer + it('should trigger a leave animation when the outer components element binding updates on the host component element', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + animations: [trigger( + 'host', + [transition( + ':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])])], + template: ` <child-cmp *ngIf="exp" @host></child-cmp> ` - }) - class ParentCmp { - public exp = true; - } + }) + class ParentCmp { + public exp = true; + } - @Component({ - selector: 'child-cmp', - template: '...', - }) - class ChildCmp { - } + @Component({ + selector: 'child-cmp', + template: '...', + }) + class ChildCmp { + } - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(0); - - cmp.exp = false; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.children.length).toBe(1); - - engine.flush(); - expect(getLog().length).toEqual(1); - - const [player] = getLog(); - expect(player.keyframes).toEqual([ - {opacity: '1', offset: 0}, - {opacity: '0', offset: 1}, - ]); - - player.finish(); - flushMicrotasks(); - expect(fixture.debugElement.nativeElement.children.length).toBe(0); - })); - - it('should trigger a leave animation when both the inner and outer components trigger on the same element', - fakeAsync(() => { - @Component({ - selector: 'parent-cmp', - animations: [trigger( - 'host', - [transition( - ':leave', - [style({height: '100px'}), animate(1000, style({height: '0px'}))])])], - template: ` + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + + cmp.exp = false; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.children.length).toBe(1); + + engine.flush(); + expect(getLog().length).toEqual(1); + + const [player] = getLog(); + expect(player.keyframes).toEqual([ + {opacity: '1', offset: 0}, + {opacity: '0', offset: 1}, + ]); + + player.finish(); + flushMicrotasks(); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + })); + + it('should trigger a leave animation when both the inner and outer components trigger on the same element', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + animations: [trigger( + 'host', [transition( + ':leave', + [style({height: '100px'}), animate(1000, style({height: '0px'}))])])], + template: ` <child-cmp *ngIf="exp" @host></child-cmp> ` - }) - class ParentCmp { - public exp = true; - } + }) + class ParentCmp { + public exp = true; + } - @Component({ - selector: 'child-cmp', - template: '...', - animations: [trigger( - 'host', [transition( - ':leave', - [style({width: '100px'}), animate(1000, style({width: '0px'}))])])] - }) - class ChildCmp { - @HostBinding('@host') public hostAnimation = true; - } + @Component({ + selector: 'child-cmp', + template: '...', + animations: [trigger( + 'host', + [transition( + ':leave', [style({width: '100px'}), animate(1000, style({width: '0px'}))])])] + }) + class ChildCmp { + @HostBinding('@host') public hostAnimation = true; + } - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(0); - - cmp.exp = false; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.children.length).toBe(1); - - engine.flush(); - expect(getLog().length).toEqual(2); - - const [p1, p2] = getLog(); - expect(p1.keyframes).toEqual([ - {width: '100px', offset: 0}, - {width: '0px', offset: 1}, - ]); - - expect(p2.keyframes).toEqual([ - {height: '100px', offset: 0}, - {height: '0px', offset: 1}, - ]); - - p1.finish(); - p2.finish(); - flushMicrotasks(); - expect(fixture.debugElement.nativeElement.children.length).toBe(0); - })); - - it('should not throw when the host element is removed and no animation triggers', - fakeAsync(() => { - @Component({ - selector: 'parent-cmp', - template: ` - <child-cmp *ngIf="exp"></child-cmp> - ` - }) - class ParentCmp { - public exp = true; - } + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - @Component({ - selector: 'child-cmp', - template: '...', - animations: [trigger('host', [transition('a => b', [style({height: '100px'})])])], - }) - class ChildCmp { - @HostBinding('@host') public hostAnimation = 'a'; - } + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.children.length).toBe(1); - - engine.flush(); - expect(getLog().length).toEqual(0); - - cmp.exp = false; - fixture.detectChanges(); - engine.flush(); - flushMicrotasks(); - expect(getLog().length).toEqual(0); - expect(fixture.debugElement.nativeElement.children.length).toBe(0); - - flushMicrotasks(); - expect(fixture.debugElement.nativeElement.children.length).toBe(0); - })); - - it('should properly evaluate pre/auto-style values when components are inserted/removed which contain host animations', - fakeAsync(() => { - @Component({ - selector: 'parent-cmp', - template: ` - <child-cmp *ngFor="let item of items"></child-cmp> - ` - }) - class ParentCmp { - items: any[] = [1, 2, 3, 4, 5]; - } + cmp.exp = false; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.children.length).toBe(1); - @Component({ - selector: 'child-cmp', - template: '... child ...', - animations: - [trigger('host', [transition(':leave', [animate(1000, style({opacity: 0}))])])] - }) - class ChildCmp { - @HostBinding('@host') public hostAnimation = 'a'; - } + engine.flush(); + expect(getLog().length).toEqual(2); - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + const [p1, p2] = getLog(); + expect(p1.keyframes).toEqual([ + {width: '100px', offset: 0}, + {width: '0px', offset: 1}, + ]); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - const element = fixture.nativeElement; - fixture.detectChanges(); + expect(p2.keyframes).toEqual([ + {height: '100px', offset: 0}, + {height: '0px', offset: 1}, + ]); - cmp.items = [0, 2, 4, 6]; // 1,3,5 get removed - fixture.detectChanges(); + p1.finish(); + p2.finish(); + flushMicrotasks(); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + })); - const items = element.querySelectorAll('child-cmp'); - for (let i = 0; i < items.length; i++) { - const item = items[i]; - expect(item.style['display']).toBeFalsy(); - } - })); - }); + it('should not throw when the host element is removed and no animation triggers', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + template: ` + <child-cmp *ngIf="exp"></child-cmp> + ` + }) + class ParentCmp { + public exp = true; + } - it('should cancel and merge in mid-animation styles into the follow-up animation, but only for animation keyframes that start right away', - () => { @Component({ - selector: 'ani-cmp', - template: ` - <div [@myAnimation]="exp"></div> - `, - animations: [trigger( - 'myAnimation', - [ - transition( - 'a => b', - [ - style({'opacity': '0'}), - animate(500, style({'opacity': '1'})), - ]), - transition( - 'b => c', - [ - group([ - animate(500, style({'width': '100px'})), - animate(500, style({'height': '100px'})), - ]), - animate(500, keyframes([ - style({'opacity': '0'}), - style({'opacity': '1'}) - ])) - ]), - ])], - }) - class Cmp { - exp: any = false; + selector: 'child-cmp', + template: '...', + animations: [trigger('host', [transition('a => b', [style({height: '100px'})])])], + }) + class ChildCmp { + @HostBinding('@host') public hostAnimation = 'a'; } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); + const fixture = TestBed.createComponent(ParentCmp); const cmp = fixture.componentInstance; - - cmp.exp = 'a'; fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.children.length).toBe(1); + engine.flush(); expect(getLog().length).toEqual(0); - resetLog(); - cmp.exp = 'b'; + cmp.exp = false; fixture.detectChanges(); engine.flush(); - expect(getLog().length).toEqual(1); - resetLog(); + flushMicrotasks(); + expect(getLog().length).toEqual(0); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); - cmp.exp = 'c'; - fixture.detectChanges(); - engine.flush(); + flushMicrotasks(); + expect(fixture.debugElement.nativeElement.children.length).toBe(0); + })); - const players = getLog(); - expect(players.length).toEqual(3); - const [p1, p2, p3] = players; - expect(p1.previousStyles).toEqual({opacity: AUTO_STYLE}); - expect(p2.previousStyles).toEqual({opacity: AUTO_STYLE}); - expect(p3.previousStyles).toEqual({}); - }); + it('should properly evaluate pre/auto-style values when components are inserted/removed which contain host animations', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + template: ` + <child-cmp *ngFor="let item of items"></child-cmp> + ` + }) + class ParentCmp { + items: any[] = [1, 2, 3, 4, 5]; + } - it('should provide the styling of previous players that are grouped', () => { - @Component({ + @Component({ + selector: 'child-cmp', + template: '... child ...', + animations: + [trigger('host', [transition(':leave', [animate(1000, style({opacity: 0}))])])] + }) + class ChildCmp { + @HostBinding('@host') public hostAnimation = 'a'; + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + const element = fixture.nativeElement; + fixture.detectChanges(); + + cmp.items = [0, 2, 4, 6]; // 1,3,5 get removed + fixture.detectChanges(); + + const items = element.querySelectorAll('child-cmp'); + for (let i = 0; i < items.length; i++) { + const item = items[i]; + expect(item.style['display']).toBeFalsy(); + } + })); + }); + + it('should cancel and merge in mid-animation styles into the follow-up animation, but only for animation keyframes that start right away', + () => { + @Component({ + selector: 'ani-cmp', + template: ` + <div [@myAnimation]="exp"></div> + `, + animations: [trigger( + 'myAnimation', + [ + transition( + 'a => b', + [ + style({'opacity': '0'}), + animate(500, style({'opacity': '1'})), + ]), + transition( + 'b => c', + [ + group([ + animate(500, style({'width': '100px'})), + animate(500, style({'height': '100px'})), + ]), + animate(500, keyframes([style({'opacity': '0'}), style({'opacity': '1'})])) + ]), + ])], + }) + class Cmp { + exp: any = false; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'a'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + resetLog(); + + cmp.exp = 'b'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(1); + resetLog(); + + cmp.exp = 'c'; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(3); + const [p1, p2, p3] = players; + expect(p1.previousStyles).toEqual({opacity: AUTO_STYLE}); + expect(p2.previousStyles).toEqual({opacity: AUTO_STYLE}); + expect(p3.previousStyles).toEqual({}); + }); + + it('should provide the styling of previous players that are grouped', () => { + @Component({ selector: 'ani-cmp', template: ` <div [@myAnimation]="exp"></div> @@ -1270,1637 +1271,1641 @@ const DEFAULT_COMPONENT_ID = '1'; ])], }) class Cmp { - exp: any = false; - } + exp: any = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - cmp.exp = '1'; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(0); - resetLog(); + cmp.exp = '1'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + resetLog(); - cmp.exp = '2'; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(3); - resetLog(); + cmp.exp = '2'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(3); + resetLog(); - cmp.exp = '3'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = '3'; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(1); - const player = players[0] as MockAnimationPlayer; - const pp = player.previousPlayers as MockAnimationPlayer[]; + const players = getLog(); + expect(players.length).toEqual(1); + const player = players[0] as MockAnimationPlayer; + const pp = player.previousPlayers as MockAnimationPlayer[]; - expect(pp.length).toEqual(3); - expect(pp[0].currentSnapshot).toEqual({width: AUTO_STYLE}); - expect(pp[1].currentSnapshot).toEqual({height: AUTO_STYLE}); - expect(pp[2].currentSnapshot).toEqual({opacity: AUTO_STYLE}); - }); + expect(pp.length).toEqual(3); + expect(pp[0].currentSnapshot).toEqual({width: AUTO_STYLE}); + expect(pp[1].currentSnapshot).toEqual({height: AUTO_STYLE}); + expect(pp[2].currentSnapshot).toEqual({opacity: AUTO_STYLE}); + }); - it('should provide the styling of previous players that are grouped and queried and make sure match the players with the correct elements', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should provide the styling of previous players that are grouped and queried and make sure match the players with the correct elements', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div class="container" [@myAnimation]="exp"> <div class="inner"></div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '1 => 2', - [ - style({fontSize: '10px'}), - query( - '.inner', - [ - style({fontSize: '20px'}), - ]), - animate('1s', style({fontSize: '100px'})), - query( - '.inner', - [ - animate('1s', style({fontSize: '200px'})), - ]), - ]), - transition( - '2 => 3', - [ - animate('1s', style({fontSize: '0px'})), - query( - '.inner', - [ - animate('1s', style({fontSize: '0px'})), - ]), - ]), - ]), - ], - }) - class Cmp { - exp: any = false; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '1 => 2', + [ + style({fontSize: '10px'}), + query( + '.inner', + [ + style({fontSize: '20px'}), + ]), + animate('1s', style({fontSize: '100px'})), + query( + '.inner', + [ + animate('1s', style({fontSize: '200px'})), + ]), + ]), + transition( + '2 => 3', + [ + animate('1s', style({fontSize: '0px'})), + query( + '.inner', + [ + animate('1s', style({fontSize: '0px'})), + ]), + ]), + ]), + ], + }) + class Cmp { + exp: any = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - fixture.detectChanges(); + fixture.detectChanges(); - cmp.exp = '1'; - fixture.detectChanges(); - resetLog(); + cmp.exp = '1'; + fixture.detectChanges(); + resetLog(); - cmp.exp = '2'; - fixture.detectChanges(); - resetLog(); + cmp.exp = '2'; + fixture.detectChanges(); + resetLog(); - cmp.exp = '3'; - fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(2); - const [p1, p2] = players as MockAnimationPlayer[]; - - const pp1 = p1.previousPlayers as MockAnimationPlayer[]; - expect(p1.element.classList.contains('container')).toBeTruthy(); - for (let i = 0; i < pp1.length; i++) { - expect(pp1[i].element).toEqual(p1.element); - } + cmp.exp = '3'; + fixture.detectChanges(); + const players = getLog(); + expect(players.length).toEqual(2); + const [p1, p2] = players as MockAnimationPlayer[]; + + const pp1 = p1.previousPlayers as MockAnimationPlayer[]; + expect(p1.element.classList.contains('container')).toBeTruthy(); + for (let i = 0; i < pp1.length; i++) { + expect(pp1[i].element).toEqual(p1.element); + } - const pp2 = p2.previousPlayers as MockAnimationPlayer[]; - expect(p2.element.classList.contains('inner')).toBeTruthy(); - for (let i = 0; i < pp2.length; i++) { - expect(pp2[i].element).toEqual(p2.element); - } - }); + const pp2 = p2.previousPlayers as MockAnimationPlayer[]; + expect(p2.element.classList.contains('inner')).toBeTruthy(); + for (let i = 0; i < pp2.length; i++) { + expect(pp2[i].element).toEqual(p2.element); + } + }); - it('should properly balance styles between states even if there are no destination state styles', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should properly balance styles between states even if there are no destination state styles', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div @myAnimation *ngIf="exp"></div> `, - animations: [trigger( - 'myAnimation', - [ - state('void', style({opacity: 0, width: '0px', height: '0px'})), - transition(':enter', animate(1000)) - ])] - }) - class Cmp { - exp: boolean = false; - } + animations: [trigger( + 'myAnimation', + [ + state('void', style({opacity: 0, width: '0px', height: '0px'})), + transition(':enter', animate(1000)) + ])] + }) + class Cmp { + exp: boolean = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = true; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = true; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - const [p1] = getLog(); - expect(p1.keyframes).toEqual([ - {opacity: '0', width: '0px', height: '0px', offset: 0}, - {opacity: AUTO_STYLE, width: AUTO_STYLE, height: AUTO_STYLE, offset: 1} - ]); - }); + const [p1] = getLog(); + expect(p1.keyframes).toEqual([ + {opacity: '0', width: '0px', height: '0px', offset: 0}, + {opacity: AUTO_STYLE, width: AUTO_STYLE, height: AUTO_STYLE, offset: 1} + ]); + }); - it('should not apply the destination styles if the final animate step already contains styles', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should not apply the destination styles if the final animate step already contains styles', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div @myAnimation *ngIf="exp"></div> `, - animations: [trigger( - 'myAnimation', - [ - state('void', style({color: 'red'})), state('*', style({color: 'blue'})), - transition( - ':enter', - [style({fontSize: '0px '}), animate(1000, style({fontSize: '100px'}))]) - ])] - }) - class Cmp { - exp: boolean = false; - } + animations: [trigger( + 'myAnimation', + [ + state('void', style({color: 'red'})), state('*', style({color: 'blue'})), + transition( + ':enter', + [style({fontSize: '0px '}), animate(1000, style({fontSize: '100px'}))]) + ])] + }) + class Cmp { + exp: boolean = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = true; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = true; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(1); + const players = getLog(); + expect(players.length).toEqual(1); - // notice how the final color is NOT blue - expect(players[0].keyframes).toEqual([ - {fontSize: '0px', color: 'red', offset: 0}, - {fontSize: '100px', color: 'red', offset: 1} - ]); - }); + // notice how the final color is NOT blue + expect(players[0].keyframes).toEqual([ + {fontSize: '0px', color: 'red', offset: 0}, {fontSize: '100px', color: 'red', offset: 1} + ]); + }); - it('should invoke an animation trigger that is state-less', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should invoke an animation trigger that is state-less', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div *ngFor="let item of items" @myAnimation></div> `, - animations: [trigger( - 'myAnimation', - [transition(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])] - }) - class Cmp { - items: number[] = []; - } + animations: [trigger( + 'myAnimation', + [transition(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])] + }) + class Cmp { + items: number[] = []; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.items = [1, 2, 3, 4, 5]; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(5); + cmp.items = [1, 2, 3, 4, 5]; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(5); - for (let i = 0; i < 5; i++) { - const item = getLog()[i]; - expect(item.duration).toEqual(1000); - expect(item.keyframes).toEqual([{opacity: '0', offset: 0}, {opacity: '1', offset: 1}]); - } - }); + for (let i = 0; i < 5; i++) { + const item = getLog()[i]; + expect(item.duration).toEqual(1000); + expect(item.keyframes).toEqual([{opacity: '0', offset: 0}, {opacity: '1', offset: 1}]); + } + }); - it('should retain styles on the element once the animation is complete', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should retain styles on the element once the animation is complete', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #green @green></div> `, - animations: [trigger( - 'green', - [ - state('*', style({backgroundColor: 'green'})), transition('* => *', animate(500)) - ])] - }) - class Cmp { - @ViewChild('green') public element: any; - } + animations: [trigger( + 'green', + [state('*', style({backgroundColor: 'green'})), transition('* => *', animate(500))])] + }) + class Cmp { + @ViewChild('green') public element: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); - const player = engine.players.pop() !; - player.finish(); + const player = engine.players.pop()!; + player.finish(); - expect(hasStyle(cmp.element.nativeElement, 'background-color', 'green')).toBeTruthy(); - }); + expect(hasStyle(cmp.element.nativeElement, 'background-color', 'green')).toBeTruthy(); + }); - it('should retain state styles when the underlying DOM structure changes even if there are no insert/remove animations', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should retain state styles when the underlying DOM structure changes even if there are no insert/remove animations', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div class="item" *ngFor="let item of items" [@color]="colorExp"> {{ item }} </div> `, - animations: [trigger('color', [state('green', style({backgroundColor: 'green'}))])] - }) - class Cmp { - public colorExp = 'green'; - public items = [0, 1, 2, 3]; + animations: [trigger('color', [state('green', style({backgroundColor: 'green'}))])] + }) + class Cmp { + public colorExp = 'green'; + public items = [0, 1, 2, 3]; - reorder() { - const temp = this.items[0]; - this.items[0] = this.items[1]; - this.items[1] = temp; - } + reorder() { + const temp = this.items[0]; + this.items[0] = this.items[1]; + this.items[1] = temp; } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); - let elements: HTMLElement[] = fixture.nativeElement.querySelectorAll('.item'); - assertBackgroundColor(elements[0], 'green'); - assertBackgroundColor(elements[1], 'green'); - assertBackgroundColor(elements[2], 'green'); - assertBackgroundColor(elements[3], 'green'); + let elements: HTMLElement[] = fixture.nativeElement.querySelectorAll('.item'); + assertBackgroundColor(elements[0], 'green'); + assertBackgroundColor(elements[1], 'green'); + assertBackgroundColor(elements[2], 'green'); + assertBackgroundColor(elements[3], 'green'); - elements[0].title = '0a'; - elements[1].title = '1a'; + elements[0].title = '0a'; + elements[1].title = '1a'; - cmp.reorder(); - fixture.detectChanges(); + cmp.reorder(); + fixture.detectChanges(); - elements = fixture.nativeElement.querySelectorAll('.item'); - assertBackgroundColor(elements[0], 'green'); - assertBackgroundColor(elements[1], 'green'); - assertBackgroundColor(elements[2], 'green'); - assertBackgroundColor(elements[3], 'green'); + elements = fixture.nativeElement.querySelectorAll('.item'); + assertBackgroundColor(elements[0], 'green'); + assertBackgroundColor(elements[1], 'green'); + assertBackgroundColor(elements[2], 'green'); + assertBackgroundColor(elements[3], 'green'); - function assertBackgroundColor(element: HTMLElement, color: string) { - expect(element.style.getPropertyValue('background-color')).toEqual(color); - } - }); + function assertBackgroundColor(element: HTMLElement, color: string) { + expect(element.style.getPropertyValue('background-color')).toEqual(color); + } + }); - it('should retain state styles when the underlying DOM structure changes even if there are insert/remove animations', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should retain state styles when the underlying DOM structure changes even if there are insert/remove animations', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div class="item" *ngFor="let item of items" [@color]="colorExp"> {{ item }} </div> `, - animations: [trigger( - 'color', - [ - transition('* => *', animate(500)), - state('green', style({backgroundColor: 'green'})) - ])] - }) - class Cmp { - public colorExp = 'green'; - public items = [0, 1, 2, 3]; + animations: [trigger( + 'color', + [ + transition('* => *', animate(500)), + state('green', style({backgroundColor: 'green'})) + ])] + }) + class Cmp { + public colorExp = 'green'; + public items = [0, 1, 2, 3]; - reorder() { - const temp = this.items[0]; - this.items[0] = this.items[1]; - this.items[1] = temp; - } + reorder() { + const temp = this.items[0]; + this.items[0] = this.items[1]; + this.items[1] = temp; } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); - getLog().forEach(p => p.finish()); + getLog().forEach(p => p.finish()); - let elements: HTMLElement[] = fixture.nativeElement.querySelectorAll('.item'); - assertBackgroundColor(elements[0], 'green'); - assertBackgroundColor(elements[1], 'green'); - assertBackgroundColor(elements[2], 'green'); - assertBackgroundColor(elements[3], 'green'); + let elements: HTMLElement[] = fixture.nativeElement.querySelectorAll('.item'); + assertBackgroundColor(elements[0], 'green'); + assertBackgroundColor(elements[1], 'green'); + assertBackgroundColor(elements[2], 'green'); + assertBackgroundColor(elements[3], 'green'); - elements[0].title = '0a'; - elements[1].title = '1a'; + elements[0].title = '0a'; + elements[1].title = '1a'; - cmp.reorder(); - fixture.detectChanges(); + cmp.reorder(); + fixture.detectChanges(); - getLog().forEach(p => p.finish()); + getLog().forEach(p => p.finish()); - elements = fixture.nativeElement.querySelectorAll('.item'); - assertBackgroundColor(elements[0], 'green'); - assertBackgroundColor(elements[1], 'green'); - assertBackgroundColor(elements[2], 'green'); - assertBackgroundColor(elements[3], 'green'); + elements = fixture.nativeElement.querySelectorAll('.item'); + assertBackgroundColor(elements[0], 'green'); + assertBackgroundColor(elements[1], 'green'); + assertBackgroundColor(elements[2], 'green'); + assertBackgroundColor(elements[3], 'green'); - function assertBackgroundColor(element: HTMLElement, color: string) { - expect(element.style.getPropertyValue('background-color')).toEqual(color); - } - }); + function assertBackgroundColor(element: HTMLElement, color: string) { + expect(element.style.getPropertyValue('background-color')).toEqual(color); + } + }); - it('should animate removals of nodes to the `void` state for each animation trigger, but treat all auto styles as pre styles', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should animate removals of nodes to the `void` state for each animation trigger, but treat all auto styles as pre styles', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div *ngIf="exp" class="ng-if" [@trig1]="exp2" @trig2></div> `, - animations: [ - trigger( - 'trig1', [transition('state => void', [animate(1000, style({opacity: 0}))])]), - trigger('trig2', [transition(':leave', [animate(1000, style({width: '0px'}))])]) - ] - }) - class Cmp { - public exp = true; - public exp2 = 'state'; - } + animations: [ + trigger('trig1', [transition('state => void', [animate(1000, style({opacity: 0}))])]), + trigger('trig2', [transition(':leave', [animate(1000, style({width: '0px'}))])]) + ] + }) + class Cmp { + public exp = true; + public exp2 = 'state'; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = true; - fixture.detectChanges(); - engine.flush(); - resetLog(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = true; + fixture.detectChanges(); + engine.flush(); + resetLog(); - const element = fixture.nativeElement.querySelector('.ng-if'); - assertHasParent(element, true); + const element = fixture.nativeElement.querySelector('.ng-if'); + assertHasParent(element, true); - cmp.exp = false; - fixture.detectChanges(); - engine.flush(); + cmp.exp = false; + fixture.detectChanges(); + engine.flush(); - assertHasParent(element, true); + assertHasParent(element, true); - expect(getLog().length).toEqual(2); + expect(getLog().length).toEqual(2); - const player2 = getLog().pop() !; - const player1 = getLog().pop() !; + const player2 = getLog().pop()!; + const player1 = getLog().pop()!; - expect(player2.keyframes).toEqual([ - {width: PRE_STYLE, offset: 0}, - {width: '0px', offset: 1}, - ]); + expect(player2.keyframes).toEqual([ + {width: PRE_STYLE, offset: 0}, + {width: '0px', offset: 1}, + ]); - expect(player1.keyframes).toEqual([ - {opacity: PRE_STYLE, offset: 0}, {opacity: '0', offset: 1} - ]); + expect(player1.keyframes).toEqual([ + {opacity: PRE_STYLE, offset: 0}, {opacity: '0', offset: 1} + ]); - player2.finish(); - player1.finish(); - assertHasParent(element, false); - }); + player2.finish(); + player1.finish(); + assertHasParent(element, false); + }); - it('should properly cancel all existing animations when a removal occurs', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should properly cancel all existing animations when a removal occurs', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div *ngIf="exp" [@myAnimation]="exp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', [style({width: '0px'}), animate(1000, style({width: '100px'}))]), - ]), - ] - }) - class Cmp { - // TODO(issue/24571): remove '!'. - public exp !: string | null; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', [style({width: '0px'}), animate(1000, style({width: '100px'}))]), + ]), + ] + }) + class Cmp { + // TODO(issue/24571): remove '!'. + public exp!: string|null; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(1); - const [player1] = getLog(); - resetLog(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(1); + const [player1] = getLog(); + resetLog(); - let finished = false; - player1.onDone(() => finished = true); + let finished = false; + player1.onDone(() => finished = true); - let destroyed = false; - player1.onDestroy(() => destroyed = true); + let destroyed = false; + player1.onDestroy(() => destroyed = true); - cmp.exp = null; - fixture.detectChanges(); - engine.flush(); + cmp.exp = null; + fixture.detectChanges(); + engine.flush(); - expect(finished).toBeTruthy(); - expect(destroyed).toBeTruthy(); - }); + expect(finished).toBeTruthy(); + expect(destroyed).toBeTruthy(); + }); - it('should not run inner child animations when a parent is set to be removed', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should not run inner child animations when a parent is set to be removed', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div *ngIf="exp" class="parent" > <div [@myAnimation]="exp2"></div> </div> `, - animations: [trigger( - 'myAnimation', [transition('a => b', [animate(1000, style({width: '0px'}))])])] - }) - class Cmp { - public exp = true; - public exp2 = '0'; - } + animations: + [trigger('myAnimation', [transition('a => b', [animate(1000, style({width: '0px'}))])])] + }) + class Cmp { + public exp = true; + public exp2 = '0'; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = true; - cmp.exp2 = 'a'; - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.exp = true; + cmp.exp2 = 'a'; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp = false; - cmp.exp2 = 'b'; - fixture.detectChanges(); - engine.flush(); - expect(getLog().length).toEqual(0); - }); + cmp.exp = false; + cmp.exp2 = 'b'; + fixture.detectChanges(); + engine.flush(); + expect(getLog().length).toEqual(0); + }); - it('should cancel all active inner child animations when a parent removal animation is set to go', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should cancel all active inner child animations when a parent removal animation is set to go', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div *ngIf="exp1" @parent> <div [@child]="exp2" class="child1"></div> <div [@child]="exp2" class="child2"></div> </div> `, - animations: [ - trigger('parent', [transition( - ':leave', - [style({opacity: 0}), animate(1000, style({opacity: 1}))])]), - trigger('child', [transition( - 'a => b', - [style({opacity: 0}), animate(1000, style({opacity: 1}))])]) - ] - }) - class Cmp { - public exp1: any; - public exp2: any; - } + animations: [ + trigger( + 'parent', + [transition(':leave', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]), + trigger( + 'child', + [transition('a => b', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]) + ] + }) + class Cmp { + public exp1: any; + public exp2: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp1 = true; - cmp.exp2 = 'a'; - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.exp1 = true; + cmp.exp2 = 'a'; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp2 = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp2 = 'b'; + fixture.detectChanges(); + engine.flush(); - let players = getLog(); - expect(players.length).toEqual(2); - const [p1, p2] = players; + let players = getLog(); + expect(players.length).toEqual(2); + const [p1, p2] = players; - let count = 0; - p1.onDone(() => count++); - p2.onDone(() => count++); + let count = 0; + p1.onDone(() => count++); + p2.onDone(() => count++); - cmp.exp1 = false; - fixture.detectChanges(); - engine.flush(); + cmp.exp1 = false; + fixture.detectChanges(); + engine.flush(); - expect(count).toEqual(2); - }); + expect(count).toEqual(2); + }); - it('should destroy inner animations when a parent node is set for removal', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should destroy inner animations when a parent node is set for removal', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #parent class="parent"> <div [@child]="exp" class="child1"></div> <div [@child]="exp" class="child2"></div> </div> `, - animations: [trigger( - 'child', - [transition('a => b', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])] - }) - class Cmp { - public exp: any; + animations: [trigger( + 'child', + [transition('a => b', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])] + }) + class Cmp { + public exp: any; - @ViewChild('parent') public parentElement: any; - } + @ViewChild('parent') public parentElement: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - const someTrigger = trigger('someTrigger', []); - const hostElement = fixture.nativeElement; - engine.register(DEFAULT_NAMESPACE_ID, hostElement); - engine.registerTrigger( - DEFAULT_COMPONENT_ID, DEFAULT_NAMESPACE_ID, hostElement, someTrigger.name, someTrigger); + const someTrigger = trigger('someTrigger', []); + const hostElement = fixture.nativeElement; + engine.register(DEFAULT_NAMESPACE_ID, hostElement); + engine.registerTrigger( + DEFAULT_COMPONENT_ID, DEFAULT_NAMESPACE_ID, hostElement, someTrigger.name, someTrigger); - cmp.exp = 'a'; - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.exp = 'a'; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'b'; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(2); - const [p1, p2] = players; + const players = getLog(); + expect(players.length).toEqual(2); + const [p1, p2] = players; - let count = 0; - p1.onDone(() => count++); - p2.onDone(() => count++); + let count = 0; + p1.onDone(() => count++); + p2.onDone(() => count++); - engine.onRemove(DEFAULT_NAMESPACE_ID, cmp.parentElement.nativeElement, null); - expect(count).toEqual(2); - }); + engine.onRemove(DEFAULT_NAMESPACE_ID, cmp.parentElement.nativeElement, null); + expect(count).toEqual(2); + }); - it('should allow inner removals to happen when a non removal-based parent animation is set to animate', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should allow inner removals to happen when a non removal-based parent animation is set to animate', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #parent [@parent]="exp1" class="parent"> <div #child *ngIf="exp2" class="child"></div> </div> `, - animations: [trigger( - 'parent', - [transition( - 'a => b', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])] - }) - class Cmp { - public exp1: any; - public exp2: any; + animations: [trigger( + 'parent', + [transition('a => b', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])] + }) + class Cmp { + public exp1: any; + public exp2: any; - @ViewChild('parent') public parent: any; + @ViewChild('parent') public parent: any; - @ViewChild('child') public child: any; - } + @ViewChild('child') public child: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp1 = 'a'; - cmp.exp2 = true; - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.exp1 = 'a'; + cmp.exp2 = true; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp1 = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp1 = 'b'; + fixture.detectChanges(); + engine.flush(); - const player = getLog()[0]; - const p = cmp.parent.nativeElement; - const c = cmp.child.nativeElement; + const player = getLog()[0]; + const p = cmp.parent.nativeElement; + const c = cmp.child.nativeElement; - expect(p.contains(c)).toBeTruthy(); + expect(p.contains(c)).toBeTruthy(); - cmp.exp2 = false; - fixture.detectChanges(); - engine.flush(); + cmp.exp2 = false; + fixture.detectChanges(); + engine.flush(); - expect(p.contains(c)).toBeFalsy(); + expect(p.contains(c)).toBeFalsy(); - player.finish(); + player.finish(); - expect(p.contains(c)).toBeFalsy(); - }); + expect(p.contains(c)).toBeFalsy(); + }); - it('should make inner removals wait until a parent based removal animation has finished', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should make inner removals wait until a parent based removal animation has finished', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #parent *ngIf="exp1" @parent class="parent"> <div #child1 *ngIf="exp2" class="child1"></div> <div #child2 *ngIf="exp2" class="child2"></div> </div> `, - animations: [trigger( - 'parent', - [transition( - ':leave', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])] - }) - class Cmp { - public exp1: any; - public exp2: any; + animations: [trigger( + 'parent', + [transition(':leave', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])] + }) + class Cmp { + public exp1: any; + public exp2: any; - @ViewChild('parent') public parent: any; + @ViewChild('parent') public parent: any; - @ViewChild('child1') public child1Elm: any; + @ViewChild('child1') public child1Elm: any; - @ViewChild('child2') public child2Elm: any; - } + @ViewChild('child2') public child2Elm: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp1 = true; - cmp.exp2 = true; - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.exp1 = true; + cmp.exp2 = true; + fixture.detectChanges(); + engine.flush(); + resetLog(); - const p = cmp.parent.nativeElement; - const c1 = cmp.child1Elm.nativeElement; - const c2 = cmp.child2Elm.nativeElement; + const p = cmp.parent.nativeElement; + const c1 = cmp.child1Elm.nativeElement; + const c2 = cmp.child2Elm.nativeElement; - cmp.exp1 = false; - cmp.exp2 = false; - fixture.detectChanges(); - engine.flush(); + cmp.exp1 = false; + cmp.exp2 = false; + fixture.detectChanges(); + engine.flush(); - expect(p.contains(c1)).toBeTruthy(); - expect(p.contains(c2)).toBeTruthy(); + expect(p.contains(c1)).toBeTruthy(); + expect(p.contains(c2)).toBeTruthy(); - cmp.exp2 = false; - fixture.detectChanges(); - engine.flush(); + cmp.exp2 = false; + fixture.detectChanges(); + engine.flush(); - expect(p.contains(c1)).toBeTruthy(); - expect(p.contains(c2)).toBeTruthy(); - }); + expect(p.contains(c1)).toBeTruthy(); + expect(p.contains(c2)).toBeTruthy(); + }); - it('should detect trigger changes based on object.value properties', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should detect trigger changes based on object.value properties', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="{value:exp}"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition('* => 1', [animate(1234, style({opacity: 0}))]), - transition('* => 2', [animate(5678, style({opacity: 0}))]), - ]), - ] - }) - class Cmp { - public exp: any; - } + animations: [ + trigger( + 'myAnimation', + [ + transition('* => 1', [animate(1234, style({opacity: 0}))]), + transition('* => 2', [animate(5678, style({opacity: 0}))]), + ]), + ] + }) + class Cmp { + public exp: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = '1'; - fixture.detectChanges(); - engine.flush(); - let players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].duration).toEqual(1234); - resetLog(); + cmp.exp = '1'; + fixture.detectChanges(); + engine.flush(); + let players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].duration).toEqual(1234); + resetLog(); - cmp.exp = '2'; - fixture.detectChanges(); - engine.flush(); - players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].duration).toEqual(5678); - }); + cmp.exp = '2'; + fixture.detectChanges(); + engine.flush(); + players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].duration).toEqual(5678); + }); - it('should not render animations when the object expression value is the same as it was previously', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should not render animations when the object expression value is the same as it was previously', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="{value:exp,params:params}"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition('* => *', [animate(1234, style({opacity: 0}))]), - ]), - ] - }) - class Cmp { - public exp: any; - public params: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); + animations: [ + trigger( + 'myAnimation', + [ + transition('* => *', [animate(1234, style({opacity: 0}))]), + ]), + ] + }) + class Cmp { + public exp: any; + public params: any; + } - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + TestBed.configureTestingModule({declarations: [Cmp]}); - cmp.exp = '1'; - cmp.params = {}; - fixture.detectChanges(); - engine.flush(); - let players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].duration).toEqual(1234); - resetLog(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = '1'; - cmp.params = {}; - fixture.detectChanges(); - engine.flush(); - players = getLog(); - expect(players.length).toEqual(0); - }); + cmp.exp = '1'; + cmp.params = {}; + fixture.detectChanges(); + engine.flush(); + let players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].duration).toEqual(1234); + resetLog(); + + cmp.exp = '1'; + cmp.params = {}; + fixture.detectChanges(); + engine.flush(); + players = getLog(); + expect(players.length).toEqual(0); + }); - it('should update the final state styles when params update even if the expression hasn\'t changed', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should update the final state styles when params update even if the expression hasn\'t changed', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="{value:exp,params:{color:color}}"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - state('*', style({color: '{{ color }}'}), {params: {color: 'black'}}), - transition('* => 1', animate(500)) - ]), - ] - }) - class Cmp { - public exp: any; - // TODO(issue/24571): remove '!'. - public color !: string | null; - } + animations: [ + trigger( + 'myAnimation', + [ + state('*', style({color: '{{ color }}'}), {params: {color: 'black'}}), + transition('* => 1', animate(500)) + ]), + ] + }) + class Cmp { + public exp: any; + // TODO(issue/24571): remove '!'. + public color!: string|null; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = '1'; - cmp.color = 'red'; - fixture.detectChanges(); - const player = getLog()[0] !; - const element = player.element; - player.finish(); + cmp.exp = '1'; + cmp.color = 'red'; + fixture.detectChanges(); + const player = getLog()[0]!; + const element = player.element; + player.finish(); - flushMicrotasks(); - expect(hasStyle(element, 'color', 'red')).toBeTruthy(); + flushMicrotasks(); + expect(hasStyle(element, 'color', 'red')).toBeTruthy(); - cmp.exp = '1'; - cmp.color = 'blue'; - fixture.detectChanges(); - resetLog(); + cmp.exp = '1'; + cmp.color = 'blue'; + fixture.detectChanges(); + resetLog(); - flushMicrotasks(); - expect(hasStyle(element, 'color', 'blue')).toBeTruthy(); + flushMicrotasks(); + expect(hasStyle(element, 'color', 'blue')).toBeTruthy(); - cmp.exp = '1'; - cmp.color = null; - fixture.detectChanges(); - resetLog(); + cmp.exp = '1'; + cmp.color = null; + fixture.detectChanges(); + resetLog(); - flushMicrotasks(); - expect(hasStyle(element, 'color', 'black')).toBeTruthy(); - })); + flushMicrotasks(); + expect(hasStyle(element, 'color', 'black')).toBeTruthy(); + })); - it('should substitute in values if the provided state match is an object with values', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should substitute in values if the provided state match is an object with values', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [trigger( - 'myAnimation', - [transition( - 'a => b', - [style({opacity: '{{ start }}'}), animate(1000, style({opacity: '{{ end }}'}))], - buildParams({start: '0', end: '1'}))])] - }) - class Cmp { - public exp: any; - } + animations: [trigger( + 'myAnimation', + [transition( + 'a => b', + [style({opacity: '{{ start }}'}), animate(1000, style({opacity: '{{ end }}'}))], + buildParams({start: '0', end: '1'}))])] + }) + class Cmp { + public exp: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = {value: 'a'}; - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.exp = {value: 'a'}; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp = {value: 'b', params: {start: .3, end: .6}}; - fixture.detectChanges(); - engine.flush(); - const player = getLog().pop() !; - expect(player.keyframes).toEqual([ - {opacity: '0.3', offset: 0}, {opacity: '0.6', offset: 1} - ]); - }); + cmp.exp = {value: 'b', params: {start: .3, end: .6}}; + fixture.detectChanges(); + engine.flush(); + const player = getLog().pop()!; + expect(player.keyframes).toEqual([{opacity: '0.3', offset: 0}, {opacity: '0.6', offset: 1}]); + }); - it('should retain substituted styles on the element once the animation is complete if referenced in the final state', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should retain substituted styles on the element once the animation is complete if referenced in the final state', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="{value:exp, params: { color: color }}"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - state( - 'start', style({ - color: '{{ color }}', - fontSize: '{{ fontSize }}px', - width: '{{ width }}' - }), - {params: {color: 'red', fontSize: '200', width: '10px'}}), - - state( - 'final', - style( - {color: '{{ color }}', fontSize: '{{ fontSize }}px', width: '888px'}), - {params: {color: 'green', fontSize: '50', width: '100px'}}), - - transition('start => final', animate(500)), - ]), - ] - }) - class Cmp { - public exp: any; - public color: any; - } + animations: [ + trigger( + 'myAnimation', + [ + state( + 'start', style({ + color: '{{ color }}', + fontSize: '{{ fontSize }}px', + width: '{{ width }}' + }), + {params: {color: 'red', fontSize: '200', width: '10px'}}), + + state( + 'final', + style({color: '{{ color }}', fontSize: '{{ fontSize }}px', width: '888px'}), + {params: {color: 'green', fontSize: '50', width: '100px'}}), + + transition('start => final', animate(500)), + ]), + ] + }) + class Cmp { + public exp: any; + public color: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'start'; - cmp.color = 'red'; - fixture.detectChanges(); - resetLog(); + cmp.exp = 'start'; + cmp.color = 'red'; + fixture.detectChanges(); + resetLog(); - cmp.exp = 'final'; - cmp.color = 'blue'; - fixture.detectChanges(); + cmp.exp = 'final'; + cmp.color = 'blue'; + fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(1); - const [p1] = players; + const players = getLog(); + expect(players.length).toEqual(1); + const [p1] = players; - expect(p1.keyframes).toEqual([ - {color: 'red', fontSize: '200px', width: '10px', offset: 0}, - {color: 'blue', fontSize: '50px', width: '888px', offset: 1} - ]); + expect(p1.keyframes).toEqual([ + {color: 'red', fontSize: '200px', width: '10px', offset: 0}, + {color: 'blue', fontSize: '50px', width: '888px', offset: 1} + ]); - const element = p1.element; - p1.finish(); - flushMicrotasks(); + const element = p1.element; + p1.finish(); + flushMicrotasks(); - expect(hasStyle(element, 'color', 'blue')).toBeTruthy(); - expect(hasStyle(element, 'fontSize', '50px')).toBeTruthy(); - expect(hasStyle(element, 'width', '888px')).toBeTruthy(); - })); + expect(hasStyle(element, 'color', 'blue')).toBeTruthy(); + expect(hasStyle(element, 'fontSize', '50px')).toBeTruthy(); + expect(hasStyle(element, 'width', '888px')).toBeTruthy(); + })); - it('should only evaluate final state param substitutions from the expression and state values and not from the transition options ', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - template: ` - <div [@myAnimation]="exp"></div> + it('should only evaluate final state param substitutions from the expression and state values and not from the transition options ', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + template: ` + <div [@myAnimation]="exp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - state( - 'start', style({ - width: '{{ width }}', - height: '{{ height }}', - }), - {params: {width: '0px', height: '0px'}}), - - state( - 'final', style({ - width: '{{ width }}', - height: '{{ height }}', - }), - {params: {width: '100px', height: '100px'}}), + animations: [ + trigger( + 'myAnimation', + [ + state( + 'start', style({ + width: '{{ width }}', + height: '{{ height }}', + }), + {params: {width: '0px', height: '0px'}}), + + state( + 'final', style({ + width: '{{ width }}', + height: '{{ height }}', + }), + {params: {width: '100px', height: '100px'}}), - transition( - 'start => final', [animate(500)], - {params: {width: '333px', height: '666px'}}), - ]), - ] - }) - class Cmp { - public exp: any; - } + transition( + 'start => final', [animate(500)], + {params: {width: '333px', height: '666px'}}), + ]), + ] + }) + class Cmp { + public exp: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'start'; - fixture.detectChanges(); - resetLog(); + cmp.exp = 'start'; + fixture.detectChanges(); + resetLog(); - cmp.exp = 'final'; - fixture.detectChanges(); + cmp.exp = 'final'; + fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(1); - const [p1] = players; + const players = getLog(); + expect(players.length).toEqual(1); + const [p1] = players; - expect(p1.keyframes).toEqual([ - {width: '0px', height: '0px', offset: 0}, - {width: '100px', height: '100px', offset: 1}, - ]); + expect(p1.keyframes).toEqual([ + {width: '0px', height: '0px', offset: 0}, + {width: '100px', height: '100px', offset: 1}, + ]); - const element = p1.element; - p1.finish(); - flushMicrotasks(); + const element = p1.element; + p1.finish(); + flushMicrotasks(); - expect(hasStyle(element, 'width', '100px')).toBeTruthy(); - expect(hasStyle(element, 'height', '100px')).toBeTruthy(); - })); + expect(hasStyle(element, 'width', '100px')).toBeTruthy(); + expect(hasStyle(element, 'height', '100px')).toBeTruthy(); + })); - it('should not flush animations twice when an inner component runs change detection', () => { - @Component({ - selector: 'outer-cmp', - template: ` + it('should not flush animations twice when an inner component runs change detection', () => { + @Component({ + selector: 'outer-cmp', + template: ` <div *ngIf="exp" @outer></div> <inner-cmp #inner></inner-cmp> `, - animations: [trigger( - 'outer', - [transition(':enter', [style({opacity: 0}), animate('1s', style({opacity: 1}))])])] - }) - class OuterCmp { - @ViewChild('inner') public inner: any; - public exp: any = null; + animations: [trigger( + 'outer', + [transition(':enter', [style({opacity: 0}), animate('1s', style({opacity: 1}))])])] + }) + class OuterCmp { + @ViewChild('inner') public inner: any; + public exp: any = null; - update() { this.exp = 'go'; } + update() { + this.exp = 'go'; + } - ngDoCheck() { - if (this.exp == 'go') { - this.inner.update(); - } + ngDoCheck() { + if (this.exp == 'go') { + this.inner.update(); } } + } - @Component({ - selector: 'inner-cmp', - template: ` + @Component({ + selector: 'inner-cmp', + template: ` <div *ngIf="exp" @inner></div> `, - animations: [trigger('inner', [transition( - ':enter', - [ - style({opacity: 0}), - animate('1s', style({opacity: 1})), - ])])] - }) - class InnerCmp { - public exp: any; - constructor(private _ref: ChangeDetectorRef) {} - update() { - this.exp = 'go'; - this._ref.detectChanges(); - } + animations: [trigger('inner', [transition( + ':enter', + [ + style({opacity: 0}), + animate('1s', style({opacity: 1})), + ])])] + }) + class InnerCmp { + public exp: any; + constructor(private _ref: ChangeDetectorRef) {} + update() { + this.exp = 'go'; + this._ref.detectChanges(); } + } - TestBed.configureTestingModule({declarations: [OuterCmp, InnerCmp]}); + TestBed.configureTestingModule({declarations: [OuterCmp, InnerCmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(OuterCmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - expect(getLog()).toEqual([]); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(OuterCmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + expect(getLog()).toEqual([]); - cmp.update(); - fixture.detectChanges(); + cmp.update(); + fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(2); - }); + const players = getLog(); + expect(players.length).toEqual(2); + }); - describe('transition aliases', () => { - describe(':increment', () => { - it('should detect when a value has incremented', () => { - @Component({ - selector: 'if-cmp', - template: ` + describe('transition aliases', () => { + describe(':increment', () => { + it('should detect when a value has incremented', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - ':increment', - [ - animate(1234, style({background: 'red'})), - ]), - ]), - ] - }) - class Cmp { - exp: number = 0; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + ':increment', + [ + animate(1234, style({background: 'red'})), + ]), + ]), + ] + }) + class Cmp { + exp: number = 0; + } - TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(0); - - cmp.exp++; - fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].duration).toEqual(1234); - resetLog(); - - cmp.exp = 5; - fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].duration).toEqual(1234); - }); + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + let players = getLog(); + expect(players.length).toEqual(0); + + cmp.exp++; + fixture.detectChanges(); + players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].duration).toEqual(1234); + resetLog(); + + cmp.exp = 5; + fixture.detectChanges(); + players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].duration).toEqual(1234); }); + }); - describe(':decrement', () => { - it('should detect when a value has decremented', () => { - @Component({ - selector: 'if-cmp', - template: ` + describe(':decrement', () => { + it('should detect when a value has decremented', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - ':decrement', - [ - animate(1234, style({background: 'red'})), - ]), - ]), - ] - }) - class Cmp { - exp: number = 5; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + ':decrement', + [ + animate(1234, style({background: 'red'})), + ]), + ]), + ] + }) + class Cmp { + exp: number = 5; + } - TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(0); - - cmp.exp--; - fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].duration).toEqual(1234); - resetLog(); - - cmp.exp = 0; - fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].duration).toEqual(1234); - }); + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + let players = getLog(); + expect(players.length).toEqual(0); + + cmp.exp--; + fixture.detectChanges(); + players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].duration).toEqual(1234); + resetLog(); + + cmp.exp = 0; + fixture.detectChanges(); + players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].duration).toEqual(1234); }); }); + }); - it('should animate nodes properly when they have been re-ordered', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should animate nodes properly when they have been re-ordered', () => { + @Component({ + selector: 'if-cmp', + template: ` <div *ngFor="let item of items" [class]="'class-' + item.value"> <div [@myAnimation]="item.count"> {{ item.value }} </div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - state('0', style({opacity: 0})), state('1', style({opacity: 0.4})), - state('2', style({opacity: 0.8})), transition('* => 1, * => 2', [animate(1000)]) - ]), - ] - }) - class Cmp { - items = [ - {value: '1', count: 0}, - {value: '2', count: 0}, - {value: '3', count: 0}, - {value: '4', count: 0}, - {value: '5', count: 0}, + animations: [ + trigger( + 'myAnimation', + [ + state('0', style({opacity: 0})), state('1', style({opacity: 0.4})), + state('2', style({opacity: 0.8})), transition('* => 1, * => 2', [animate(1000)]) + ]), + ] + }) + class Cmp { + items = [ + {value: '1', count: 0}, + {value: '2', count: 0}, + {value: '3', count: 0}, + {value: '4', count: 0}, + {value: '5', count: 0}, + ]; + + reOrder() { + this.items = [ + this.items[4], + this.items[1], + this.items[3], + this.items[0], + this.items[2], ]; - - reOrder() { - this.items = [ - this.items[4], - this.items[1], - this.items[3], - this.items[0], - this.items[2], - ]; - } } + } - TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - const one = cmp.items[0]; - const two = cmp.items[1]; - one.count++; - fixture.detectChanges(); - - cmp.reOrder(); - fixture.detectChanges(); - resetLog(); + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + const one = cmp.items[0]; + const two = cmp.items[1]; + one.count++; + fixture.detectChanges(); + + cmp.reOrder(); + fixture.detectChanges(); + resetLog(); - one.count++; - two.count++; - fixture.detectChanges(); + one.count++; + two.count++; + fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(2); - }); + const players = getLog(); + expect(players.length).toEqual(2); }); + }); - describe('animation listeners', () => { - it('should trigger a `start` state change listener for when the animation changes state from void => state', - fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + describe('animation listeners', () => { + it('should trigger a `start` state change listener for when the animation changes state from void => state', + fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: ` <div *ngIf="exp" [@myAnimation]="exp" (@myAnimation.start)="callback($event)"></div> `, - animations: [trigger( - 'myAnimation', - [transition( - 'void => *', - [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], - }) - class Cmp { - exp: any = false; - // TODO(issue/24571): remove '!'. - event !: AnimationEvent; + animations: [trigger( + 'myAnimation', + [transition( + 'void => *', + [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], + }) + class Cmp { + exp: any = false; + // TODO(issue/24571): remove '!'. + event!: AnimationEvent; - callback = (event: any) => { this.event = event; }; + callback = (event: any) => { + this.event = event; } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'true'; - fixture.detectChanges(); - flushMicrotasks(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'true'; + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.event.triggerName).toEqual('myAnimation'); - expect(cmp.event.phaseName).toEqual('start'); - expect(cmp.event.totalTime).toEqual(500); - expect(cmp.event.fromState).toEqual('void'); - expect(cmp.event.toState).toEqual('true'); - })); + expect(cmp.event.triggerName).toEqual('myAnimation'); + expect(cmp.event.phaseName).toEqual('start'); + expect(cmp.event.totalTime).toEqual(500); + expect(cmp.event.fromState).toEqual('void'); + expect(cmp.event.toState).toEqual('true'); + })); - it('should trigger a `done` state change listener for when the animation changes state from a => b', - fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + it('should trigger a `done` state change listener for when the animation changes state from a => b', + fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: ` <div *ngIf="exp" [@myAnimation123]="exp" (@myAnimation123.done)="callback($event)"></div> `, - animations: [trigger( - 'myAnimation123', - [transition( - '* => b', [style({'opacity': '0'}), animate(999, style({'opacity': '1'}))])])], - }) - class Cmp { - exp: any = false; - // TODO(issue/24571): remove '!'. - event !: AnimationEvent; + animations: [trigger( + 'myAnimation123', + [transition( + '* => b', [style({'opacity': '0'}), animate(999, style({'opacity': '1'}))])])], + }) + class Cmp { + exp: any = false; + // TODO(issue/24571): remove '!'. + event!: AnimationEvent; - callback = (event: any) => { this.event = event; }; + callback = (event: any) => { + this.event = event; } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'b'; + fixture.detectChanges(); + engine.flush(); - expect(cmp.event).toBeFalsy(); + expect(cmp.event).toBeFalsy(); - const player = engine.players.pop() !; - player.finish(); - flushMicrotasks(); + const player = engine.players.pop()!; + player.finish(); + flushMicrotasks(); - expect(cmp.event.triggerName).toEqual('myAnimation123'); - expect(cmp.event.phaseName).toEqual('done'); - expect(cmp.event.totalTime).toEqual(999); - expect(cmp.event.fromState).toEqual('void'); - expect(cmp.event.toState).toEqual('b'); - })); + expect(cmp.event.triggerName).toEqual('myAnimation123'); + expect(cmp.event.phaseName).toEqual('done'); + expect(cmp.event.totalTime).toEqual(999); + expect(cmp.event.fromState).toEqual('void'); + expect(cmp.event.toState).toEqual('b'); + })); - it('should handle callbacks for multiple triggers running simultaneously', fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + it('should handle callbacks for multiple triggers running simultaneously', fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: ` <div [@ani1]="exp1" (@ani1.done)="callback1($event)"></div> <div [@ani2]="exp2" (@ani2.done)="callback2($event)"></div> `, - animations: [ - trigger( - 'ani1', - [ - transition( - '* => a', - [style({'opacity': '0'}), animate(999, style({'opacity': '1'}))]), - ]), - trigger( - 'ani2', - [ - transition( - '* => b', - [style({'width': '0px'}), animate(999, style({'width': '100px'}))]), - ]) - ], - }) - class Cmp { - exp1: any = false; - exp2: any = false; - // TODO(issue/24571): remove '!'. - event1 !: AnimationEvent; - // TODO(issue/24571): remove '!'. - event2 !: AnimationEvent; - // tslint:disable:semicolon - callback1 = (event: any) => { this.event1 = event; }; - // tslint:disable:semicolon - callback2 = (event: any) => { this.event2 = event; }; - } + animations: [ + trigger( + 'ani1', + [ + transition( + '* => a', [style({'opacity': '0'}), animate(999, style({'opacity': '1'}))]), + ]), + trigger( + 'ani2', + [ + transition( + '* => b', + [style({'width': '0px'}), animate(999, style({'width': '100px'}))]), + ]) + ], + }) + class Cmp { + exp1: any = false; + exp2: any = false; + // TODO(issue/24571): remove '!'. + event1!: AnimationEvent; + // TODO(issue/24571): remove '!'. + event2!: AnimationEvent; + // tslint:disable:semicolon + callback1 = (event: any) => { + this.event1 = event; + }; + // tslint:disable:semicolon + callback2 = (event: any) => { + this.event2 = event; + }; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp1 = 'a'; - cmp.exp2 = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp1 = 'a'; + cmp.exp2 = 'b'; + fixture.detectChanges(); + engine.flush(); - expect(cmp.event1).toBeFalsy(); - expect(cmp.event2).toBeFalsy(); + expect(cmp.event1).toBeFalsy(); + expect(cmp.event2).toBeFalsy(); - const player1 = engine.players[0]; - const player2 = engine.players[1]; + const player1 = engine.players[0]; + const player2 = engine.players[1]; - player1.finish(); - player2.finish(); - expect(cmp.event1).toBeFalsy(); - expect(cmp.event2).toBeFalsy(); + player1.finish(); + player2.finish(); + expect(cmp.event1).toBeFalsy(); + expect(cmp.event2).toBeFalsy(); - flushMicrotasks(); - expect(cmp.event1.triggerName).toBeTruthy('ani1'); - expect(cmp.event2.triggerName).toBeTruthy('ani2'); - })); + flushMicrotasks(); + expect(cmp.event1.triggerName).toBeTruthy('ani1'); + expect(cmp.event2.triggerName).toBeTruthy('ani2'); + })); - it('should handle callbacks for multiple triggers running simultaneously on the same element', - fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + it('should handle callbacks for multiple triggers running simultaneously on the same element', + fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: ` <div [@ani1]="exp1" (@ani1.done)="callback1($event)" [@ani2]="exp2" (@ani2.done)="callback2($event)"></div> `, - animations: [ - trigger( - 'ani1', - [ - transition( - '* => a', - [style({'opacity': '0'}), animate(999, style({'opacity': '1'}))]), - ]), - trigger( - 'ani2', - [ - transition( - '* => b', - [style({'width': '0px'}), animate(999, style({'width': '100px'}))]), - ]) - ], - }) - class Cmp { - exp1: any = false; - exp2: any = false; - // TODO(issue/24571): remove '!'. - event1 !: AnimationEvent; - // TODO(issue/24571): remove '!'. - event2 !: AnimationEvent; - callback1 = (event: any) => { this.event1 = event; }; - callback2 = (event: any) => { this.event2 = event; }; - } + animations: [ + trigger( + 'ani1', + [ + transition( + '* => a', [style({'opacity': '0'}), animate(999, style({'opacity': '1'}))]), + ]), + trigger( + 'ani2', + [ + transition( + '* => b', + [style({'width': '0px'}), animate(999, style({'width': '100px'}))]), + ]) + ], + }) + class Cmp { + exp1: any = false; + exp2: any = false; + // TODO(issue/24571): remove '!'. + event1!: AnimationEvent; + // TODO(issue/24571): remove '!'. + event2!: AnimationEvent; + callback1 = (event: any) => { + this.event1 = event; + }; + callback2 = (event: any) => { + this.event2 = event; + }; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp1 = 'a'; - cmp.exp2 = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp1 = 'a'; + cmp.exp2 = 'b'; + fixture.detectChanges(); + engine.flush(); - expect(cmp.event1).toBeFalsy(); - expect(cmp.event2).toBeFalsy(); + expect(cmp.event1).toBeFalsy(); + expect(cmp.event2).toBeFalsy(); - const player1 = engine.players[0]; - const player2 = engine.players[1]; + const player1 = engine.players[0]; + const player2 = engine.players[1]; - player1.finish(); - player2.finish(); - expect(cmp.event1).toBeFalsy(); - expect(cmp.event2).toBeFalsy(); + player1.finish(); + player2.finish(); + expect(cmp.event1).toBeFalsy(); + expect(cmp.event2).toBeFalsy(); - flushMicrotasks(); - expect(cmp.event1.triggerName).toBeTruthy('ani1'); - expect(cmp.event2.triggerName).toBeTruthy('ani2'); - })); + flushMicrotasks(); + expect(cmp.event1.triggerName).toBeTruthy('ani1'); + expect(cmp.event2.triggerName).toBeTruthy('ani2'); + })); - it('should handle a leave animation for multiple triggers even if not all triggers have their own leave transition specified', - fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` + it('should handle a leave animation for multiple triggers even if not all triggers have their own leave transition specified', + fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: ` <div *ngIf="exp" @foo @bar>123</div> `, - animations: [ - trigger( - 'foo', - [ - transition( - ':enter', - [ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - ]), - trigger( - 'bar', - [ - transition( - ':leave', - [ - animate(1000, style({opacity: 0})), - ]), - ]) - ], - }) - class Cmp { - exp: boolean = false; - } + animations: [ + trigger( + 'foo', + [ + transition( + ':enter', + [ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + ]), + trigger( + 'bar', + [ + transition( + ':leave', + [ + animate(1000, style({opacity: 0})), + ]), + ]) + ], + }) + class Cmp { + exp: boolean = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const elm = fixture.elementRef.nativeElement; - const cmp = fixture.componentInstance; + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const elm = fixture.elementRef.nativeElement; + const cmp = fixture.componentInstance; - cmp.exp = true; - fixture.detectChanges(); + cmp.exp = true; + fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(1); - let [p1] = players; - p1.finish(); - flushMicrotasks(); - expect(elm.innerText.trim()).toEqual('123'); + let players = getLog(); + expect(players.length).toEqual(1); + let [p1] = players; + p1.finish(); + flushMicrotasks(); + expect(elm.innerText.trim()).toEqual('123'); - resetLog(); - cmp.exp = false; - fixture.detectChanges(); + resetLog(); + cmp.exp = false; + fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(1); - [p1] = players; - p1.finish(); - flushMicrotasks(); - expect(elm.innerText.trim()).toEqual(''); - })); + players = getLog(); + expect(players.length).toEqual(1); + [p1] = players; + p1.finish(); + flushMicrotasks(); + expect(elm.innerText.trim()).toEqual(''); + })); - it('should trigger a state change listener for when the animation changes state from void => state on the host element', - fakeAsync(() => { - @Component({ - selector: 'my-cmp', - template: `...`, - animations: [trigger( - 'myAnimation2', - [transition( - 'void => *', - [style({'opacity': '0'}), animate(1000, style({'opacity': '1'}))])])], - }) - class Cmp { - // TODO(issue/24571): remove '!'. - event !: AnimationEvent; + it('should trigger a state change listener for when the animation changes state from void => state on the host element', + fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: `...`, + animations: [trigger( + 'myAnimation2', + [transition( + 'void => *', + [style({'opacity': '0'}), animate(1000, style({'opacity': '1'}))])])], + }) + class Cmp { + // TODO(issue/24571): remove '!'. + event!: AnimationEvent; - @HostBinding('@myAnimation2') - exp: any = false; + @HostBinding('@myAnimation2') exp: any = false; - @HostListener('@myAnimation2.start', ['$event']) - callback = (event: any) => { this.event = event; } + @HostListener('@myAnimation2.start', ['$event']) + callback = (event: any) => { + this.event = event; } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'TRUE'; - fixture.detectChanges(); - flushMicrotasks(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'TRUE'; + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.event.triggerName).toEqual('myAnimation2'); - expect(cmp.event.phaseName).toEqual('start'); - expect(cmp.event.totalTime).toEqual(1000); - expect(cmp.event.fromState).toEqual('void'); - expect(cmp.event.toState).toEqual('TRUE'); - })); + expect(cmp.event.triggerName).toEqual('myAnimation2'); + expect(cmp.event.phaseName).toEqual('start'); + expect(cmp.event.totalTime).toEqual(1000); + expect(cmp.event.fromState).toEqual('void'); + expect(cmp.event.toState).toEqual('TRUE'); + })); - it('should always fire callbacks even when a transition is not detected', fakeAsync(() => { - @Component({ - selector: 'my-cmp', - template: ` + it('should always fire callbacks even when a transition is not detected', fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: ` <div [@myAnimation]="exp" (@myAnimation.start)="callback($event)" (@myAnimation.done)="callback($event)"></div> `, - animations: [trigger('myAnimation', [])] - }) - class Cmp { - // TODO(issue/24571): remove '!'. - exp !: string; - log: any[] = []; - callback = (event: any) => this.log.push(`${event.phaseName} => ${event.toState}`); - } + animations: [trigger('myAnimation', [])] + }) + class Cmp { + // TODO(issue/24571): remove '!'. + exp!: string; + log: any[] = []; + callback = (event: any) => this.log.push(`${event.phaseName} => ${event.toState}`); + } - TestBed.configureTestingModule({ - providers: [{provide: AnimationDriver, useClass: NoopAnimationDriver}], - declarations: [Cmp] - }); + TestBed.configureTestingModule({ + providers: [{provide: AnimationDriver, useClass: NoopAnimationDriver}], + declarations: [Cmp] + }); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'a'; - fixture.detectChanges(); - flushMicrotasks(); - expect(cmp.log).toEqual(['start => a', 'done => a']); + cmp.exp = 'a'; + fixture.detectChanges(); + flushMicrotasks(); + expect(cmp.log).toEqual(['start => a', 'done => a']); - cmp.log = []; - cmp.exp = 'b'; - fixture.detectChanges(); - flushMicrotasks(); + cmp.log = []; + cmp.exp = 'b'; + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.log).toEqual(['start => b', 'done => b']); - })); + expect(cmp.log).toEqual(['start => b', 'done => b']); + })); - it('should fire callback events for leave animations even if there is no leave transition', - fakeAsync(() => { - @Component({ - selector: 'my-cmp', - template: ` + it('should fire callback events for leave animations even if there is no leave transition', + fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: ` <div *ngIf="exp" @myAnimation (@myAnimation.start)="callback($event)" (@myAnimation.done)="callback($event)"></div> `, - animations: [trigger('myAnimation', [])] - }) - class Cmp { - exp: boolean = false; - log: any[] = []; - callback = (event: any) => { - const state = event.toState || '_default_'; - this.log.push(`${event.phaseName} => ${state}`); - } + animations: [trigger('myAnimation', [])] + }) + class Cmp { + exp: boolean = false; + log: any[] = []; + callback = (event: any) => { + const state = event.toState || '_default_'; + this.log.push(`${event.phaseName} => ${state}`); } + } - TestBed.configureTestingModule({ - providers: [{provide: AnimationDriver, useClass: NoopAnimationDriver}], - declarations: [Cmp] - }); + TestBed.configureTestingModule({ + providers: [{provide: AnimationDriver, useClass: NoopAnimationDriver}], + declarations: [Cmp] + }); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = true; - fixture.detectChanges(); - flushMicrotasks(); - expect(cmp.log).toEqual(['start => _default_', 'done => _default_']); + cmp.exp = true; + fixture.detectChanges(); + flushMicrotasks(); + expect(cmp.log).toEqual(['start => _default_', 'done => _default_']); - cmp.log = []; + cmp.log = []; - cmp.exp = false; - fixture.detectChanges(); - flushMicrotasks(); + cmp.exp = false; + fixture.detectChanges(); + flushMicrotasks(); - expect(cmp.log).toEqual(['start => void', 'done => void']); - })); + expect(cmp.log).toEqual(['start => void', 'done => void']); + })); - it('should fire callbacks on a sub animation once it starts and finishes', fakeAsync(() => { - @Component({ - selector: 'my-cmp', - template: ` + it('should fire callbacks on a sub animation once it starts and finishes', fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: ` <div class="parent" [@parent]="exp1" (@parent.start)="cb('parent-start',$event)" @@ -2911,82 +2916,83 @@ const DEFAULT_COMPONENT_ID = '1'; (@child.done)="cb('child-done', $event)"></div> </div> `, - animations: [ - trigger( - 'parent', - [ - transition( - '* => go', - [ - style({width: '0px'}), - animate(1000, style({width: '100px'})), - query( - '.child', - [ - animateChild({duration: '1s'}), - ]), - animate(1000, style({width: '0px'})), - ]), - ]), - trigger( - 'child', - [ - transition( - '* => go', - [ - style({height: '0px'}), - animate(1000, style({height: '100px'})), - ]), - ]) - ] - }) - class Cmp { - log: string[] = []; - // TODO(issue/24571): remove '!'. - exp1 !: string; - // TODO(issue/24571): remove '!'. - exp2 !: string; - - cb(name: string, event: AnimationEvent) { this.log.push(name); } + animations: [ + trigger( + 'parent', + [ + transition( + '* => go', + [ + style({width: '0px'}), + animate(1000, style({width: '100px'})), + query( + '.child', + [ + animateChild({duration: '1s'}), + ]), + animate(1000, style({width: '0px'})), + ]), + ]), + trigger( + 'child', + [ + transition( + '* => go', + [ + style({height: '0px'}), + animate(1000, style({height: '100px'})), + ]), + ]) + ] + }) + class Cmp { + log: string[] = []; + // TODO(issue/24571): remove '!'. + exp1!: string; + // TODO(issue/24571): remove '!'. + exp2!: string; + + cb(name: string, event: AnimationEvent) { + this.log.push(name); } + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp1 = 'go'; - cmp.exp2 = 'go'; - fixture.detectChanges(); - engine.flush(); - flushMicrotasks(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp1 = 'go'; + cmp.exp2 = 'go'; + fixture.detectChanges(); + engine.flush(); + flushMicrotasks(); - expect(cmp.log).toEqual(['parent-start', 'child-start']); - cmp.log = []; + expect(cmp.log).toEqual(['parent-start', 'child-start']); + cmp.log = []; - const players = getLog(); - expect(players.length).toEqual(3); - const [p1, p2, p3] = players; + const players = getLog(); + expect(players.length).toEqual(3); + const [p1, p2, p3] = players; - p1.finish(); - flushMicrotasks(); - expect(cmp.log).toEqual([]); + p1.finish(); + flushMicrotasks(); + expect(cmp.log).toEqual([]); - p2.finish(); - flushMicrotasks(); - expect(cmp.log).toEqual([]); + p2.finish(); + flushMicrotasks(); + expect(cmp.log).toEqual([]); - p3.finish(); - flushMicrotasks(); - expect(cmp.log).toEqual(['parent-done', 'child-done']); - })); + p3.finish(); + flushMicrotasks(); + expect(cmp.log).toEqual(['parent-done', 'child-done']); + })); - it('should fire callbacks and collect the correct the totalTime and element details for any queried sub animations', - fakeAsync( - () => { - @Component({ - selector: 'my-cmp', - template: ` + it('should fire callbacks and collect the correct the totalTime and element details for any queried sub animations', + fakeAsync(() => { + @Component({ + selector: 'my-cmp', + template: ` <div class="parent" [@parent]="exp" (@parent.done)="cb('all','done', $event)"> <div *ngFor="let item of items" class="item item-{{ item }}" @@ -2997,563 +3003,441 @@ const DEFAULT_COMPONENT_ID = '1'; </div> </div> `, - animations: [ - trigger('parent', [ - transition('* => go', [ - style({ opacity: 0 }), - animate('1s', style({ opacity: 1 })), - query('.item', [ - style({ opacity: 0 }), - animate(1000, style({ opacity: 1 })) - ]), - query('.item', [ - animateChild({ duration: '1.8s', delay: '300ms' }) - ]) - ]) - ]), - trigger('child', [ - transition(':enter', [ - style({ opacity: 0 }), - animate(1500, style({ opacity: 1 })) - ]) - ]) - ] - }) - class Cmp { - log: string[] = []; - events: {[name: string]: any} = {}; - // TODO(issue/24571): remove '!'. - exp !: string; - items: any = [0, 1, 2, 3]; - - cb(name: string, phase: string, event: AnimationEvent) { - this.log.push(name + '-' + phase); - this.events[name] = event; - } - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); - flushMicrotasks(); - - expect(cmp.log).toEqual(['c-0-start', 'c-1-start', 'c-2-start', 'c-3-start']); - cmp.log = []; - - const players = getLog(); - // 1 + 4 + 4 = 9 players - expect(players.length).toEqual(9); - - const [pA, pq1a, pq1b, pq1c, pq1d, pq2a, pq2b, pq2c, pq2d] = getLog(); - pA.finish(); - pq1a.finish(); - pq1b.finish(); - pq1c.finish(); - pq1d.finish(); - flushMicrotasks(); - - expect(cmp.log).toEqual([]); - pq2a.finish(); - pq2b.finish(); - pq2c.finish(); - pq2d.finish(); - flushMicrotasks(); - - expect(cmp.log).toEqual( - ['all-done', 'c-0-done', 'c-1-done', 'c-2-done', 'c-3-done']); - - expect(cmp.events['c-0'].totalTime).toEqual(4100); // 1000 + 1000 + 1800 + 300 - expect(cmp.events['c-0'].element.innerText.trim()).toEqual('0'); - expect(cmp.events['c-1'].totalTime).toEqual(4100); - expect(cmp.events['c-1'].element.innerText.trim()).toEqual('1'); - expect(cmp.events['c-2'].totalTime).toEqual(4100); - expect(cmp.events['c-2'].element.innerText.trim()).toEqual('2'); - expect(cmp.events['c-3'].totalTime).toEqual(4100); - expect(cmp.events['c-3'].element.innerText.trim()).toEqual('3'); - })); - }); + animations: [ + trigger( + 'parent', + [transition( + '* => go', + [ + style({opacity: 0}), animate('1s', style({opacity: 1})), + query('.item', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), + query('.item', [animateChild({duration: '1.8s', delay: '300ms'})]) + ])]), + trigger( + 'child', + [transition(':enter', [style({opacity: 0}), animate(1500, style({opacity: 1}))])]) + ] + }) + class Cmp { + log: string[] = []; + events: {[name: string]: any} = {}; + // TODO(issue/24571): remove '!'. + exp!: string; + items: any = [0, 1, 2, 3]; + + cb(name: string, phase: string, event: AnimationEvent) { + this.log.push(name + '-' + phase); + this.events[name] = event; + } + } - describe('animation control flags', () => { - describe('[@.disabled]', () => { - it('should disable child animations when set to true', () => { - @Component({ - selector: 'if-cmp', - template: ` + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); + flushMicrotasks(); + + expect(cmp.log).toEqual(['c-0-start', 'c-1-start', 'c-2-start', 'c-3-start']); + cmp.log = []; + + const players = getLog(); + // 1 + 4 + 4 = 9 players + expect(players.length).toEqual(9); + + const [pA, pq1a, pq1b, pq1c, pq1d, pq2a, pq2b, pq2c, pq2d] = getLog(); + pA.finish(); + pq1a.finish(); + pq1b.finish(); + pq1c.finish(); + pq1d.finish(); + flushMicrotasks(); + + expect(cmp.log).toEqual([]); + pq2a.finish(); + pq2b.finish(); + pq2c.finish(); + pq2d.finish(); + flushMicrotasks(); + + expect(cmp.log).toEqual(['all-done', 'c-0-done', 'c-1-done', 'c-2-done', 'c-3-done']); + + expect(cmp.events['c-0'].totalTime).toEqual(4100); // 1000 + 1000 + 1800 + 300 + expect(cmp.events['c-0'].element.innerText.trim()).toEqual('0'); + expect(cmp.events['c-1'].totalTime).toEqual(4100); + expect(cmp.events['c-1'].element.innerText.trim()).toEqual('1'); + expect(cmp.events['c-2'].totalTime).toEqual(4100); + expect(cmp.events['c-2'].element.innerText.trim()).toEqual('2'); + expect(cmp.events['c-3'].totalTime).toEqual(4100); + expect(cmp.events['c-3'].element.innerText.trim()).toEqual('3'); + })); + }); + + describe('animation control flags', () => { + describe('[@.disabled]', () => { + it('should disable child animations when set to true', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@.disabled]="disableExp"> <div [@myAnimation]="exp"></div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => 1, * => 2', - [ - animate(1234, style({width: '100px'})), - ]), - ]), - ] - }) - class Cmp { - exp: any = false; - disableExp = false; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => 1, * => 2', + [ + animate(1234, style({width: '100px'})), + ]), + ]), + ] + }) + class Cmp { + exp: any = false; + disableExp = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - resetLog(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + resetLog(); - cmp.disableExp = true; - cmp.exp = '1'; - fixture.detectChanges(); + cmp.disableExp = true; + cmp.exp = '1'; + fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(0); + let players = getLog(); + expect(players.length).toEqual(0); - cmp.disableExp = false; - cmp.exp = '2'; - fixture.detectChanges(); + cmp.disableExp = false; + cmp.exp = '2'; + fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].totalTime).toEqual(1234); - }); + players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].totalTime).toEqual(1234); + }); - it('should ensure state() values are applied when an animation is disabled', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should ensure state() values are applied when an animation is disabled', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@.disabled]="disableExp"> <div [@myAnimation]="exp" #elm></div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - state('1', style({height: '100px'})), state('2', style({height: '200px'})), - state('3', style({height: '300px'})), transition('* => *', animate(500)) - ]), - ] - }) - class Cmp { - exp: any = false; - disableExp = false; + animations: [ + trigger( + 'myAnimation', + [ + state('1', style({height: '100px'})), state('2', style({height: '200px'})), + state('3', style({height: '300px'})), transition('* => *', animate(500)) + ]), + ] + }) + class Cmp { + exp: any = false; + disableExp = false; - @ViewChild('elm', {static: true}) public element: any; - } + @ViewChild('elm', {static: true}) public element: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const engine = TestBed.inject(ɵAnimationEngine); - function assertHeight(element: any, height: string) { - expect(element.style['height']).toEqual(height); - } + function assertHeight(element: any, height: string) { + expect(element.style['height']).toEqual(height); + } - // In Ivy, change detection needs to run before the ViewQuery for cmp.element will - // resolve. Keeping this test enabled since we still want to test the animation logic. - if (ivyEnabled) fixture.detectChanges(); + // In Ivy, change detection needs to run before the ViewQuery for cmp.element will + // resolve. Keeping this test enabled since we still want to test the animation logic. + if (ivyEnabled) fixture.detectChanges(); - const cmp = fixture.componentInstance; - const element = cmp.element.nativeElement; - fixture.detectChanges(); + const cmp = fixture.componentInstance; + const element = cmp.element.nativeElement; + fixture.detectChanges(); - cmp.disableExp = true; - cmp.exp = '1'; - fixture.detectChanges(); - assertHeight(element, '100px'); + cmp.disableExp = true; + cmp.exp = '1'; + fixture.detectChanges(); + assertHeight(element, '100px'); - cmp.exp = '2'; - fixture.detectChanges(); - assertHeight(element, '200px'); + cmp.exp = '2'; + fixture.detectChanges(); + assertHeight(element, '200px'); - cmp.exp = '3'; - fixture.detectChanges(); - assertHeight(element, '300px'); - }); + cmp.exp = '3'; + fixture.detectChanges(); + assertHeight(element, '300px'); + }); - it('should disable animations for the element that they are disabled on', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should disable animations for the element that they are disabled on', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@.disabled]="disableExp" [@myAnimation]="exp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => 1, * => 2', - [ - animate(1234, style({width: '100px'})), - ]), - ]), - ] - }) - class Cmp { - exp: any = false; - disableExp = false; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => 1, * => 2', + [ + animate(1234, style({width: '100px'})), + ]), + ]), + ] + }) + class Cmp { + exp: any = false; + disableExp = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - resetLog(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + resetLog(); - cmp.disableExp = true; - cmp.exp = '1'; - fixture.detectChanges(); + cmp.disableExp = true; + cmp.exp = '1'; + fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(0); - resetLog(); + let players = getLog(); + expect(players.length).toEqual(0); + resetLog(); - cmp.disableExp = false; - cmp.exp = '2'; - fixture.detectChanges(); + cmp.disableExp = false; + cmp.exp = '2'; + fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].totalTime).toEqual(1234); - }); + players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].totalTime).toEqual(1234); + }); - it('should respect inner disabled nodes once a parent becomes enabled', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should respect inner disabled nodes once a parent becomes enabled', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@.disabled]="disableParentExp"> <div [@.disabled]="disableChildExp"> <div [@myAnimation]="exp"></div> </div> </div> `, - animations: [trigger( - 'myAnimation', - [transition('* => 1, * => 2, * => 3', [animate(1234, style({width: '100px'}))])])] - }) - class Cmp { - disableParentExp = false; - disableChildExp = false; - exp = ''; - } + animations: [trigger( + 'myAnimation', + [transition('* => 1, * => 2, * => 3', [animate(1234, style({width: '100px'}))])])] + }) + class Cmp { + disableParentExp = false; + disableChildExp = false; + exp = ''; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - resetLog(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + resetLog(); - cmp.disableParentExp = true; - cmp.disableChildExp = true; - cmp.exp = '1'; - fixture.detectChanges(); + cmp.disableParentExp = true; + cmp.disableChildExp = true; + cmp.exp = '1'; + fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(0); + let players = getLog(); + expect(players.length).toEqual(0); - cmp.disableParentExp = false; - cmp.exp = '2'; - fixture.detectChanges(); + cmp.disableParentExp = false; + cmp.exp = '2'; + fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(0); + players = getLog(); + expect(players.length).toEqual(0); - cmp.disableChildExp = false; - cmp.exp = '3'; - fixture.detectChanges(); + cmp.disableChildExp = false; + cmp.exp = '3'; + fixture.detectChanges(); - players = getLog(); - expect(players.length).toEqual(1); - }); + players = getLog(); + expect(players.length).toEqual(1); + }); - it('should properly handle dom operations when disabled', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should properly handle dom operations when disabled', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@.disabled]="disableExp" #parent> <div *ngIf="exp" @myAnimation></div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - ':enter', - [ - style({opacity: 0}), - animate(1234, style({opacity: 1})), - ]), - transition( - ':leave', - [ - animate(1234, style({opacity: 0})), - ]), - ]), - ] - }) - class Cmp { - @ViewChild('parent') public parentElm: any; - disableExp = false; - exp = false; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + ':enter', + [ + style({opacity: 0}), + animate(1234, style({opacity: 1})), + ]), + transition( + ':leave', + [ + animate(1234, style({opacity: 0})), + ]), + ]), + ] + }) + class Cmp { + @ViewChild('parent') public parentElm: any; + disableExp = false; + exp = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.disableExp = true; - fixture.detectChanges(); - resetLog(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.disableExp = true; + fixture.detectChanges(); + resetLog(); - const parent = cmp.parentElm !.nativeElement; + const parent = cmp.parentElm!.nativeElement; - cmp.exp = true; - fixture.detectChanges(); - expect(getLog().length).toEqual(0); - expect(parent.childElementCount).toEqual(1); + cmp.exp = true; + fixture.detectChanges(); + expect(getLog().length).toEqual(0); + expect(parent.childElementCount).toEqual(1); - cmp.exp = false; - fixture.detectChanges(); - expect(getLog().length).toEqual(0); - expect(parent.childElementCount).toEqual(0); - }); + cmp.exp = false; + fixture.detectChanges(); + expect(getLog().length).toEqual(0); + expect(parent.childElementCount).toEqual(0); + }); + + it('should properly resolve animation event listeners when disabled', fakeAsync(() => { + @Component({ + selector: 'if-cmp', + template: ` + <div [@.disabled]="disableExp"> + <div [@myAnimation]="exp" (@myAnimation.start)="startEvent=$event" (@myAnimation.done)="doneEvent=$event"></div> + </div> + `, + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => 1, * => 2', + [style({opacity: 0}), animate(9876, style({opacity: 1}))]), + ]), + ] + }) + class Cmp { + disableExp = false; + exp = ''; + // TODO(issue/24571): remove '!'. + startEvent!: AnimationEvent; + // TODO(issue/24571): remove '!'. + doneEvent!: AnimationEvent; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.disableExp = true; + fixture.detectChanges(); + resetLog(); + expect(cmp.startEvent).toBeFalsy(); + expect(cmp.doneEvent).toBeFalsy(); + + cmp.exp = '1'; + fixture.detectChanges(); + flushMicrotasks(); + expect(cmp.startEvent.totalTime).toEqual(9876); + expect(cmp.startEvent.disabled).toBeTruthy(); + expect(cmp.doneEvent.totalTime).toEqual(9876); + expect(cmp.doneEvent.disabled).toBeTruthy(); - it('should properly resolve animation event listeners when disabled', fakeAsync(() => { - @Component({ - selector: 'if-cmp', - template: ` - <div [@.disabled]="disableExp"> - <div [@myAnimation]="exp" (@myAnimation.start)="startEvent=$event" (@myAnimation.done)="doneEvent=$event"></div> - </div> - `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => 1, * => 2', - [style({opacity: 0}), animate(9876, style({opacity: 1}))]), - ]), - ] - }) - class Cmp { - disableExp = false; - exp = ''; - // TODO(issue/24571): remove '!'. - startEvent !: AnimationEvent; - // TODO(issue/24571): remove '!'. - doneEvent !: AnimationEvent; - } + cmp.exp = '2'; + cmp.disableExp = false; + fixture.detectChanges(); + flushMicrotasks(); + expect(cmp.startEvent.totalTime).toEqual(9876); + expect(cmp.startEvent.disabled).toBeFalsy(); + // the done event isn't fired because it's an actual animation + })); - TestBed.configureTestingModule({declarations: [Cmp]}); - - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.disableExp = true; - fixture.detectChanges(); - resetLog(); - expect(cmp.startEvent).toBeFalsy(); - expect(cmp.doneEvent).toBeFalsy(); - - cmp.exp = '1'; - fixture.detectChanges(); - flushMicrotasks(); - expect(cmp.startEvent.totalTime).toEqual(9876); - expect(cmp.startEvent.disabled).toBeTruthy(); - expect(cmp.doneEvent.totalTime).toEqual(9876); - expect(cmp.doneEvent.disabled).toBeTruthy(); - - cmp.exp = '2'; - cmp.disableExp = false; - fixture.detectChanges(); - flushMicrotasks(); - expect(cmp.startEvent.totalTime).toEqual(9876); - expect(cmp.startEvent.disabled).toBeFalsy(); - // the done event isn't fired because it's an actual animation - })); - - it('should work when there are no animations on the component handling the disable/enable flag', - () => { - @Component({ - selector: 'parent-cmp', - template: ` + it('should work when there are no animations on the component handling the disable/enable flag', + () => { + @Component({ + selector: 'parent-cmp', + template: ` <div [@.disabled]="disableExp"> <child-cmp #child></child-cmp> </div> ` - }) - class ParentCmp { - @ViewChild('child') public child: ChildCmp|null = null; - disableExp = false; - } + }) + class ParentCmp { + @ViewChild('child') public child: ChildCmp|null = null; + disableExp = false; + } - @Component({ - selector: 'child-cmp', - template: ` + @Component({ + selector: 'child-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [trigger( - 'myAnimation', - [transition( - '* => go, * => goAgain', - [style({opacity: 0}), animate('1s', style({opacity: 1}))])])] - }) - class ChildCmp { - public exp = ''; - } - - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - cmp.disableExp = true; - fixture.detectChanges(); - resetLog(); - - const child = cmp.child !; - child.exp = 'go'; - fixture.detectChanges(); - - expect(getLog().length).toEqual(0); - resetLog(); - - cmp.disableExp = false; - child.exp = 'goAgain'; - fixture.detectChanges(); - expect(getLog().length).toEqual(1); - }); - - it('should treat the property as true when the expression is missing', () => { - @Component({ - selector: 'parent-cmp', - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - style({opacity: 0}), - animate(500, style({opacity: 1})), - ]), - ]), - ], - template: ` - <div @.disabled> - <div [@myAnimation]="exp"></div> - </div> - ` - }) - class Cmp { - exp = ''; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - resetLog(); + animations: [trigger( + 'myAnimation', [transition( + '* => go, * => goAgain', + [style({opacity: 0}), animate('1s', style({opacity: 1}))])])] + }) + class ChildCmp { + public exp = ''; + } - cmp.exp = 'go'; - fixture.detectChanges(); - expect(getLog().length).toEqual(0); - }); + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - it('should respect parent/sub animations when the respective area in the DOM is disabled', - fakeAsync(() => { - @Component({ - selector: 'parent-cmp', - animations: [ - trigger( - 'parent', - [ - transition( - '* => empty', - [ - style({opacity: 0}), - query( - '@child', - [ - animateChild(), - ]), - animate('1s', style({opacity: 1})), - ]), - ]), - trigger( - 'child', - [ - transition( - ':leave', - [ - animate('1s', style({opacity: 0})), - ]), - ]), - ], - template: ` - <div [@.disabled]="disableExp" #container> - <div [@parent]="exp" (@parent.done)="onDone($event)"> - <div class="item" *ngFor="let item of items" @child (@child.done)="onDone($event)"></div> - </div> - </div> - ` - }) - class Cmp { - @ViewChild('container') public container: any; + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + cmp.disableExp = true; + fixture.detectChanges(); + resetLog(); - disableExp = false; - exp = ''; - items: any[] = []; - doneLog: any[] = []; + const child = cmp.child!; + child.exp = 'go'; + fixture.detectChanges(); - onDone(event: any) { this.doneLog.push(event); } - } + expect(getLog().length).toEqual(0); + resetLog(); - TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.disableExp = true; - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - flushMicrotasks(); - - cmp.exp = 'empty'; - cmp.items = []; - cmp.doneLog = []; - fixture.detectChanges(); - flushMicrotasks(); - - const elms = cmp.container.nativeElement.querySelectorAll('.item'); - expect(elms.length).toEqual(0); - - expect(cmp.doneLog.length).toEqual(6); - })); - }); - }); + cmp.disableExp = false; + child.exp = 'goAgain'; + fixture.detectChanges(); + expect(getLog().length).toEqual(1); + }); - describe('animation normalization', () => { - it('should convert hyphenated properties to camelcase by default', () => { + it('should treat the property as true when the expression is missing', () => { @Component({ - selector: 'cmp', - template: ` - <div [@myAnimation]="exp"></div> - `, + selector: 'parent-cmp', animations: [ trigger( 'myAnimation', @@ -3561,194 +3445,311 @@ const DEFAULT_COMPONENT_ID = '1'; transition( '* => go', [ - style({'background-color': 'red', height: '100px', fontSize: '100px'}), - animate( - '1s', - style( - {'background-color': 'blue', height: '200px', fontSize: '200px'})), + style({opacity: 0}), + animate(500, style({opacity: 1})), ]), ]), - ] + ], + template: ` + <div @.disabled> + <div [@myAnimation]="exp"></div> + </div> + ` }) class Cmp { - exp: any = false; + exp = ''; } TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); const cmp = fixture.componentInstance; - cmp.exp = 'go'; fixture.detectChanges(); + resetLog(); - const players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].keyframes).toEqual([ - {backgroundColor: 'red', height: '100px', fontSize: '100px', offset: 0}, - {backgroundColor: 'blue', height: '200px', fontSize: '200px', offset: 1}, - ]); + cmp.exp = 'go'; + fixture.detectChanges(); + expect(getLog().length).toEqual(0); }); - it('should convert hyphenated properties to camelcase by default that are auto/pre style properties', - () => { + it('should respect parent/sub animations when the respective area in the DOM is disabled', + fakeAsync(() => { @Component({ - selector: 'cmp', - template: ` - <div [@myAnimation]="exp"></div> - `, + selector: 'parent-cmp', animations: [ trigger( - 'myAnimation', + 'parent', [ transition( - '* => go', + '* => empty', [ - style({'background-color': AUTO_STYLE, 'font-size': '100px'}), - animate( - '1s', style({'background-color': 'blue', 'font-size': PRE_STYLE})), + style({opacity: 0}), + query( + '@child', + [ + animateChild(), + ]), + animate('1s', style({opacity: 1})), ]), ]), - ] + trigger( + 'child', + [ + transition( + ':leave', + [ + animate('1s', style({opacity: 0})), + ]), + ]), + ], + template: ` + <div [@.disabled]="disableExp" #container> + <div [@parent]="exp" (@parent.done)="onDone($event)"> + <div class="item" *ngFor="let item of items" @child (@child.done)="onDone($event)"></div> + </div> + </div> + ` }) class Cmp { - exp: any = false; + @ViewChild('container') public container: any; + + disableExp = false; + exp = ''; + items: any[] = []; + doneLog: any[] = []; + + onDone(event: any) { + this.doneLog.push(event); + } } TestBed.configureTestingModule({declarations: [Cmp]}); + const engine = TestBed.inject(ɵAnimationEngine); const fixture = TestBed.createComponent(Cmp); const cmp = fixture.componentInstance; - cmp.exp = 'go'; + cmp.disableExp = true; + cmp.items = [0, 1, 2, 3, 4]; fixture.detectChanges(); + flushMicrotasks(); - const players = getLog(); - expect(players.length).toEqual(1); - expect(players[0].keyframes).toEqual([ - {backgroundColor: AUTO_STYLE, fontSize: '100px', offset: 0}, - {backgroundColor: 'blue', fontSize: PRE_STYLE, offset: 1}, - ]); - }); + cmp.exp = 'empty'; + cmp.items = []; + cmp.doneLog = []; + fixture.detectChanges(); + flushMicrotasks(); + + const elms = cmp.container.nativeElement.querySelectorAll('.item'); + expect(elms.length).toEqual(0); + + expect(cmp.doneLog.length).toEqual(6); + })); }); + }); - it('should throw neither state() or transition() are used inside of trigger()', () => { + describe('animation normalization', () => { + it('should convert hyphenated properties to camelcase by default', () => { @Component({ - selector: 'if-cmp', + selector: 'cmp', template: ` - <div [@myAnimation]="exp"></div> - `, - animations: [trigger('myAnimation', [animate(1000, style({width: '100px'}))])] + <div [@myAnimation]="exp"></div> + `, + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + style({'background-color': 'red', height: '100px', fontSize: '100px'}), + animate( + '1s', + style({'background-color': 'blue', height: '200px', fontSize: '200px'})), + ]), + ]), + ] }) class Cmp { exp: any = false; } TestBed.configureTestingModule({declarations: [Cmp]}); - - expect(() => { TestBed.createComponent(Cmp); }) - .toThrowError( - /only state\(\) and transition\(\) definitions can sit inside of a trigger\(\)/); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'go'; + fixture.detectChanges(); + + const players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].keyframes).toEqual([ + {backgroundColor: 'red', height: '100px', fontSize: '100px', offset: 0}, + {backgroundColor: 'blue', height: '200px', fontSize: '200px', offset: 1}, + ]); }); - it('should combine multiple errors together into one exception when an animation fails to be built', + it('should convert hyphenated properties to camelcase by default that are auto/pre style properties', () => { @Component({ - selector: 'if-cmp', + selector: 'cmp', template: ` - <div [@foo]="fooExp" [@bar]="barExp"></div> - `, + <div [@myAnimation]="exp"></div> + `, animations: [ trigger( - 'foo', - [ - transition(':enter', []), - transition( - '* => *', - [ - query('foo', animate(1000, style({background: 'red'}))), - ]), - ]), - trigger( - 'bar', + 'myAnimation', [ - transition(':enter', []), transition( - '* => *', + '* => go', [ - query('bar', animate(1000, style({background: 'blue'}))), + style({'background-color': AUTO_STYLE, 'font-size': '100px'}), + animate('1s', style({'background-color': 'blue', 'font-size': PRE_STYLE})), ]), ]), ] }) class Cmp { - fooExp: any = false; - barExp: any = false; + exp: any = false; } TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); const fixture = TestBed.createComponent(Cmp); const cmp = fixture.componentInstance; + cmp.exp = 'go'; fixture.detectChanges(); - cmp.fooExp = 'go'; - cmp.barExp = 'go'; + const players = getLog(); + expect(players.length).toEqual(1); + expect(players[0].keyframes).toEqual([ + {backgroundColor: AUTO_STYLE, fontSize: '100px', offset: 0}, + {backgroundColor: 'blue', fontSize: PRE_STYLE, offset: 1}, + ]); + }); + }); + + it('should throw neither state() or transition() are used inside of trigger()', () => { + @Component({ + selector: 'if-cmp', + template: ` + <div [@myAnimation]="exp"></div> + `, + animations: [trigger('myAnimation', [animate(1000, style({width: '100px'}))])] + }) + class Cmp { + exp: any = false; + } - let errorMsg: string = ''; - try { - fixture.detectChanges(); - } catch (e) { - errorMsg = e.message; - } + TestBed.configureTestingModule({declarations: [Cmp]}); - expect(errorMsg).toMatch(/@foo has failed due to:/); - expect(errorMsg).toMatch(/`query\("foo"\)` returned zero elements/); - expect(errorMsg).toMatch(/@bar has failed due to:/); - expect(errorMsg).toMatch(/`query\("bar"\)` returned zero elements/); - }); + expect(() => { + TestBed.createComponent(Cmp); + }) + .toThrowError( + /only state\(\) and transition\(\) definitions can sit inside of a trigger\(\)/); + }); - it('should not throw an error if styles overlap in separate transitions', () => { - @Component({ - selector: 'if-cmp', - template: ` + it('should combine multiple errors together into one exception when an animation fails to be built', + () => { + @Component({ + selector: 'if-cmp', + template: ` + <div [@foo]="fooExp" [@bar]="barExp"></div> + `, + animations: [ + trigger( + 'foo', + [ + transition(':enter', []), + transition( + '* => *', + [ + query('foo', animate(1000, style({background: 'red'}))), + ]), + ]), + trigger( + 'bar', + [ + transition(':enter', []), + transition( + '* => *', + [ + query('bar', animate(1000, style({background: 'blue'}))), + ]), + ]), + ] + }) + class Cmp { + fooExp: any = false; + barExp: any = false; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + + cmp.fooExp = 'go'; + cmp.barExp = 'go'; + + let errorMsg: string = ''; + try { + fixture.detectChanges(); + } catch (e) { + errorMsg = e.message; + } + + expect(errorMsg).toMatch(/@foo has failed due to:/); + expect(errorMsg).toMatch(/`query\("foo"\)` returned zero elements/); + expect(errorMsg).toMatch(/@bar has failed due to:/); + expect(errorMsg).toMatch(/`query\("bar"\)` returned zero elements/); + }); + + it('should not throw an error if styles overlap in separate transitions', () => { + @Component({ + selector: 'if-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - 'void => *', - [ - style({opacity: 0}), - animate('0.5s 1s', style({opacity: 1})), - ]), - transition( - '* => void', - [animate(1000, style({height: 0})), animate(1000, style({opacity: 0}))]), - ]), - ] - }) - class Cmp { - exp: any = false; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + 'void => *', + [ + style({opacity: 0}), + animate('0.5s 1s', style({opacity: 1})), + ]), + transition( + '* => void', + [animate(1000, style({height: 0})), animate(1000, style({opacity: 0}))]), + ]), + ] + }) + class Cmp { + exp: any = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - expect(() => { TestBed.createComponent(Cmp); }).not.toThrowError(); - }); + expect(() => { + TestBed.createComponent(Cmp); + }).not.toThrowError(); + }); - modifiedInIvy('FW-952 - Error recovery is handled differently in Ivy than VE') - .it('should continue to clean up DOM-related animation artifacts even if a compiler-level error is thrown midway', - () => { - @Component({ - selector: 'if-cmp', - animations: [ - trigger( - 'foo', - [ - transition('* => something', []), - ]), - ], - template: ` + modifiedInIvy('FW-952 - Error recovery is handled differently in Ivy than VE') + .it('should continue to clean up DOM-related animation artifacts even if a compiler-level error is thrown midway', + () => { + @Component({ + selector: 'if-cmp', + animations: [ + trigger( + 'foo', + [ + transition('* => something', []), + ]), + ], + template: ` value = {{ foo[bar] }} <div #contents> <div *ngIf="exp">1</div> @@ -3756,67 +3757,66 @@ const DEFAULT_COMPONENT_ID = '1'; <div *ngIf="exp" [@foo]="'123'">3</div> </div> `, - }) - class Cmp { - exp: any = false; + }) + class Cmp { + exp: any = false; - @ViewChild('contents', {static: true}) public contents: any; - } + @ViewChild('contents', {static: true}) public contents: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); - const runCD = () => fixture.detectChanges(); - const cmp = fixture.componentInstance; + const runCD = () => fixture.detectChanges(); + const cmp = fixture.componentInstance; - cmp.exp = true; - expect(runCD).toThrow(); + cmp.exp = true; + expect(runCD).toThrow(); - const contents = cmp.contents.nativeElement; - expect(contents.innerText.replace(/\s+/gm, '')).toEqual('123'); + const contents = cmp.contents.nativeElement; + expect(contents.innerText.replace(/\s+/gm, '')).toEqual('123'); - cmp.exp = false; - expect(runCD).toThrow(); + cmp.exp = false; + expect(runCD).toThrow(); - expect(contents.innerText.trim()).toEqual(''); - }); + expect(contents.innerText.trim()).toEqual(''); + }); - describe('errors for not using the animation module', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [{provide: RendererFactory2, useExisting: ɵDomRendererFactory2}], - }); + describe('errors for not using the animation module', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{provide: RendererFactory2, useExisting: ɵDomRendererFactory2}], }); + }); - it('should throw when using an @prop binding without the animation module', () => { - @Component({template: `<div [@myAnimation]="true"></div>`}) - class Cmp { - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - const comp = TestBed.createComponent(Cmp); - expect(() => comp.detectChanges()) - .toThrowError( - 'Found the synthetic property @myAnimation. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.'); - }); + it('should throw when using an @prop binding without the animation module', () => { + @Component({template: `<div [@myAnimation]="true"></div>`}) + class Cmp { + } - it('should throw when using an @prop listener without the animation module', () => { - @Component({template: `<div (@myAnimation.start)="a = true"></div>`}) - class Cmp { - a: any; - } + TestBed.configureTestingModule({declarations: [Cmp]}); + const comp = TestBed.createComponent(Cmp); + expect(() => comp.detectChanges()) + .toThrowError( + 'Found the synthetic property @myAnimation. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.'); + }); - TestBed.configureTestingModule({declarations: [Cmp]}); + it('should throw when using an @prop listener without the animation module', () => { + @Component({template: `<div (@myAnimation.start)="a = true"></div>`}) + class Cmp { + a: any; + } - expect(() => TestBed.createComponent(Cmp)) - .toThrowError( - 'Found the synthetic listener @myAnimation.start. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.'); + TestBed.configureTestingModule({declarations: [Cmp]}); - }); + expect(() => TestBed.createComponent(Cmp)) + .toThrowError( + 'Found the synthetic listener @myAnimation.start. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.'); }); }); +}); })(); function assertHasParent(element: any, yes: boolean) { diff --git a/packages/core/test/animation/animation_query_integration_spec.ts b/packages/core/test/animation/animation_query_integration_spec.ts index 37be2f2718994..ff17a0f9aa422 100644 --- a/packages/core/test/animation/animation_query_integration_spec.ts +++ b/packages/core/test/animation/animation_query_integration_spec.ts @@ -5,7 +5,7 @@ * 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 {AUTO_STYLE, AnimationPlayer, animate, animateChild, group, query, sequence, stagger, state, style, transition, trigger, ɵAnimationGroupPlayer as AnimationGroupPlayer} from '@angular/animations'; +import {animate, animateChild, AnimationPlayer, AUTO_STYLE, group, query, sequence, stagger, state, style, transition, trigger, ɵAnimationGroupPlayer as AnimationGroupPlayer} from '@angular/animations'; import {AnimationDriver, ɵAnimationEngine} from '@angular/animations/browser'; import {matchesElement} from '@angular/animations/browser/src/render/shared'; import {TransitionAnimationPlayer} from '@angular/animations/browser/src/render/transition_animation_engine'; @@ -13,35 +13,37 @@ import {ENTER_CLASSNAME, LEAVE_CLASSNAME} from '@angular/animations/browser/src/ import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing'; import {CommonModule} from '@angular/common'; import {Component, HostBinding, ViewChild} from '@angular/core'; -import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing'; +import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {HostListener} from '../../src/metadata/directives'; (function() { - // these tests are only mean't to be run within the DOM (for now) - if (isNode) return; - - describe('animation query tests', function() { - function getLog(): MockAnimationPlayer[] { - return MockAnimationDriver.log as MockAnimationPlayer[]; - } - - function resetLog() { MockAnimationDriver.log = []; } - - beforeEach(() => { - resetLog(); - TestBed.configureTestingModule({ - providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}], - imports: [BrowserAnimationsModule, CommonModule] - }); +// these tests are only mean't to be run within the DOM (for now) +if (isNode) return; + +describe('animation query tests', function() { + function getLog(): MockAnimationPlayer[] { + return MockAnimationDriver.log as MockAnimationPlayer[]; + } + + function resetLog() { + MockAnimationDriver.log = []; + } + + beforeEach(() => { + resetLog(); + TestBed.configureTestingModule({ + providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}], + imports: [BrowserAnimationsModule, CommonModule] }); + }); - describe('query()', () => { - it('should be able to query all elements that contain animation triggers via @*', () => { - @Component({ - selector: 'ani-cmp', - template: ` + describe('query()', () => { + it('should be able to query all elements that contain animation triggers via @*', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@parent]="exp0"> <div class="a" [@a]="exp1"></div> <div class="b" [@b]="exp2"></div> @@ -50,75 +52,71 @@ import {HostListener} from '../../src/metadata/directives'; </section> </div> `, - animations: [ - trigger( - 'parent', - [ - transition( - '* => go', - [ - query( - '@*', - [ - style({ backgroundColor: 'blue' }), - animate(1000, style({backgroundColor: 'red'})), - ]), - ]), - ]), - trigger( - 'a', - [ - transition('* => 1', [ - animate(1000, style({ opacity: 0 })) - ]), - ]), - trigger( - 'b', - [ - transition('* => 1', [ - animate(1000, style({ opacity: 0 })), - query('.b-inner', [ - animate(1000, style({ opacity: 0 })) + animations: [ + trigger( + 'parent', + [ + transition( + '* => go', + [ + query( + '@*', + [ + style({backgroundColor: 'blue'}), + animate(1000, style({backgroundColor: 'red'})), + ]), ]), - ]), - ]), - trigger( - 'c', - [ - transition('* => 1', [ - animate(1000, style({ opacity: 0 })) - ]), - ]), - ] - }) - class Cmp { - public exp0: any; - public exp1: any; - public exp2: any; - } + ]), + trigger( + 'a', + [ + transition('* => 1', [animate(1000, style({opacity: 0}))]), + ]), + trigger( + 'b', + [ + transition( + '* => 1', + [ + animate(1000, style({opacity: 0})), + query('.b-inner', [animate(1000, style({opacity: 0}))]), + ]), + ]), + trigger( + 'c', + [ + transition('* => 1', [animate(1000, style({opacity: 0}))]), + ]), + ] + }) + class Cmp { + public exp0: any; + public exp1: any; + public exp2: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp0 = 'go'; - fixture.detectChanges(); + cmp.exp0 = 'go'; + fixture.detectChanges(); - let players = getLog(); - expect(players.length).toEqual(3); // a,b,c - resetLog(); + let players = getLog(); + expect(players.length).toEqual(3); // a,b,c + resetLog(); - const [p1, p2, p3] = players; - expect(p1.element.classList.contains('a')).toBeTruthy(); - expect(p2.element.classList.contains('b')).toBeTruthy(); - expect(p3.element.classList.contains('c')).toBeTruthy(); - }); + const [p1, p2, p3] = players; + expect(p1.element.classList.contains('a')).toBeTruthy(); + expect(p2.element.classList.contains('b')).toBeTruthy(); + expect(p3.element.classList.contains('c')).toBeTruthy(); + }); - it('should be able to query currently animating elements via :animating', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should be able to query currently animating elements via :animating', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@parent]="exp0"> <div class="a" [@a]="exp1"></div> <div class="b" [@b]="exp2"> @@ -127,95 +125,91 @@ import {HostListener} from '../../src/metadata/directives'; <div class="c" [@c]="exp3"></div> </div> `, - animations: [ - trigger( - 'parent', - [ - transition( - '* => go', - [ - query( - ':animating', - [ - style({ backgroundColor: 'blue' }), - animate(1000, style({backgroundColor: 'red'})), - ]), - ]), - ]), - trigger( - 'a', - [ - transition('* => 1', [ - animate(1000, style({ opacity: 0 })) - ]), - ]), - trigger( - 'b', - [ - transition('* => 1', [ - animate(1000, style({ opacity: 0 })), - query('.b-inner', [ - animate(1000, style({ opacity: 0 })) + animations: [ + trigger( + 'parent', + [ + transition( + '* => go', + [ + query( + ':animating', + [ + style({backgroundColor: 'blue'}), + animate(1000, style({backgroundColor: 'red'})), + ]), ]), - ]), - ]), - trigger( - 'c', - [ - transition('* => 1', [ - animate(1000, style({ opacity: 0 })) - ]), - ]), - ] - }) - class Cmp { - public exp0: any; - public exp1: any; - public exp2: any; - public exp3: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp0 = ''; - cmp.exp1 = 1; - cmp.exp2 = 1; - // note that exp3 is skipped here - fixture.detectChanges(); - - let players = getLog(); - expect(players.length).toEqual(3); // a,b,b-inner and not c - resetLog(); + ]), + trigger( + 'a', + [ + transition('* => 1', [animate(1000, style({opacity: 0}))]), + ]), + trigger( + 'b', + [ + transition( + '* => 1', + [ + animate(1000, style({opacity: 0})), + query('.b-inner', [animate(1000, style({opacity: 0}))]), + ]), + ]), + trigger( + 'c', + [ + transition('* => 1', [animate(1000, style({opacity: 0}))]), + ]), + ] + }) + class Cmp { + public exp0: any; + public exp1: any; + public exp2: any; + public exp3: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp0 = ''; + cmp.exp1 = 1; + cmp.exp2 = 1; + // note that exp3 is skipped here + fixture.detectChanges(); + + let players = getLog(); + expect(players.length).toEqual(3); // a,b,b-inner and not c + resetLog(); - cmp.exp0 = 'go'; - fixture.detectChanges(); + cmp.exp0 = 'go'; + fixture.detectChanges(); - const expectedKeyframes = [ - {backgroundColor: 'blue', offset: 0}, - {backgroundColor: 'red', offset: 1}, - ]; + const expectedKeyframes = [ + {backgroundColor: 'blue', offset: 0}, + {backgroundColor: 'red', offset: 1}, + ]; - players = getLog(); - expect(players.length).toEqual(3); - const [p1, p2, p3] = players; + players = getLog(); + expect(players.length).toEqual(3); + const [p1, p2, p3] = players; - expect(p1.element.classList.contains('a')).toBeTruthy(); - expect(p1.keyframes).toEqual(expectedKeyframes); + expect(p1.element.classList.contains('a')).toBeTruthy(); + expect(p1.keyframes).toEqual(expectedKeyframes); - expect(p2.element.classList.contains('b')).toBeTruthy(); - expect(p2.keyframes).toEqual(expectedKeyframes); + expect(p2.element.classList.contains('b')).toBeTruthy(); + expect(p2.keyframes).toEqual(expectedKeyframes); - expect(p3.element.classList.contains('b-inner')).toBeTruthy(); - expect(p3.keyframes).toEqual(expectedKeyframes); - }); + expect(p3.element.classList.contains('b-inner')).toBeTruthy(); + expect(p3.keyframes).toEqual(expectedKeyframes); + }); - it('should be able to query triggers directly by name', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should be able to query triggers directly by name', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp0"> <div class="f1" @foo></div> <div class="f2" [@foo]></div> @@ -225,147 +219,146 @@ import {HostListener} from '../../src/metadata/directives'; <div class="b3" [@bar]="exp2"></div> </div> `, - animations: [ - trigger('foo', []), - trigger('bar', []), - trigger( - 'myAnimation', - [ - transition( - '* => foo', - [ - query( - '@foo', - [ - animate(1000, style({color: 'red'})), - ]), - ]), - transition( - '* => bar', - [ - query( - '@bar', - [ - animate(1000, style({color: 'blue'})), - ]), - ]) - ]), - ] - }) - class Cmp { - public exp0: any; - public exp1: any; - public exp2: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - fixture.detectChanges(); - engine.flush(); - resetLog(); + animations: [ + trigger('foo', []), + trigger('bar', []), + trigger( + 'myAnimation', + [ + transition( + '* => foo', + [ + query( + '@foo', + [ + animate(1000, style({color: 'red'})), + ]), + ]), + transition( + '* => bar', + [ + query( + '@bar', + [ + animate(1000, style({color: 'blue'})), + ]), + ]) + ]), + ] + }) + class Cmp { + public exp0: any; + public exp1: any; + public exp2: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp0 = 'foo'; - fixture.detectChanges(); - engine.flush(); + cmp.exp0 = 'foo'; + fixture.detectChanges(); + engine.flush(); - let players = getLog(); - expect(players.length).toEqual(3); - const [p1, p2, p3] = players; - resetLog(); + let players = getLog(); + expect(players.length).toEqual(3); + const [p1, p2, p3] = players; + resetLog(); - expect(p1.element.classList.contains('f1')).toBeTruthy(); - expect(p2.element.classList.contains('f2')).toBeTruthy(); - expect(p3.element.classList.contains('f3')).toBeTruthy(); + expect(p1.element.classList.contains('f1')).toBeTruthy(); + expect(p2.element.classList.contains('f2')).toBeTruthy(); + expect(p3.element.classList.contains('f3')).toBeTruthy(); - cmp.exp0 = 'bar'; - fixture.detectChanges(); - engine.flush(); + cmp.exp0 = 'bar'; + fixture.detectChanges(); + engine.flush(); - players = getLog(); - expect(players.length).toEqual(3); - const [p4, p5, p6] = players; - resetLog(); + players = getLog(); + expect(players.length).toEqual(3); + const [p4, p5, p6] = players; + resetLog(); - expect(p4.element.classList.contains('b1')).toBeTruthy(); - expect(p5.element.classList.contains('b2')).toBeTruthy(); - expect(p6.element.classList.contains('b3')).toBeTruthy(); - }); + expect(p4.element.classList.contains('b1')).toBeTruthy(); + expect(p5.element.classList.contains('b2')).toBeTruthy(); + expect(p6.element.classList.contains('b3')).toBeTruthy(); + }); - it('should be able to query all active animations using :animating in a query', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should be able to query all active animations using :animating in a query', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp" #parent> <div *ngFor="let item of items" class="item e-{{ item }}"> </div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => a', - [ - query( - '.item:nth-child(odd)', - [ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - ]), - transition( - '* => b', - [ - query( - '.item:animating', - [ - style({opacity: 1}), - animate(1000, style({opacity: 0})), - ]), - ]), - ]), - ] - }) - class Cmp { - public exp: any; - public items: number[] = [0, 1, 2, 3, 4]; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => a', + [ + query( + '.item:nth-child(odd)', + [ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + ]), + transition( + '* => b', + [ + query( + '.item:animating', + [ + style({opacity: 1}), + animate(1000, style({opacity: 0})), + ]), + ]), + ]), + ] + }) + class Cmp { + public exp: any; + public items: number[] = [0, 1, 2, 3, 4]; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'a'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'a'; + fixture.detectChanges(); + engine.flush(); - let players = getLog(); - expect(players.length).toEqual(3); - resetLog(); + let players = getLog(); + expect(players.length).toEqual(3); + resetLog(); - cmp.exp = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'b'; + fixture.detectChanges(); + engine.flush(); - players = getLog(); - expect(players.length).toEqual(3); - expect(players[0].element.classList.contains('e-0')).toBeTruthy(); - expect(players[1].element.classList.contains('e-2')).toBeTruthy(); - expect(players[2].element.classList.contains('e-4')).toBeTruthy(); - }); + players = getLog(); + expect(players.length).toEqual(3); + expect(players[0].element.classList.contains('e-0')).toBeTruthy(); + expect(players[1].element.classList.contains('e-2')).toBeTruthy(); + expect(players[2].element.classList.contains('e-4')).toBeTruthy(); + }); - it('should be able to query all actively queued animation triggers via `@*:animating`', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should be able to query all actively queued animation triggers via `@*:animating`', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@parent]="exp0"> <div class="c1" [@child]="exp1"></div> <div class="c2" [@child]="exp2"></div> @@ -374,88 +367,89 @@ import {HostListener} from '../../src/metadata/directives'; <div class="c5" [@child]="exp5"></div> </div> `, - animations: [ - trigger( - 'parent', - [ - transition( - '* => *', - [ - query( - '@*:animating', [animate(1000, style({background: 'red'}))], - {optional: true}), - ]), - ]), - trigger( - 'child', - [ - transition('* => *', []), - ]) - ] - }) - class Cmp { - public exp0: any; - public exp1: any; - public exp2: any; - public exp3: any; - public exp4: any; - public exp5: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp1 = 0; - cmp.exp2 = 0; - cmp.exp3 = 0; - cmp.exp4 = 0; - cmp.exp5 = 0; - fixture.detectChanges(); - - cmp.exp0 = 0; - fixture.detectChanges(); - - let players = engine.players; - cancelAllPlayers(players); - - cmp.exp2 = 1; - cmp.exp4 = 1; - fixture.detectChanges(); - - cmp.exp0 = 1; - fixture.detectChanges(); + animations: [ + trigger( + 'parent', + [ + transition( + '* => *', + [ + query( + '@*:animating', [animate(1000, style({background: 'red'}))], + {optional: true}), + ]), + ]), + trigger( + 'child', + [ + transition('* => *', []), + ]) + ] + }) + class Cmp { + public exp0: any; + public exp1: any; + public exp2: any; + public exp3: any; + public exp4: any; + public exp5: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp1 = 0; + cmp.exp2 = 0; + cmp.exp3 = 0; + cmp.exp4 = 0; + cmp.exp5 = 0; + fixture.detectChanges(); + + cmp.exp0 = 0; + fixture.detectChanges(); + + let players = engine.players; + cancelAllPlayers(players); + + cmp.exp2 = 1; + cmp.exp4 = 1; + fixture.detectChanges(); + + cmp.exp0 = 1; + fixture.detectChanges(); + + players = engine.players; + cancelAllPlayers(players); + expect(players.length).toEqual(3); + + cmp.exp1 = 2; + cmp.exp2 = 2; + cmp.exp3 = 2; + cmp.exp4 = 2; + cmp.exp5 = 2; + fixture.detectChanges(); + + cmp.exp0 = 2; + fixture.detectChanges(); + + players = engine.players; + cancelAllPlayers(players); + expect(players.length).toEqual(6); + + cmp.exp0 = 3; + fixture.detectChanges(); + + players = engine.players; + cancelAllPlayers(players); + expect(players.length).toEqual(1); + }); - players = engine.players; - cancelAllPlayers(players); - expect(players.length).toEqual(3); - - cmp.exp1 = 2; - cmp.exp2 = 2; - cmp.exp3 = 2; - cmp.exp4 = 2; - cmp.exp5 = 2; - fixture.detectChanges(); - - cmp.exp0 = 2; - fixture.detectChanges(); - - players = engine.players; - cancelAllPlayers(players); - expect(players.length).toEqual(6); - - cmp.exp0 = 3; - fixture.detectChanges(); - - players = engine.players; - cancelAllPlayers(players); - expect(players.length).toEqual(1); - }); - - it('should collect styles for the same elements between queries', () => { - @Component({ + it( + 'should collect styles for the same elements between queries', () => { + @Component({ selector: 'ani-cmp', template: ` <div [@myAnimation]="exp"> @@ -478,110 +472,109 @@ import {HostListener} from '../../src/metadata/directives'; ] }) class Cmp { - public exp: any; - public items: any[] = [0, 1, 2]; - } + public exp: any; + public items: any[] = [0, 1, 2]; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(6); + const players = getLog(); + expect(players.length).toEqual(6); - const [p1, p2, p3, p4, p5, p6] = players; + const [p1, p2, p3, p4, p5, p6] = players; - expect(p1.delay).toEqual(0); - expect(p1.duration).toEqual(0); - expect(p1.keyframes).toEqual([ - {opacity: '0.01', offset: 0}, - {opacity: '0.01', offset: 1}, - ]); + expect(p1.delay).toEqual(0); + expect(p1.duration).toEqual(0); + expect(p1.keyframes).toEqual([ + {opacity: '0.01', offset: 0}, + {opacity: '0.01', offset: 1}, + ]); - expect(p2.delay).toEqual(0); - expect(p2.duration).toEqual(0); - expect(p2.keyframes).toEqual([ - {opacity: '0.01', offset: 0}, - {opacity: '0.01', offset: 1}, - ]); + expect(p2.delay).toEqual(0); + expect(p2.duration).toEqual(0); + expect(p2.keyframes).toEqual([ + {opacity: '0.01', offset: 0}, + {opacity: '0.01', offset: 1}, + ]); - expect(p3.delay).toEqual(0); - expect(p3.duration).toEqual(0); - expect(p3.keyframes).toEqual([ - {opacity: '0.01', offset: 0}, - {opacity: '0.01', offset: 1}, - ]); + expect(p3.delay).toEqual(0); + expect(p3.duration).toEqual(0); + expect(p3.keyframes).toEqual([ + {opacity: '0.01', offset: 0}, + {opacity: '0.01', offset: 1}, + ]); - expect(p4.delay).toEqual(0); - expect(p4.duration).toEqual(1000); - expect(p4.keyframes).toEqual([ - {opacity: '0.01', offset: 0}, - {opacity: '1', offset: 1}, - ]); + expect(p4.delay).toEqual(0); + expect(p4.duration).toEqual(1000); + expect(p4.keyframes).toEqual([ + {opacity: '0.01', offset: 0}, + {opacity: '1', offset: 1}, + ]); - expect(p5.delay).toEqual(1000); - expect(p5.duration).toEqual(1000); - expect(p5.keyframes).toEqual([ - {opacity: '0.01', offset: 0}, - {opacity: '1', offset: 1}, - ]); + expect(p5.delay).toEqual(1000); + expect(p5.duration).toEqual(1000); + expect(p5.keyframes).toEqual([ + {opacity: '0.01', offset: 0}, + {opacity: '1', offset: 1}, + ]); - expect(p6.delay).toEqual(1500); - expect(p6.duration).toEqual(1000); - expect(p6.keyframes).toEqual([ - {opacity: '0.01', offset: 0}, - {opacity: '1', offset: 1}, - ]); - }); + expect(p6.delay).toEqual(1500); + expect(p6.duration).toEqual(1000); + expect(p6.keyframes).toEqual([ + {opacity: '0.01', offset: 0}, + {opacity: '1', offset: 1}, + ]); + }); - it('should retain style values when :self is used inside of a query', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should retain style values when :self is used inside of a query', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [trigger('myAnimation', [transition( - '* => go', - [ - query(':self', style({opacity: '0.5'})), - animate(1000, style({opacity: '1'})) - ])])] - }) - class Cmp { - public exp: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(2); - - const [p1, p2] = players; - expect(p1.delay).toEqual(0); - expect(p1.duration).toEqual(0); - expect(p1.keyframes).toEqual([{opacity: '0.5', offset: 0}, {opacity: '0.5', offset: 1}]); - - expect(p2.delay).toEqual(0); - expect(p2.duration).toEqual(1000); - expect(p2.keyframes).toEqual([{opacity: '0.5', offset: 0}, {opacity: '1', offset: 1}]); - }); + animations: [trigger( + 'myAnimation', + [transition( + '* => go', + [query(':self', style({opacity: '0.5'})), animate(1000, style({opacity: '1'}))])])] + }) + class Cmp { + public exp: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(2); + + const [p1, p2] = players; + expect(p1.delay).toEqual(0); + expect(p1.duration).toEqual(0); + expect(p1.keyframes).toEqual([{opacity: '0.5', offset: 0}, {opacity: '0.5', offset: 1}]); + + expect(p2.delay).toEqual(0); + expect(p2.duration).toEqual(1000); + expect(p2.keyframes).toEqual([{opacity: '0.5', offset: 0}, {opacity: '1', offset: 1}]); + }); - it('should properly apply stagger after various other steps within a query', () => { - @Component({ + it('should properly apply stagger after various other steps within a query', () => { + @Component({ selector: 'ani-cmp', template: ` <div [@myAnimation]="exp"> @@ -605,101 +598,101 @@ import {HostListener} from '../../src/metadata/directives'; ] }) class Cmp { - public exp: any; - public items: any[] = [0, 1, 2]; - } + public exp: any; + public items: any[] = [0, 1, 2]; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(3); + const players = getLog(); + expect(players.length).toEqual(3); - const [p1, p2, p3] = players; + const [p1, p2, p3] = players; - expect(p1.delay).toEqual(0); - expect(p1.duration).toEqual(3000); - expect(p2.delay).toEqual(0); - expect(p2.duration).toEqual(3500); - expect(p3.delay).toEqual(0); - expect(p3.duration).toEqual(4000); - }); + expect(p1.delay).toEqual(0); + expect(p1.duration).toEqual(3000); + expect(p2.delay).toEqual(0); + expect(p2.duration).toEqual(3500); + expect(p3.delay).toEqual(0); + expect(p3.duration).toEqual(4000); + }); - it('should properly apply pre styling before a stagger is issued', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should properly apply pre styling before a stagger is issued', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp"> <div *ngFor="let item of items" class="item"> {{ item }} </div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - query( - ':enter', - [ - style({opacity: 0}), - stagger( - 100, - [ - animate(1000, style({opacity: 1})), - ]), - ]), - ]), - ]), - ] - }) - class Cmp { - public exp: any; - public items: any[] = [0, 1, 2, 3, 4]; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(5); - - for (let i = 0; i < players.length; i++) { - const player = players[i]; - const kf = player.keyframes; - const limit = kf.length - 1; - const staggerDelay = 100 * i; - const duration = 1000 + staggerDelay; - - expect(kf[0]).toEqual({opacity: '0', offset: 0}); - if (limit > 1) { - const offsetAtStaggerDelay = staggerDelay / duration; - expect(kf[1]).toEqual({opacity: '0', offset: offsetAtStaggerDelay}); - } - expect(kf[limit]).toEqual({opacity: '1', offset: 1}); - expect(player.duration).toEqual(duration); + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + query( + ':enter', + [ + style({opacity: 0}), + stagger( + 100, + [ + animate(1000, style({opacity: 1})), + ]), + ]), + ]), + ]), + ] + }) + class Cmp { + public exp: any; + public items: any[] = [0, 1, 2, 3, 4]; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(5); + + for (let i = 0; i < players.length; i++) { + const player = players[i]; + const kf = player.keyframes; + const limit = kf.length - 1; + const staggerDelay = 100 * i; + const duration = 1000 + staggerDelay; + + expect(kf[0]).toEqual({opacity: '0', offset: 0}); + if (limit > 1) { + const offsetAtStaggerDelay = staggerDelay / duration; + expect(kf[1]).toEqual({opacity: '0', offset: offsetAtStaggerDelay}); } - }); + expect(kf[limit]).toEqual({opacity: '1', offset: 1}); + expect(player.duration).toEqual(duration); + } + }); - it('should apply a full stagger step delay if the timing data is left undefined', () => { - @Component({ + it('should apply a full stagger step delay if the timing data is left undefined', () => { + @Component({ selector: 'ani-cmp', template: ` <div [@myAnimation]="exp"> @@ -717,146 +710,144 @@ import {HostListener} from '../../src/metadata/directives'; ])])])])] }) class Cmp { - public exp: any; - public items: any[] = [0, 1, 2, 3, 4]; - } + public exp: any; + public items: any[] = [0, 1, 2, 3, 4]; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(5); + const players = getLog(); + expect(players.length).toEqual(5); - const [p1, p2, p3, p4, p5] = players; - expect(p1.delay).toEqual(0); - expect(p2.delay).toEqual(1500); - expect(p3.delay).toEqual(3000); - expect(p4.delay).toEqual(4500); - expect(p5.delay).toEqual(6000); - }); + const [p1, p2, p3, p4, p5] = players; + expect(p1.delay).toEqual(0); + expect(p2.delay).toEqual(1500); + expect(p3.delay).toEqual(3000); + expect(p4.delay).toEqual(4500); + expect(p5.delay).toEqual(6000); + }); - it('should persist inner sub trigger styles once their animation is complete', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should persist inner sub trigger styles once their animation is complete', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div @parent *ngIf="exp1"> <div class="child" [@child]="exp2"></div> </div> `, - animations: [ - trigger( - 'parent', - [ - transition( - ':enter', - [ - query( - '.child', - [ - animateChild(), - ]), - ]), - ]), - trigger( - 'child', - [ - state('*, void', style({height: '0px'})), - state('b', style({height: '444px'})), - transition('* => *', animate(500)), - ]), - ] - }) - class Cmp { - public exp1: any; - public exp2: any; - } + animations: [ + trigger( + 'parent', + [ + transition( + ':enter', + [ + query( + '.child', + [ + animateChild(), + ]), + ]), + ]), + trigger( + 'child', + [ + state('*, void', style({height: '0px'})), + state('b', style({height: '444px'})), + transition('* => *', animate(500)), + ]), + ] + }) + class Cmp { + public exp1: any; + public exp2: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp1 = true; - cmp.exp2 = 'b'; - fixture.detectChanges(); - engine.flush(); + cmp.exp1 = true; + cmp.exp2 = 'b'; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(1); - const player = players[0]; + const players = getLog(); + expect(players.length).toEqual(1); + const player = players[0]; - expect(player.keyframes).toEqual([ - {height: '0px', offset: 0}, {height: '444px', offset: 1} - ]); - player.finish(); + expect(player.keyframes).toEqual([{height: '0px', offset: 0}, {height: '444px', offset: 1}]); + player.finish(); - expect(player.element.style.height).toEqual('444px'); - }); + expect(player.element.style.height).toEqual('444px'); + }); - it('should find newly inserted items in the component via :enter', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should find newly inserted items in the component via :enter', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div @myAnimation> <div *ngFor="let item of items" class="child"> {{ item }} </div> </div> `, - animations: [trigger( - 'myAnimation', - [ - transition( - ':enter', - [ - query( - ':enter', - [ - style({opacity: 0}), - animate(1000, style({opacity: .5})), - ]), - ]), - ])] - }) - class Cmp { - public items: any[] = [0, 1, 2]; - } + animations: [trigger( + 'myAnimation', + [ + transition( + ':enter', + [ + query( + ':enter', + [ + style({opacity: 0}), + animate(1000, style({opacity: .5})), + ]), + ]), + ])] + }) + class Cmp { + public items: any[] = [0, 1, 2]; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(3); + const players = getLog(); + expect(players.length).toEqual(3); - const [p1, p2, p3] = players; - expect(p1.element.innerText.trim()).toEqual('0'); - expect(p2.element.innerText.trim()).toEqual('1'); - expect(p3.element.innerText.trim()).toEqual('2'); + const [p1, p2, p3] = players; + expect(p1.element.innerText.trim()).toEqual('0'); + expect(p2.element.innerText.trim()).toEqual('1'); + expect(p3.element.innerText.trim()).toEqual('2'); - players.forEach(p => { - expect(p.keyframes).toEqual([{opacity: '0', offset: 0}, {opacity: '0.5', offset: 1}]); - }); + players.forEach(p => { + expect(p.keyframes).toEqual([{opacity: '0', offset: 0}, {opacity: '0.5', offset: 1}]); }); + }); - it('should cleanup :enter and :leave artifacts from nodes when any animation sequences fail to be built', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should cleanup :enter and :leave artifacts from nodes when any animation sequences fail to be built', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="items.length" class="parent" #container> <div *ngFor="let item of items" class="child"> {{ item }} @@ -864,161 +855,163 @@ import {HostListener} from '../../src/metadata/directives'; <div *ngIf="items.length == 0" class="child">Leave!</div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition('* => 0', []), - transition( - '* => *', - [ - query( - '.child:enter', - [ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - query( - '.incorrect-child:leave', - [ - animate(1000, style({opacity: 0})), - ]), - ]), - ]), - ] - }) - class Cmp { - @ViewChild('container') public container: any; - public items: any[] = []; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.items = []; + animations: [ + trigger( + 'myAnimation', + [ + transition('* => 0', []), + transition( + '* => *', + [ + query( + '.child:enter', + [ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + query( + '.incorrect-child:leave', + [ + animate(1000, style({opacity: 0})), + ]), + ]), + ]), + ] + }) + class Cmp { + @ViewChild('container') public container: any; + public items: any[] = []; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.items = []; + fixture.detectChanges(); + + cmp.items = [0, 1, 2, 3, 4]; + + expect(() => { fixture.detectChanges(); - - cmp.items = [0, 1, 2, 3, 4]; - - expect(() => { fixture.detectChanges(); }).toThrow(); - - const children = cmp.container.nativeElement.querySelectorAll('.child'); - expect(children.length).toEqual(5); - - for (let i = 0; i < children.length; i++) { - let child = children[i]; - expect(child.classList.contains(ENTER_CLASSNAME)).toBe(false); - expect(child.classList.contains(LEAVE_CLASSNAME)).toBe(false); - } - }); - - it('should find elements that have been removed via :leave', () => { - @Component({ - selector: 'ani-cmp', - template: ` + }).toThrow(); + + const children = cmp.container.nativeElement.querySelectorAll('.child'); + expect(children.length).toEqual(5); + + for (let i = 0; i < children.length; i++) { + let child = children[i]; + expect(child.classList.contains(ENTER_CLASSNAME)).toBe(false); + expect(child.classList.contains(LEAVE_CLASSNAME)).toBe(false); + } + }); + + it('should find elements that have been removed via :leave', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp" class="parent"> <div *ngFor="let item of items" class="child"> {{ item }} </div> </div> `, - animations: [trigger( - 'myAnimation', - [ - transition( - 'a => b', - [query(':leave', [style({opacity: 1}), animate(1000, style({opacity: .5}))])]), - ])] - }) - class Cmp { - public exp: any; - public items: any[] = [4, 2, 0]; - } + animations: [trigger( + 'myAnimation', + [ + transition( + 'a => b', + [query(':leave', [style({opacity: 1}), animate(1000, style({opacity: .5}))])]), + ])] + }) + class Cmp { + public exp: any; + public items: any[] = [4, 2, 0]; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'a'; - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.exp = 'a'; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp = 'b'; - cmp.items = []; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'b'; + cmp.items = []; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(3); + const players = getLog(); + expect(players.length).toEqual(3); - const [p1, p2, p3] = players; - expect(p1.element.innerText.trim()).toEqual('4'); - expect(p2.element.innerText.trim()).toEqual('2'); - expect(p3.element.innerText.trim()).toEqual('0'); + const [p1, p2, p3] = players; + expect(p1.element.innerText.trim()).toEqual('4'); + expect(p2.element.innerText.trim()).toEqual('2'); + expect(p3.element.innerText.trim()).toEqual('0'); - players.forEach(p => { - expect(p.keyframes).toEqual([{opacity: '1', offset: 0}, {opacity: '0.5', offset: 1}]); - }); + players.forEach(p => { + expect(p.keyframes).toEqual([{opacity: '1', offset: 0}, {opacity: '0.5', offset: 1}]); }); + }); - it('should find :enter nodes that have been inserted around non enter nodes', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should find :enter nodes that have been inserted around non enter nodes', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp" class="parent"> <div *ngFor="let item of items" class="child"> {{ item }} </div> </div> `, - animations: [trigger( - 'myAnimation', - [ - transition( - '* => go', - [query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]), - ])] - }) - class Cmp { - public exp: any; - public items: any[] = []; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'no'; - cmp.items = [2]; - fixture.detectChanges(); - engine.flush(); - resetLog(); + animations: [trigger( + 'myAnimation', + [ + transition( + '* => go', + [query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]), + ])] + }) + class Cmp { + public exp: any; + public items: any[] = []; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'no'; + cmp.items = [2]; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.exp = 'go'; - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'go'; + cmp.items = [0, 1, 2, 3, 4]; + fixture.detectChanges(); + engine.flush(); - const players = getLog(); - expect(players.length).toEqual(4); + const players = getLog(); + expect(players.length).toEqual(4); - const [p1, p2, p3, p4] = players; - expect(p1.element.innerText.trim()).toEqual('0'); - expect(p2.element.innerText.trim()).toEqual('1'); - expect(p3.element.innerText.trim()).toEqual('3'); - expect(p4.element.innerText.trim()).toEqual('4'); - }); + const [p1, p2, p3, p4] = players; + expect(p1.element.innerText.trim()).toEqual('0'); + expect(p2.element.innerText.trim()).toEqual('1'); + expect(p3.element.innerText.trim()).toEqual('3'); + expect(p4.element.innerText.trim()).toEqual('4'); + }); - it('should find :enter/:leave nodes that are nested inside of ng-container elements', () => { - @Component({ + it('should find :enter/:leave nodes that are nested inside of ng-container elements', () => { + @Component({ selector: 'ani-cmp', template: ` <div [@myAnimation]="items.length" class="parent"> @@ -1048,64 +1041,64 @@ import {HostListener} from '../../src/metadata/directives'; ])] }) class Cmp { - // TODO(issue/24571): remove '!'. - public items !: any[]; - } + // TODO(issue/24571): remove '!'. + public items!: any[]; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.items = []; - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.items = []; + fixture.detectChanges(); + engine.flush(); + resetLog(); - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - engine.flush(); + cmp.items = [0, 1, 2, 3, 4]; + fixture.detectChanges(); + engine.flush(); - let players = getLog(); - expect(players.length).toEqual(5); + let players = getLog(); + expect(players.length).toEqual(5); - for (let i = 0; i < 5; i++) { - let player = players[i] !; - expect(player.keyframes).toEqual([ - {opacity: '0', offset: 0}, - {opacity: '1', offset: 1}, - ]); + for (let i = 0; i < 5; i++) { + let player = players[i]!; + expect(player.keyframes).toEqual([ + {opacity: '0', offset: 0}, + {opacity: '1', offset: 1}, + ]); - let elm = player.element; - let text = i % 2 == 0 ? `even ${i}` : `odd ${i}`; - expect(elm.innerText.trim()).toEqual(text); - } + let elm = player.element; + let text = i % 2 == 0 ? `even ${i}` : `odd ${i}`; + expect(elm.innerText.trim()).toEqual(text); + } - resetLog(); - cmp.items = []; - fixture.detectChanges(); - engine.flush(); + resetLog(); + cmp.items = []; + fixture.detectChanges(); + engine.flush(); - players = getLog(); - expect(players.length).toEqual(5); + players = getLog(); + expect(players.length).toEqual(5); - for (let i = 0; i < 5; i++) { - let player = players[i] !; - expect(player.keyframes).toEqual([ - {opacity: '1', offset: 0}, - {opacity: '0', offset: 1}, - ]); + for (let i = 0; i < 5; i++) { + let player = players[i]!; + expect(player.keyframes).toEqual([ + {opacity: '1', offset: 0}, + {opacity: '0', offset: 1}, + ]); - let elm = player.element; - let text = i % 2 == 0 ? `even ${i}` : `odd ${i}`; - expect(elm.innerText.trim()).toEqual(text); - } - }); + let elm = player.element; + let text = i % 2 == 0 ? `even ${i}` : `odd ${i}`; + expect(elm.innerText.trim()).toEqual(text); + } + }); - it('should properly cancel items that were queried into a former animation and pass in the associated styles into the follow-up players per element', - () => { - @Component({ + it('should properly cancel items that were queried into a former animation and pass in the associated styles into the follow-up players per element', + () => { + @Component({ selector: 'ani-cmp', template: ` <div [@myAnimation]="exp" class="parent"> @@ -1128,52 +1121,52 @@ import {HostListener} from '../../src/metadata/directives'; ])] }) class Cmp { - public exp: any; - // TODO(issue/24571): remove '!'. - public items !: any[]; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'on'; - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - engine.flush(); - - const previousPlayers = getLog(); - expect(previousPlayers.length).toEqual(10); - resetLog(); - - cmp.exp = 'off'; - cmp.items = [0, 1, 2]; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(4); - - const [p1, p2, p3, p4] = players; - - // p1 && p2 are the starting players for item3 and item4 - expect(p1.previousStyles) - .toEqual({opacity: AUTO_STYLE, width: AUTO_STYLE, height: AUTO_STYLE}); - expect(p2.previousStyles) - .toEqual({opacity: AUTO_STYLE, width: AUTO_STYLE, height: AUTO_STYLE}); - - // p3 && p4 are the following players for item3 and item4 - expect(p3.previousStyles).toEqual({}); - expect(p4.previousStyles).toEqual({}); - }); - - it('should not remove a parent container if its contents are queried into by an ancestor element', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + public exp: any; + // TODO(issue/24571): remove '!'. + public items!: any[]; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'on'; + cmp.items = [0, 1, 2, 3, 4]; + fixture.detectChanges(); + engine.flush(); + + const previousPlayers = getLog(); + expect(previousPlayers.length).toEqual(10); + resetLog(); + + cmp.exp = 'off'; + cmp.items = [0, 1, 2]; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(4); + + const [p1, p2, p3, p4] = players; + + // p1 && p2 are the starting players for item3 and item4 + expect(p1.previousStyles) + .toEqual({opacity: AUTO_STYLE, width: AUTO_STYLE, height: AUTO_STYLE}); + expect(p2.previousStyles) + .toEqual({opacity: AUTO_STYLE, width: AUTO_STYLE, height: AUTO_STYLE}); + + // p3 && p4 are the following players for item3 and item4 + expect(p3.previousStyles).toEqual({}); + expect(p4.previousStyles).toEqual({}); + }); + + it('should not remove a parent container if its contents are queried into by an ancestor element', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp1" class="ancestor" #ancestor> <div class="parent" *ngIf="exp2" #parent> <div class="child"></div> @@ -1181,67 +1174,67 @@ import {HostListener} from '../../src/metadata/directives'; </div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - query( - '.child', - [ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - ]), - ]), - ] - }) - class Cmp { - public exp1: any = ''; - public exp2: any = true; - - @ViewChild('ancestor') public ancestorElm: any; - - @ViewChild('parent') public parentElm: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); - resetLog(); - - const ancestorElm = cmp.ancestorElm.nativeElement; - const parentElm = cmp.parentElm.nativeElement; - - cmp.exp1 = 'go'; - cmp.exp2 = false; - fixture.detectChanges(); - engine.flush(); - - expect(ancestorElm.contains(parentElm)).toBe(true); - - const players = getLog(); - expect(players.length).toEqual(2); - const [p1, p2] = players; - expect(parentElm.contains(p1.element)).toBe(true); - expect(parentElm.contains(p2.element)).toBe(true); - - cancelAllPlayers(players); - - expect(ancestorElm.contains(parentElm)).toBe(false); - }); - - it('should only retain a to-be-removed node if the inner queried items are apart of an animation issued by an ancestor', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - template: ` + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + query( + '.child', + [ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + ]), + ]), + ] + }) + class Cmp { + public exp1: any = ''; + public exp2: any = true; + + @ViewChild('ancestor') public ancestorElm: any; + + @ViewChild('parent') public parentElm: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + resetLog(); + + const ancestorElm = cmp.ancestorElm.nativeElement; + const parentElm = cmp.parentElm.nativeElement; + + cmp.exp1 = 'go'; + cmp.exp2 = false; + fixture.detectChanges(); + engine.flush(); + + expect(ancestorElm.contains(parentElm)).toBe(true); + + const players = getLog(); + expect(players.length).toEqual(2); + const [p1, p2] = players; + expect(parentElm.contains(p1.element)).toBe(true); + expect(parentElm.contains(p2.element)).toBe(true); + + cancelAllPlayers(players); + + expect(ancestorElm.contains(parentElm)).toBe(false); + }); + + it('should only retain a to-be-removed node if the inner queried items are apart of an animation issued by an ancestor', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@one]="exp1" [@two]="exp2" class="ancestor" #ancestor> <header>hello</header> <div class="parent" *ngIf="parentExp" #parent> @@ -1249,191 +1242,195 @@ import {HostListener} from '../../src/metadata/directives'; </div> </div> `, - animations: [ - trigger( - 'one', - [ - transition( - '* => go', - [ - query( - '.child', - [ - style({height: '100px'}), - animate(1000, style({height: '0px'})), - ]), - ]), - ]), - trigger( - 'two', - [ - transition('* => go', [query( - 'header', - [ - style({width: '100px'}), - animate(1000, style({width: '0px'})), - ])]), - ]), - ] - }) - class Cmp { - public exp1: any = ''; - public exp2: any = ''; - public parentExp: any = true; - - @ViewChild('ancestor') public ancestorElm: any; - - @ViewChild('parent') public parentElm: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - engine.flush(); - resetLog(); - - const ancestorElm = cmp.ancestorElm.nativeElement; - const parentElm = cmp.parentElm.nativeElement; - expect(ancestorElm.contains(parentElm)).toBe(true); - - cmp.exp1 = 'go'; - fixture.detectChanges(); - engine.flush(); - - expect(ancestorElm.contains(parentElm)).toBe(true); - - const onePlayers = getLog(); - expect(onePlayers.length).toEqual(1); // element.child - const [childPlayer] = onePlayers; - - let childPlayerComplete = false; - childPlayer.onDone(() => childPlayerComplete = true); - resetLog(); - flushMicrotasks(); - - expect(childPlayerComplete).toBe(false); - - cmp.exp2 = 'go'; - cmp.parentExp = false; - fixture.detectChanges(); - engine.flush(); - - const twoPlayers = getLog(); - expect(twoPlayers.length).toEqual(1); // the header element - expect(ancestorElm.contains(parentElm)).toBe(false); - expect(childPlayerComplete).toBe(true); - })); - - it('should finish queried players in an animation when the next animation takes over', () => { - @Component({ - selector: 'ani-cmp', - template: ` + animations: [ + trigger( + 'one', + [ + transition( + '* => go', + [ + query( + '.child', + [ + style({height: '100px'}), + animate(1000, style({height: '0px'})), + ]), + ]), + ]), + trigger( + 'two', + [ + transition('* => go', [query( + 'header', + [ + style({width: '100px'}), + animate(1000, style({width: '0px'})), + ])]), + ]), + ] + }) + class Cmp { + public exp1: any = ''; + public exp2: any = ''; + public parentExp: any = true; + + @ViewChild('ancestor') public ancestorElm: any; + + @ViewChild('parent') public parentElm: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + engine.flush(); + resetLog(); + + const ancestorElm = cmp.ancestorElm.nativeElement; + const parentElm = cmp.parentElm.nativeElement; + expect(ancestorElm.contains(parentElm)).toBe(true); + + cmp.exp1 = 'go'; + fixture.detectChanges(); + engine.flush(); + + expect(ancestorElm.contains(parentElm)).toBe(true); + + const onePlayers = getLog(); + expect(onePlayers.length).toEqual(1); // element.child + const [childPlayer] = onePlayers; + + let childPlayerComplete = false; + childPlayer.onDone(() => childPlayerComplete = true); + resetLog(); + flushMicrotasks(); + + expect(childPlayerComplete).toBe(false); + + cmp.exp2 = 'go'; + cmp.parentExp = false; + fixture.detectChanges(); + engine.flush(); + + const twoPlayers = getLog(); + expect(twoPlayers.length).toEqual(1); // the header element + expect(ancestorElm.contains(parentElm)).toBe(false); + expect(childPlayerComplete).toBe(true); + })); + + it('should finish queried players in an animation when the next animation takes over', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp" class="parent"> <div *ngFor="let item of items" class="child"> {{ item }} </div> </div> `, - animations: [trigger( - 'myAnimation', - [ - transition( - '* => on', - [ - query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), - ]), - transition('* => off', []) - ])] - }) - class Cmp { - public exp: any; - // TODO(issue/24571): remove '!'. - public items !: any[]; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'on'; - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(5); - - let count = 0; - players.forEach(p => { p.onDone(() => count++); }); + animations: [trigger( + 'myAnimation', + [ + transition( + '* => on', + [ + query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), + ]), + transition('* => off', []) + ])] + }) + class Cmp { + public exp: any; + // TODO(issue/24571): remove '!'. + public items!: any[]; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'on'; + cmp.items = [0, 1, 2, 3, 4]; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(5); + + let count = 0; + players.forEach(p => { + p.onDone(() => count++); + }); - expect(count).toEqual(0); + expect(count).toEqual(0); - cmp.exp = 'off'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'off'; + fixture.detectChanges(); + engine.flush(); - expect(count).toEqual(5); - }); + expect(count).toEqual(5); + }); - it('should finish queried players when the previous player is finished', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should finish queried players when the previous player is finished', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp" class="parent"> <div *ngFor="let item of items" class="child"> {{ item }} </div> </div> `, - animations: [trigger( - 'myAnimation', - [ - transition( - '* => on', - [ - query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), - ]), - transition('* => off', []) - ])] - }) - class Cmp { - public exp: any; - // TODO(issue/24571): remove '!'. - public items !: any[]; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'on'; - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(5); - - let count = 0; - players.forEach(p => { p.onDone(() => count++); }); + animations: [trigger( + 'myAnimation', + [ + transition( + '* => on', + [ + query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), + ]), + transition('* => off', []) + ])] + }) + class Cmp { + public exp: any; + // TODO(issue/24571): remove '!'. + public items!: any[]; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'on'; + cmp.items = [0, 1, 2, 3, 4]; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(5); + + let count = 0; + players.forEach(p => { + p.onDone(() => count++); + }); - expect(count).toEqual(0); + expect(count).toEqual(0); - expect(engine.players.length).toEqual(1); - engine.players[0].finish(); + expect(engine.players.length).toEqual(1); + engine.players[0].finish(); - expect(count).toEqual(5); - }); + expect(count).toEqual(5); + }); - it('should allow multiple triggers to animate on queried elements at the same time', () => { - @Component({ + it('should allow multiple triggers to animate on queried elements at the same time', () => { + @Component({ selector: 'ani-cmp', template: ` <div [@one]="exp1" [@two]="exp2" class="parent"> @@ -1464,111 +1461,117 @@ import {HostListener} from '../../src/metadata/directives'; ] }) class Cmp { - public exp1: any; - public exp2: any; - // TODO(issue/24571): remove '!'. - public items !: any[]; - } + public exp1: any; + public exp2: any; + // TODO(issue/24571): remove '!'. + public items!: any[]; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp1 = 'on'; - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - engine.flush(); + cmp.exp1 = 'on'; + cmp.items = [0, 1, 2, 3, 4]; + fixture.detectChanges(); + engine.flush(); - let players = getLog(); - expect(players.length).toEqual(5); + let players = getLog(); + expect(players.length).toEqual(5); - let count = 0; - players.forEach(p => { p.onDone(() => count++); }); + let count = 0; + players.forEach(p => { + p.onDone(() => count++); + }); - resetLog(); + resetLog(); - expect(count).toEqual(0); + expect(count).toEqual(0); - cmp.exp2 = 'on'; - fixture.detectChanges(); - engine.flush(); + cmp.exp2 = 'on'; + fixture.detectChanges(); + engine.flush(); - expect(count).toEqual(0); + expect(count).toEqual(0); - players = getLog(); - expect(players.length).toEqual(3); + players = getLog(); + expect(players.length).toEqual(3); - players.forEach(p => { p.onDone(() => count++); }); + players.forEach(p => { + p.onDone(() => count++); + }); - cmp.exp1 = 'off'; - fixture.detectChanges(); - engine.flush(); + cmp.exp1 = 'off'; + fixture.detectChanges(); + engine.flush(); - expect(count).toEqual(5); + expect(count).toEqual(5); - cmp.exp2 = 'off'; - fixture.detectChanges(); - engine.flush(); + cmp.exp2 = 'off'; + fixture.detectChanges(); + engine.flush(); - expect(count).toEqual(8); - }); + expect(count).toEqual(8); + }); - it('should cancel inner queried animations if a trigger state value changes, but isn\'t detected as a valid transition', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should cancel inner queried animations if a trigger state value changes, but isn\'t detected as a valid transition', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp" class="parent"> <div *ngFor="let item of items" class="child"> {{ item }} </div> </div> `, - animations: [trigger( - 'myAnimation', - [transition( - '* => on', - [ - query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), - ])])] - }) - class Cmp { - public exp: any; - // TODO(issue/24571): remove '!'. - public items !: any[]; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'on'; - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(5); + animations: [trigger( + 'myAnimation', + [transition( + '* => on', + [ + query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))]), + ])])] + }) + class Cmp { + public exp: any; + // TODO(issue/24571): remove '!'. + public items!: any[]; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'on'; + cmp.items = [0, 1, 2, 3, 4]; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(5); + + let count = 0; + players.forEach(p => { + p.onDone(() => count++); + }); - let count = 0; - players.forEach(p => { p.onDone(() => count++); }); + expect(count).toEqual(0); - expect(count).toEqual(0); + cmp.exp = 'off'; + fixture.detectChanges(); + engine.flush(); - cmp.exp = 'off'; - fixture.detectChanges(); - engine.flush(); + expect(count).toEqual(5); + }); - expect(count).toEqual(5); - }); - - it('should allow for queried items to restore their styling back to the original state via animate(time, "*")', - () => { - @Component({ + it('should allow for queried items to restore their styling back to the original state via animate(time, "*")', + () => { + @Component({ selector: 'ani-cmp', template: ` <div [@myAnimation]="exp" class="parent"> @@ -1590,301 +1593,299 @@ import {HostListener} from '../../src/metadata/directives'; ] }) class Cmp { - public exp: any; - // TODO(issue/24571): remove '!'. - public items !: any[]; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'on'; - cmp.items = [0, 1, 2]; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(3); - - players.forEach(p => { - expect(p.keyframes).toEqual([ - {opacity: '0', width: '0px', height: '0px', offset: 0}, - {opacity: '1', width: '0px', height: '0px', offset: .5}, - {opacity: AUTO_STYLE, width: AUTO_STYLE, height: '200px', offset: 1} - ]); - }); + public exp: any; + // TODO(issue/24571): remove '!'. + public items!: any[]; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'on'; + cmp.items = [0, 1, 2]; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(3); + + players.forEach(p => { + expect(p.keyframes).toEqual([ + {opacity: '0', width: '0px', height: '0px', offset: 0}, + {opacity: '1', width: '0px', height: '0px', offset: .5}, + {opacity: AUTO_STYLE, width: AUTO_STYLE, height: '200px', offset: 1} + ]); }); + }); - it('should query elements in sub components that do not contain animations using the :enter selector', - () => { - @Component({ - selector: 'parent-cmp', - template: ` + it('should query elements in sub components that do not contain animations using the :enter selector', + () => { + @Component({ + selector: 'parent-cmp', + template: ` <div [@myAnimation]="exp"> <child-cmp #child></child-cmp> </div> `, - animations: [trigger( - 'myAnimation', - [transition( - '* => on', - [query( - ':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])])] - }) - class ParentCmp { - public exp: any; - - @ViewChild('child') public child: any; - } - - @Component({ - selector: 'child-cmp', - template: ` + animations: [trigger( + 'myAnimation', + [transition( + '* => on', + [query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])])] + }) + class ParentCmp { + public exp: any; + + @ViewChild('child') public child: any; + } + + @Component({ + selector: 'child-cmp', + template: ` <div *ngFor="let item of items"> {{ item }} </div> ` - }) - class ChildCmp { - public items: any[] = []; - } - - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - fixture.detectChanges(); - - cmp.exp = 'on'; - cmp.child.items = [1, 2, 3]; - fixture.detectChanges(); - - const players = getLog() as any[]; - expect(players.length).toEqual(3); - - expect(players[0].element.innerText.trim()).toEqual('1'); - expect(players[1].element.innerText.trim()).toEqual('2'); - expect(players[2].element.innerText.trim()).toEqual('3'); - }); - - it('should query elements in sub components that do not contain animations using the :leave selector', - () => { - @Component({ - selector: 'parent-cmp', - template: ` + }) + class ChildCmp { + public items: any[] = []; + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + fixture.detectChanges(); + + cmp.exp = 'on'; + cmp.child.items = [1, 2, 3]; + fixture.detectChanges(); + + const players = getLog() as any[]; + expect(players.length).toEqual(3); + + expect(players[0].element.innerText.trim()).toEqual('1'); + expect(players[1].element.innerText.trim()).toEqual('2'); + expect(players[2].element.innerText.trim()).toEqual('3'); + }); + + it('should query elements in sub components that do not contain animations using the :leave selector', + () => { + @Component({ + selector: 'parent-cmp', + template: ` <div [@myAnimation]="exp"> <child-cmp #child></child-cmp> </div> `, - animations: [trigger( - 'myAnimation', - [transition( - '* => on', [query(':leave', [animate(1000, style({opacity: 0}))])])])] - }) - class ParentCmp { - public exp: any; - - @ViewChild('child', {static: true}) public child: any; - } - - @Component({ - selector: 'child-cmp', - template: ` + animations: [trigger( + 'myAnimation', + [transition('* => on', [query(':leave', [animate(1000, style({opacity: 0}))])])])] + }) + class ParentCmp { + public exp: any; + + @ViewChild('child', {static: true}) public child: any; + } + + @Component({ + selector: 'child-cmp', + template: ` <div *ngFor="let item of items"> {{ item }} </div> ` - }) - class ChildCmp { - public items: any[] = []; - } + }) + class ChildCmp { + public items: any[] = []; + } - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; - cmp.child.items = [4, 5, 6]; - fixture.detectChanges(); + cmp.child.items = [4, 5, 6]; + fixture.detectChanges(); - cmp.exp = 'on'; - cmp.child.items = []; - fixture.detectChanges(); + cmp.exp = 'on'; + cmp.child.items = []; + fixture.detectChanges(); - const players = getLog() as any[]; - expect(players.length).toEqual(3); + const players = getLog() as any[]; + expect(players.length).toEqual(3); - expect(players[0].element.innerText.trim()).toEqual('4'); - expect(players[1].element.innerText.trim()).toEqual('5'); - expect(players[2].element.innerText.trim()).toEqual('6'); - }); + expect(players[0].element.innerText.trim()).toEqual('4'); + expect(players[1].element.innerText.trim()).toEqual('5'); + expect(players[2].element.innerText.trim()).toEqual('6'); + }); - describe('options.limit', () => { - it('should limit results when a limit value is passed into the query options', () => { - @Component({ - selector: 'cmp', - template: ` + describe('options.limit', () => { + it('should limit results when a limit value is passed into the query options', () => { + @Component({ + selector: 'cmp', + template: ` <div [@myAnimation]="exp"> <div *ngFor="let item of items" class="item"> {{ item }} </div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - query( - '.item', - [ - style({opacity: 0}), - animate('1s', style({opacity: 1})), - ], - {limit: 2}), - ]), - ]), - ] - }) - class Cmp { - public exp: any; - public items: any[] = []; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + query( + '.item', + [ + style({opacity: 0}), + animate('1s', style({opacity: 1})), + ], + {limit: 2}), + ]), + ]), + ] + }) + class Cmp { + public exp: any; + public items: any[] = []; + } - TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.items = ['a', 'b', 'c', 'd', 'e']; - fixture.detectChanges(); + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.items = ['a', 'b', 'c', 'd', 'e']; + fixture.detectChanges(); - cmp.exp = 'go'; - fixture.detectChanges(); + cmp.exp = 'go'; + fixture.detectChanges(); - const players = getLog() as any[]; - expect(players.length).toEqual(2); - expect(players[0].element.innerText.trim()).toEqual('a'); - expect(players[1].element.innerText.trim()).toEqual('b'); - }); + const players = getLog() as any[]; + expect(players.length).toEqual(2); + expect(players[0].element.innerText.trim()).toEqual('a'); + expect(players[1].element.innerText.trim()).toEqual('b'); + }); - it('should support negative limit values by pulling in elements from the end of the query', - () => { - @Component({ - selector: 'cmp', - template: ` + it('should support negative limit values by pulling in elements from the end of the query', + () => { + @Component({ + selector: 'cmp', + template: ` <div [@myAnimation]="exp"> <div *ngFor="let item of items" class="item"> {{ item }} </div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - query( - '.item', - [ - style({opacity: 0}), - animate('1s', style({opacity: 1})), - ], - {limit: -3}), - ]), - ]), - ] - }) - class Cmp { - public exp: any; - public items: any[] = []; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + query( + '.item', + [ + style({opacity: 0}), + animate('1s', style({opacity: 1})), + ], + {limit: -3}), + ]), + ]), + ] + }) + class Cmp { + public exp: any; + public items: any[] = []; + } - TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.items = ['a', 'b', 'c', 'd', 'e']; - fixture.detectChanges(); - - cmp.exp = 'go'; - fixture.detectChanges(); - - const players = getLog() as any[]; - expect(players.length).toEqual(3); - expect(players[0].element.innerText.trim()).toEqual('c'); - expect(players[1].element.innerText.trim()).toEqual('d'); - expect(players[2].element.innerText.trim()).toEqual('e'); - }); - }); + TestBed.configureTestingModule({declarations: [Cmp]}); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.items = ['a', 'b', 'c', 'd', 'e']; + fixture.detectChanges(); + + cmp.exp = 'go'; + fixture.detectChanges(); + + const players = getLog() as any[]; + expect(players.length).toEqual(3); + expect(players[0].element.innerText.trim()).toEqual('c'); + expect(players[1].element.innerText.trim()).toEqual('d'); + expect(players[2].element.innerText.trim()).toEqual('e'); + }); }); + }); - describe('sub triggers', () => { - it('should animate a sub trigger that exists in an inner element in the template', () => { - @Component({ - selector: 'ani-cmp', - template: ` + describe('sub triggers', () => { + it('should animate a sub trigger that exists in an inner element in the template', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #parent class="parent" [@parent]="exp1"> <div #child class="child" [@child]="exp2"></div> </div> `, - animations: [ - trigger('parent', [transition( - '* => go1', - [ - style({width: '0px'}), animate(1000, style({width: '100px'})), - query('.child', [animateChild()]) - ])]), - trigger('child', [transition( - '* => go2', - [ - style({height: '0px'}), - animate(1000, style({height: '100px'})), - ])]) - ] - }) - class Cmp { - public exp1: any; - public exp2: any; - - @ViewChild('parent') public elm1: any; - - @ViewChild('child') public elm2: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp1 = 'go1'; - cmp.exp2 = 'go2'; - fixture.detectChanges(); - engine.flush(); - - const elm1 = cmp.elm1; - const elm2 = cmp.elm2; - - const [p1, p2] = getLog(); - expect(p1.delay).toEqual(0); - expect(p1.element).toEqual(elm1.nativeElement); - expect(p1.duration).toEqual(1000); - expect(p1.keyframes).toEqual([{width: '0px', offset: 0}, {width: '100px', offset: 1}]); - - expect(p2.delay).toEqual(0); - expect(p2.element).toEqual(elm2.nativeElement); - expect(p2.duration).toEqual(2000); - expect(p2.keyframes).toEqual([ - {height: '0px', offset: 0}, {height: '0px', offset: .5}, {height: '100px', offset: 1} - ]); - }); + animations: [ + trigger('parent', [transition( + '* => go1', + [ + style({width: '0px'}), animate(1000, style({width: '100px'})), + query('.child', [animateChild()]) + ])]), + trigger('child', [transition( + '* => go2', + [ + style({height: '0px'}), + animate(1000, style({height: '100px'})), + ])]) + ] + }) + class Cmp { + public exp1: any; + public exp2: any; + + @ViewChild('parent') public elm1: any; + + @ViewChild('child') public elm2: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp1 = 'go1'; + cmp.exp2 = 'go2'; + fixture.detectChanges(); + engine.flush(); + + const elm1 = cmp.elm1; + const elm2 = cmp.elm2; + + const [p1, p2] = getLog(); + expect(p1.delay).toEqual(0); + expect(p1.element).toEqual(elm1.nativeElement); + expect(p1.duration).toEqual(1000); + expect(p1.keyframes).toEqual([{width: '0px', offset: 0}, {width: '100px', offset: 1}]); + + expect(p2.delay).toEqual(0); + expect(p2.element).toEqual(elm2.nativeElement); + expect(p2.duration).toEqual(2000); + expect(p2.keyframes).toEqual([ + {height: '0px', offset: 0}, {height: '0px', offset: .5}, {height: '100px', offset: 1} + ]); + }); - it('should run and operate a series of triggers on a list of elements with overridden timing data', - () => { - @Component({ + it('should run and operate a series of triggers on a list of elements with overridden timing data', + () => { + @Component({ selector: 'ani-cmp', template: ` <div #parent class="parent" [@parent]="exp"> @@ -1908,557 +1909,548 @@ import {HostListener} from '../../src/metadata/directives'; ] }) class Cmp { - public exp: any; - public items: any[] = [0, 1, 2, 3, 4]; + public exp: any; + public items: any[] = [0, 1, 2, 3, 4]; - @ViewChild('parent') public elm: any; - } + @ViewChild('parent') public elm: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); - const parent = cmp.elm.nativeElement; - const elements = parent.querySelectorAll('.item'); + const parent = cmp.elm.nativeElement; + const elements = parent.querySelectorAll('.item'); - const players = getLog(); - expect(players.length).toEqual(7); - const [pA, pc1, pc2, pc3, pc4, pc5, pZ] = players; + const players = getLog(); + expect(players.length).toEqual(7); + const [pA, pc1, pc2, pc3, pc4, pc5, pZ] = players; - expect(pA.element).toEqual(parent); - expect(pA.delay).toEqual(0); - expect(pA.duration).toEqual(1000); + expect(pA.element).toEqual(parent); + expect(pA.delay).toEqual(0); + expect(pA.duration).toEqual(1000); - expect(pc1.element).toEqual(elements[0]); - expect(pc1.delay).toEqual(0); - expect(pc1.duration).toEqual(4000); + expect(pc1.element).toEqual(elements[0]); + expect(pc1.delay).toEqual(0); + expect(pc1.duration).toEqual(4000); - expect(pc2.element).toEqual(elements[1]); - expect(pc2.delay).toEqual(0); - expect(pc2.duration).toEqual(4000); + expect(pc2.element).toEqual(elements[1]); + expect(pc2.delay).toEqual(0); + expect(pc2.duration).toEqual(4000); - expect(pc3.element).toEqual(elements[2]); - expect(pc3.delay).toEqual(0); - expect(pc3.duration).toEqual(4000); + expect(pc3.element).toEqual(elements[2]); + expect(pc3.delay).toEqual(0); + expect(pc3.duration).toEqual(4000); - expect(pc4.element).toEqual(elements[3]); - expect(pc4.delay).toEqual(0); - expect(pc4.duration).toEqual(4000); + expect(pc4.element).toEqual(elements[3]); + expect(pc4.delay).toEqual(0); + expect(pc4.duration).toEqual(4000); - expect(pc5.element).toEqual(elements[4]); - expect(pc5.delay).toEqual(0); - expect(pc5.duration).toEqual(4000); + expect(pc5.element).toEqual(elements[4]); + expect(pc5.delay).toEqual(0); + expect(pc5.duration).toEqual(4000); - expect(pZ.element).toEqual(parent); - expect(pZ.delay).toEqual(4000); - expect(pZ.duration).toEqual(1000); - }); + expect(pZ.element).toEqual(parent); + expect(pZ.delay).toEqual(4000); + expect(pZ.duration).toEqual(1000); + }); - it('should silently continue if a sub trigger is animated that doesn\'t exist', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should silently continue if a sub trigger is animated that doesn\'t exist', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #parent class="parent" [@parent]="exp"> <div class="child"></div> </div> `, - animations: - [trigger('parent', [transition( - '* => go', - [ - style({opacity: 0}), animate(1000, style({opacity: 1})), - query('.child', [animateChild({duration: '1s'})]), - animate(1000, style({opacity: 0})) - ])])] - }) - class Cmp { - public exp: any; - public items: any[] = [0, 1, 2, 3, 4]; + animations: + [trigger('parent', [transition( + '* => go', + [ + style({opacity: 0}), animate(1000, style({opacity: 1})), + query('.child', [animateChild({duration: '1s'})]), + animate(1000, style({opacity: 0})) + ])])] + }) + class Cmp { + public exp: any; + public items: any[] = [0, 1, 2, 3, 4]; + + @ViewChild('parent') public elm: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'go'; + fixture.detectChanges(); + engine.flush(); + + const parent = cmp.elm.nativeElement; + const players = getLog(); + expect(players.length).toEqual(2); + + const [pA, pZ] = players; + expect(pA.element).toEqual(parent); + expect(pA.delay).toEqual(0); + expect(pA.duration).toEqual(1000); + + expect(pZ.element).toEqual(parent); + expect(pZ.delay).toEqual(1000); + expect(pZ.duration).toEqual(1000); + }); - @ViewChild('parent') public elm: any; - } + it('should silently continue if a sub trigger is animated that doesn\'t contain a trigger that is setup for animation', + () => { + @Component({ + selector: 'ani-cmp', + template: ` + <div #parent class="parent" [@parent]="exp1"> + <div [@child]="exp2" class="child"></div> + </div> + `, + animations: [ + trigger( + 'child', + [transition('a => z', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]), + trigger('parent', [transition( + 'a => z', + [ + style({opacity: 0}), animate(1000, style({opacity: 1})), + query('.child', [animateChild({duration: '1s'})]), + animate(1000, style({opacity: 0})) + ])]) + ] + }) + class Cmp { + public exp1: any; + public exp2: any; + + @ViewChild('parent') public elm: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp1 = 'a'; + cmp.exp2 = 'a'; + fixture.detectChanges(); + engine.flush(); + resetLog(); + + cmp.exp1 = 'z'; + fixture.detectChanges(); + engine.flush(); + + const parent = cmp.elm.nativeElement; + const players = getLog(); + expect(players.length).toEqual(2); + + const [pA, pZ] = players; + expect(pA.element).toEqual(parent); + expect(pA.delay).toEqual(0); + expect(pA.duration).toEqual(1000); + + expect(pZ.element).toEqual(parent); + expect(pZ.delay).toEqual(1000); + expect(pZ.duration).toEqual(1000); + }); + + it('should animate all sub triggers on the element at the same time', () => { + @Component({ + selector: 'ani-cmp', + template: ` + <div #parent class="parent" [@parent]="exp1"> + <div [@w]="exp2" [@h]="exp2" class="child"></div> + </div> + `, + animations: [ + trigger( + 'w', + [transition('* => go', [style({width: 0}), animate(1800, style({width: '100px'}))])]), + trigger( + 'h', [transition( + '* => go', [style({height: 0}), animate(1500, style({height: '100px'}))])]), + trigger( + 'parent', [transition( + '* => go', + [ + style({opacity: 0}), animate(1000, style({opacity: 1})), + query('.child', [animateChild()]), animate(1000, style({opacity: 0})) + ])]) + ] + }) + class Cmp { + public exp1: any; + public exp2: any; - TestBed.configureTestingModule({declarations: [Cmp]}); + @ViewChild('parent') public elm: any; + } - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + TestBed.configureTestingModule({declarations: [Cmp]}); - cmp.exp = 'go'; - fixture.detectChanges(); - engine.flush(); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - const parent = cmp.elm.nativeElement; - const players = getLog(); - expect(players.length).toEqual(2); + cmp.exp1 = 'go'; + cmp.exp2 = 'go'; + fixture.detectChanges(); + engine.flush(); - const [pA, pZ] = players; - expect(pA.element).toEqual(parent); - expect(pA.delay).toEqual(0); - expect(pA.duration).toEqual(1000); + const players = getLog(); + expect(players.length).toEqual(4); + const [pA, pc1, pc2, pZ] = players; - expect(pZ.element).toEqual(parent); - expect(pZ.delay).toEqual(1000); - expect(pZ.duration).toEqual(1000); - }); + expect(pc1.delay).toEqual(0); + expect(pc1.duration).toEqual(2800); - it('should silently continue if a sub trigger is animated that doesn\'t contain a trigger that is setup for animation', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + expect(pc2.delay).toEqual(0); + expect(pc2.duration).toEqual(2500); + + expect(pZ.delay).toEqual(2800); + expect(pZ.duration).toEqual(1000); + }); + + it('should skip a sub animation when a zero duration value is passed in', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #parent class="parent" [@parent]="exp1"> <div [@child]="exp2" class="child"></div> </div> `, - animations: [ - trigger('child', [transition( - 'a => z', - [style({opacity: 0}), animate(1000, style({opacity: 1}))])]), - trigger('parent', [transition( - 'a => z', - [ - style({opacity: 0}), animate(1000, style({opacity: 1})), - query('.child', [animateChild({duration: '1s'})]), - animate(1000, style({opacity: 0})) - ])]) - ] - }) - class Cmp { - public exp1: any; - public exp2: any; + animations: [ + trigger( + 'child', + [transition('* => go', [style({width: 0}), animate(1800, style({width: '100px'}))])]), + trigger('parent', [transition( + '* => go', + [ + style({opacity: 0}), animate(1000, style({opacity: 1})), + query('.child', [animateChild({duration: '0'})]), + animate(1000, style({opacity: 0})) + ])]) + ] + }) + class Cmp { + public exp1: any; + public exp2: any; + + @ViewChild('parent') public elm: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp1 = 'go'; + cmp.exp2 = 'go'; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(2); + const [pA, pZ] = players; + + expect(pA.delay).toEqual(0); + expect(pA.duration).toEqual(1000); + + expect(pZ.delay).toEqual(1000); + expect(pZ.duration).toEqual(1000); + }); - @ViewChild('parent') public elm: any; - } + it('should only allow a sub animation to be used up by a parent trigger once', () => { + @Component({ + selector: 'ani-cmp', + template: ` + <div [@parent]="exp1" class="parent1" #parent> + <div [@parent]="exp1" class="parent2"> + <div [@child]="exp2" class="child"> + </div> + </div> + </div> + `, + animations: [ + trigger('parent', [transition( + '* => go', + [ + style({opacity: 0}), animate(1000, style({opacity: 1})), + query('.child', animateChild()) + ])]), + trigger( + 'child', + [transition('* => go', [style({opacity: 0}), animate(1800, style({opacity: 1}))])]) + ] + }) + class Cmp { + public exp1: any; + public exp2: any; + + @ViewChild('parent') public elm: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp1 = 'go'; + cmp.exp2 = 'go'; + fixture.detectChanges(); + engine.flush(); + + const players = getLog(); + expect(players.length).toEqual(3); + + const [p1, p2, p3] = players; + + // parent2 is evaluated first because it is inside of parent1 + expect(p1.element.classList.contains('parent2')).toBeTruthy(); + expect(p2.element.classList.contains('child')).toBeTruthy(); + expect(p3.element.classList.contains('parent1')).toBeTruthy(); + }); - TestBed.configureTestingModule({declarations: [Cmp]}); + it('should emulate a leave animation on the nearest sub host elements when a parent is removed', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + template: ` + <div @parent *ngIf="exp" class="parent1" #parent> + <child-cmp #child @leave (@leave.start)="animateStart($event)"></child-cmp> + </div> + `, + animations: [ + trigger( + 'leave', + [ + transition(':leave', [animate(1000, style({color: 'gold'}))]), + ]), + trigger( + 'parent', + [ + transition(':leave', [query(':leave', animateChild())]), + ]), + ] + }) + class ParentCmp { + public exp: boolean = true; + @ViewChild('child') public childElm: any; + + public childEvent: any; + + animateStart(event: any) { + if (event.toState == 'void') { + this.childEvent = event; + } + } + } - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + @Component({ + selector: 'child-cmp', + template: '...', + animations: [ + trigger( + 'child', + [ + transition(':leave', [animate(1000, style({color: 'gold'}))]), + ]), + ] + }) + class ChildCmp { + public childEvent: any; + + @HostBinding('@child') public animate = true; + + @HostListener('@child.start', ['$event']) + animateStart(event: any) { + if (event.toState == 'void') { + this.childEvent = event; + } + } + } - cmp.exp1 = 'a'; - cmp.exp2 = 'a'; - fixture.detectChanges(); - engine.flush(); - resetLog(); + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; - cmp.exp1 = 'z'; - fixture.detectChanges(); - engine.flush(); + fixture.detectChanges(); - const parent = cmp.elm.nativeElement; - const players = getLog(); - expect(players.length).toEqual(2); + const childCmp = cmp.childElm; - const [pA, pZ] = players; - expect(pA.element).toEqual(parent); - expect(pA.delay).toEqual(0); - expect(pA.duration).toEqual(1000); + cmp.exp = false; + fixture.detectChanges(); + flushMicrotasks(); - expect(pZ.element).toEqual(parent); - expect(pZ.delay).toEqual(1000); - expect(pZ.duration).toEqual(1000); - }); + expect(cmp.childEvent.toState).toEqual('void'); + expect(cmp.childEvent.totalTime).toEqual(1000); + expect(childCmp.childEvent.toState).toEqual('void'); + expect(childCmp.childEvent.totalTime).toEqual(1000); + })); - it('should animate all sub triggers on the element at the same time', () => { - @Component({ - selector: 'ani-cmp', - template: ` - <div #parent class="parent" [@parent]="exp1"> - <div [@w]="exp2" [@h]="exp2" class="child"></div> + it('should emulate a leave animation on a sub component\'s inner elements when a parent leave animation occurs with animateChild', + () => { + @Component({ + selector: 'ani-cmp', + template: ` + <div @myAnimation *ngIf="exp" class="parent"> + <child-cmp></child-cmp> </div> `, - animations: [ - trigger('w', [ - transition('* => go', [ - style({ width: 0 }), - animate(1800, style({ width: '100px' })) - ]) - ]), - trigger('h', [ - transition('* => go', [ - style({ height: 0 }), - animate(1500, style({ height: '100px' })) - ]) - ]), - trigger('parent', [ - transition('* => go', [ - style({ opacity: 0 }), - animate(1000, style({ opacity: 1 })), - query('.child', [ - animateChild() - ]), - animate(1000, style({ opacity: 0 })) - ]) - ]) - ] - }) - class Cmp { - public exp1: any; - public exp2: any; - - @ViewChild('parent') public elm: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp1 = 'go'; - cmp.exp2 = 'go'; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(4); - const [pA, pc1, pc2, pZ] = players; - - expect(pc1.delay).toEqual(0); - expect(pc1.duration).toEqual(2800); - - expect(pc2.delay).toEqual(0); - expect(pc2.duration).toEqual(2500); - - expect(pZ.delay).toEqual(2800); - expect(pZ.duration).toEqual(1000); - }); - - it('should skip a sub animation when a zero duration value is passed in', () => { - @Component({ - selector: 'ani-cmp', - template: ` - <div #parent class="parent" [@parent]="exp1"> - <div [@child]="exp2" class="child"></div> - </div> - `, - animations: [ - trigger('child', [transition( - '* => go', - [style({width: 0}), animate(1800, style({width: '100px'}))])]), - trigger('parent', [transition( - '* => go', - [ - style({opacity: 0}), animate(1000, style({opacity: 1})), - query('.child', [animateChild({duration: '0'})]), - animate(1000, style({opacity: 0})) - ])]) - ] - }) - class Cmp { - public exp1: any; - public exp2: any; - - @ViewChild('parent') public elm: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp1 = 'go'; - cmp.exp2 = 'go'; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(2); - const [pA, pZ] = players; - - expect(pA.delay).toEqual(0); - expect(pA.duration).toEqual(1000); - - expect(pZ.delay).toEqual(1000); - expect(pZ.duration).toEqual(1000); - }); - - it('should only allow a sub animation to be used up by a parent trigger once', () => { - @Component({ - selector: 'ani-cmp', - template: ` - <div [@parent]="exp1" class="parent1" #parent> - <div [@parent]="exp1" class="parent2"> - <div [@child]="exp2" class="child"> - </div> - </div> - </div> - `, - animations: [ - trigger('parent', [transition( - '* => go', - [ - style({opacity: 0}), animate(1000, style({opacity: 1})), - query('.child', animateChild()) - ])]), - trigger('child', [transition( - '* => go', - [style({opacity: 0}), animate(1800, style({opacity: 1}))])]) - ] - }) - class Cmp { - public exp1: any; - public exp2: any; - - @ViewChild('parent') public elm: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp1 = 'go'; - cmp.exp2 = 'go'; - fixture.detectChanges(); - engine.flush(); - - const players = getLog(); - expect(players.length).toEqual(3); - - const [p1, p2, p3] = players; - - // parent2 is evaluated first because it is inside of parent1 - expect(p1.element.classList.contains('parent2')).toBeTruthy(); - expect(p2.element.classList.contains('child')).toBeTruthy(); - expect(p3.element.classList.contains('parent1')).toBeTruthy(); - }); - - it('should emulate a leave animation on the nearest sub host elements when a parent is removed', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - template: ` - <div @parent *ngIf="exp" class="parent1" #parent> - <child-cmp #child @leave (@leave.start)="animateStart($event)"></child-cmp> - </div> - `, - animations: [ - trigger( - 'leave', - [ - transition(':leave', [animate(1000, style({color: 'gold'}))]), - ]), - trigger( - 'parent', - [ - transition(':leave', [query(':leave', animateChild())]), - ]), - ] - }) - class ParentCmp { - public exp: boolean = true; - @ViewChild('child') public childElm: any; - - public childEvent: any; - - animateStart(event: any) { - if (event.toState == 'void') { - this.childEvent = event; - } - } - } - - @Component({ - selector: 'child-cmp', - template: '...', - animations: [ - trigger( - 'child', - [ - transition(':leave', [animate(1000, style({color: 'gold'}))]), - ]), - ] - }) - class ChildCmp { - public childEvent: any; - - @HostBinding('@child') public animate = true; - - @HostListener('@child.start', ['$event']) - animateStart(event: any) { - if (event.toState == 'void') { - this.childEvent = event; - } - } - } - - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - - fixture.detectChanges(); - - const childCmp = cmp.childElm; - - cmp.exp = false; - fixture.detectChanges(); - flushMicrotasks(); - - expect(cmp.childEvent.toState).toEqual('void'); - expect(cmp.childEvent.totalTime).toEqual(1000); - expect(childCmp.childEvent.toState).toEqual('void'); - expect(childCmp.childEvent.totalTime).toEqual(1000); - })); - - it('should emulate a leave animation on a sub component\'s inner elements when a parent leave animation occurs with animateChild', - () => { - @Component({ - selector: 'ani-cmp', - template: ` - <div @myAnimation *ngIf="exp" class="parent"> - <child-cmp></child-cmp> - </div> - `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - ':leave', - [ - query('@*', animateChild()), - ]), - ]), - ] - }) - class ParentCmp { - public exp: boolean = true; - } - - @Component({ - selector: 'child-cmp', - template: ` + animations: [ + trigger( + 'myAnimation', + [ + transition( + ':leave', + [ + query('@*', animateChild()), + ]), + ]), + ] + }) + class ParentCmp { + public exp: boolean = true; + } + + @Component({ + selector: 'child-cmp', + template: ` <section> <div class="inner-div" @myChildAnimation></div> </section> `, - animations: [ - trigger( - 'myChildAnimation', - [ - transition( - ':leave', - [ - style({opacity: 0}), - animate('1s', style({opacity: 1})), - ]), - ]), - ] - }) - class ChildCmp { - } - - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - const cmp = fixture.componentInstance; - - cmp.exp = true; - fixture.detectChanges(); - - cmp.exp = false; - fixture.detectChanges(); - - let players = getLog(); - expect(players.length).toEqual(1); - const [player] = players; - - expect(player.element.classList.contains('inner-div')).toBeTruthy(); - expect(player.keyframes).toEqual([ - {opacity: '0', offset: 0}, - {opacity: '1', offset: 1}, - ]); - }); - - it('should not cause a removal of inner @trigger DOM nodes when a parent animation occurs', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - template: ` + animations: [ + trigger( + 'myChildAnimation', + [ + transition( + ':leave', + [ + style({opacity: 0}), + animate('1s', style({opacity: 1})), + ]), + ]), + ] + }) + class ChildCmp { + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + const cmp = fixture.componentInstance; + + cmp.exp = true; + fixture.detectChanges(); + + cmp.exp = false; + fixture.detectChanges(); + + let players = getLog(); + expect(players.length).toEqual(1); + const [player] = players; + + expect(player.element.classList.contains('inner-div')).toBeTruthy(); + expect(player.keyframes).toEqual([ + {opacity: '0', offset: 0}, + {opacity: '1', offset: 1}, + ]); + }); + + it('should not cause a removal of inner @trigger DOM nodes when a parent animation occurs', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + template: ` <div @parent *ngIf="exp" class="parent"> this <div @child>child</div> </div> `, - animations: [ - trigger( - 'parent', - [ - transition( - ':leave', - [ - style({opacity: 0}), - animate('1s', style({opacity: 1})), - ]), - ]), - trigger( - 'child', - [ - transition( - '* => something', - [ - style({opacity: 0}), - animate('1s', style({opacity: 1})), - ]), - ]), - ] - }) - class Cmp { - public exp: boolean = true; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = true; - fixture.detectChanges(); - flushMicrotasks(); - - cmp.exp = false; - fixture.detectChanges(); - flushMicrotasks(); - - const players = getLog(); - expect(players.length).toEqual(1); - - const element = players[0] !.element; - expect(element.innerText.trim()).toMatch(/this\s+child/mg); - })); - - it('should only mark outermost *directive nodes :enter and :leave when inserts and removals occur', - () => { - @Component({ - selector: 'ani-cmp', - animations: [ - trigger( - 'anim', - [ - transition( - '* => enter', - [ - query(':enter', [animate(1000, style({color: 'red'}))]), - ]), - transition( - '* => leave', - [ - query(':leave', [animate(1000, style({color: 'blue'}))]), - ]), - ]), - ], - template: ` + animations: [ + trigger( + 'parent', + [ + transition( + ':leave', + [ + style({opacity: 0}), + animate('1s', style({opacity: 1})), + ]), + ]), + trigger( + 'child', + [ + transition( + '* => something', + [ + style({opacity: 0}), + animate('1s', style({opacity: 1})), + ]), + ]), + ] + }) + class Cmp { + public exp: boolean = true; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = true; + fixture.detectChanges(); + flushMicrotasks(); + + cmp.exp = false; + fixture.detectChanges(); + flushMicrotasks(); + + const players = getLog(); + expect(players.length).toEqual(1); + + const element = players[0]!.element; + expect(element.innerText.trim()).toMatch(/this\s+child/mg); + })); + + it('should only mark outermost *directive nodes :enter and :leave when inserts and removals occur', + () => { + @Component({ + selector: 'ani-cmp', + animations: [ + trigger( + 'anim', + [ + transition( + '* => enter', + [ + query(':enter', [animate(1000, style({color: 'red'}))]), + ]), + transition( + '* => leave', + [ + query(':leave', [animate(1000, style({color: 'blue'}))]), + ]), + ]), + ], + template: ` <section class="container" [@anim]="exp ? 'enter' : 'leave'"> <div class="a" *ngIf="exp"> <div class="b" *ngIf="exp"> @@ -2474,629 +2466,632 @@ import {HostListener} from '../../src/metadata/directives'; </div> </section> ` - }) - class Cmp { - // TODO(issue/24571): remove '!'. - public exp !: boolean; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - const container = fixture.elementRef.nativeElement; - - cmp.exp = true; - fixture.detectChanges(); - engine.flush(); - - let players = getLog(); - resetLog(); - expect(players.length).toEqual(2); - const [p1, p2] = players; - - expect(p1.element.classList.contains('a')); - expect(p2.element.classList.contains('d')); - - cmp.exp = false; - fixture.detectChanges(); - engine.flush(); - - players = getLog(); - resetLog(); - expect(players.length).toEqual(2); - const [p3, p4] = players; - - expect(p3.element.classList.contains('a')); - expect(p4.element.classList.contains('d')); - }); - - it('should collect multiple root levels of :enter and :leave nodes', () => { - @Component({ - selector: 'ani-cmp', - animations: [ - trigger('pageAnimation', [ + }) + class Cmp { + // TODO(issue/24571): remove '!'. + public exp!: boolean; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + const container = fixture.elementRef.nativeElement; + + cmp.exp = true; + fixture.detectChanges(); + engine.flush(); + + let players = getLog(); + resetLog(); + expect(players.length).toEqual(2); + const [p1, p2] = players; + + expect(p1.element.classList.contains('a')).toBeTrue(); + expect(p2.element.classList.contains('d')).toBeTrue(); + + cmp.exp = false; + fixture.detectChanges(); + engine.flush(); + + players = getLog(); + resetLog(); + expect(players.length).toEqual(2); + const [p3, p4] = players; + + expect(p3.element.classList.contains('a')).toBeTrue(); + expect(p4.element.classList.contains('d')).toBeTrue(); + }); + + it('should collect multiple root levels of :enter and :leave nodes', () => { + @Component({ + selector: 'ani-cmp', + animations: [trigger( + 'pageAnimation', + [ transition(':enter', []), - transition('* => *', [ - query(':leave', [ - animate('1s', style({ opacity: 0 })) - ], { optional: true }), - query(':enter', [ - animate('1s', style({ opacity: 1 })) - ], { optional: true }) - ]) - ]) - ], - template: ` - <div [@pageAnimation]="status"> - <header> - <div *ngIf="!loading" class="title">{{ title }}</div> - <div *ngIf="loading" class="loading">loading...</div> - </header> - <section> - <div class="page"> - <div *ngIf="page1" class="page1"> - <div *ngIf="true">page 1</div> - </div> - <div *ngIf="page2" class="page2"> - <div *ngIf="true">page 2</div> - </div> - </div> - </section> - </div> - ` - }) - class Cmp { - get title() { - if (this.page1) { - return 'hello from page1'; - } - return 'greetings from page2'; - } - - page1 = false; - page2 = false; - loading = false; - - get status() { - if (this.loading) return 'loading'; - if (this.page1) return 'page1'; - if (this.page2) return 'page2'; - return ''; - } - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.loading = true; - fixture.detectChanges(); - engine.flush(); - - let players = getLog(); - resetLog(); - cancelAllPlayers(players); - - cmp.page1 = true; - cmp.loading = false; - fixture.detectChanges(); - engine.flush(); - - let p1: MockAnimationPlayer; - let p2: MockAnimationPlayer; - let p3: MockAnimationPlayer; - - players = getLog(); - expect(players.length).toEqual(3); - [p1, p2, p3] = players; - - expect(p1.element.classList.contains('loading')).toBe(true); - expect(p2.element.classList.contains('title')).toBe(true); - expect(p3.element.classList.contains('page1')).toBe(true); - - resetLog(); - cancelAllPlayers(players); - - cmp.page1 = false; - cmp.loading = true; - fixture.detectChanges(); - - players = getLog(); - cancelAllPlayers(players); - - expect(players.length).toEqual(3); - [p1, p2, p3] = players; - - expect(p1.element.classList.contains('title')).toBe(true); - expect(p2.element.classList.contains('page1')).toBe(true); - expect(p3.element.classList.contains('loading')).toBe(true); - - resetLog(); - cancelAllPlayers(players); - - cmp.page2 = true; - cmp.loading = false; - fixture.detectChanges(); - engine.flush(); - - players = getLog(); - expect(players.length).toEqual(3); - [p1, p2, p3] = players; - - expect(p1.element.classList.contains('loading')).toBe(true); - expect(p2.element.classList.contains('title')).toBe(true); - expect(p3.element.classList.contains('page2')).toBe(true); - }); - - it('should emulate leave animation callbacks for all sub elements that have leave triggers within the component', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - animations: [ - trigger('parent', []), trigger('child', []), - trigger( - 'childWithAnimation', - [ - transition( - ':leave', - [ - animate(1000, style({background: 'red'})), - ]), - ]) - ], - template: ` - <div data-name="p" class="parent" @parent *ngIf="exp" (@parent.start)="callback($event)" (@parent.done)="callback($event)"> - <div data-name="c1" @child (@child.start)="callback($event)" (@child.done)="callback($event)"></div> - <div data-name="c2" @child (@child.start)="callback($event)" (@child.done)="callback($event)"></div> - <div data-name="c3" @childWithAnimation (@childWithAnimation.start)="callback($event)" (@childWithAnimation.done)="callback($event)"></div> - </div> - ` - }) - class Cmp { - // TODO(issue/24571): remove '!'. - public exp !: boolean; - public log: string[] = []; - callback(event: any) { - this.log.push(event.element.getAttribute('data-name') + '-' + event.phaseName); - } - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = true; - fixture.detectChanges(); - flushMicrotasks(); - cmp.log = []; - - cmp.exp = false; - fixture.detectChanges(); - flushMicrotasks(); - expect(cmp.log).toEqual([ - 'c1-start', 'c1-done', 'c2-start', 'c2-done', 'p-start', 'c3-start', 'c3-done', - 'p-done' - ]); - })); - - it('should build, but not run sub triggers when a parent animation is scheduled', () => { - @Component({ - selector: 'parent-cmp', - animations: - [trigger('parent', [transition('* => *', [animate(1000, style({opacity: 0}))])])], - template: '<div [@parent]="exp"><child-cmp #child></child-cmp></div>' - }) - class ParentCmp { - public exp: any; - - @ViewChild('child') public childCmp: any; - } - - @Component({ - selector: 'child-cmp', - animations: - [trigger('child', [transition('* => *', [animate(1000, style({color: 'red'}))])])], - template: '<div [@child]="exp"></div>' - }) - class ChildCmp { - public exp: any; - } - - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - fixture.detectChanges(); - engine.flush(); - resetLog(); - - const cmp = fixture.componentInstance; - const childCmp = cmp.childCmp; - - cmp.exp = 1; - childCmp.exp = 1; - fixture.detectChanges(); - engine.flush(); - - // we have 2 players, but the child is not used even though - // it is created. - const players = getLog(); - expect(players.length).toEqual(2); - expect(engine.players.length).toEqual(1); - - expect((engine.players[0] as TransitionAnimationPlayer).getRealPlayer()).toBe(players[1]); - }); - - it('should fire and synchronize the start/done callbacks on sub triggers even if they are not allowed to animate within the animation', - fakeAsync(() => { - @Component({ - selector: 'parent-cmp', - animations: [ - trigger( - 'parent', - [ - transition( - '* => go', - [ - style({height: '0px'}), - animate(1000, style({height: '100px'})), - ]), - ]), - ], - template: ` - <div *ngIf="!remove" - [@parent]="exp" - (@parent.start)="track($event)" - (@parent.done)="track($event)"> - <child-cmp #child></child-cmp> - </div> - ` - }) - class ParentCmp { - @ViewChild('child') public childCmp: any; - - public exp: any; - public log: string[] = []; - public remove = false; - - track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); } - } - - @Component({ - selector: 'child-cmp', - animations: [ - trigger( - 'child', - [ - transition( - '* => go', - [ - style({width: '0px'}), - animate(1000, style({width: '100px'})), - ]), - ]), - ], - template: ` - <div [@child]="exp" - (@child.start)="track($event)" - (@child.done)="track($event)"></div> - ` - }) - class ChildCmp { - public exp: any; - public log: string[] = []; - track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); } - } - - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - fixture.detectChanges(); - flushMicrotasks(); - - const cmp = fixture.componentInstance; - const child = cmp.childCmp; - - expect(cmp.log).toEqual(['parent-start', 'parent-done']); - expect(child.log).toEqual(['child-start', 'child-done']); - - cmp.log = []; - child.log = []; - cmp.exp = 'go'; - cmp.childCmp.exp = 'go'; - fixture.detectChanges(); - flushMicrotasks(); - - expect(cmp.log).toEqual(['parent-start']); - expect(child.log).toEqual(['child-start']); - - const players = engine.players; - expect(players.length).toEqual(1); - players[0].finish(); - - expect(cmp.log).toEqual(['parent-start', 'parent-done']); - expect(child.log).toEqual(['child-start', 'child-done']); - - cmp.log = []; - child.log = []; - cmp.remove = true; - fixture.detectChanges(); - flushMicrotasks(); - - expect(cmp.log).toEqual(['parent-start', 'parent-done']); - expect(child.log).toEqual(['child-start', 'child-done']); - })); - - it('should fire and synchronize the start/done callbacks on multiple blocked sub triggers', - fakeAsync(() => { - @Component({ - selector: 'cmp', - animations: [ - trigger( - 'parent1', - [ - transition( - '* => go, * => go-again', - [ - style({opacity: 0}), - animate('1s', style({opacity: 1})), - ]), - ]), - trigger( - 'parent2', - [ - transition( - '* => go, * => go-again', - [ - style({lineHeight: '0px'}), - animate('1s', style({lineHeight: '10px'})), - ]), - ]), - trigger( - 'child1', - [ - transition( - '* => go, * => go-again', - [ - style({width: '0px'}), - animate('1s', style({width: '100px'})), - ]), - ]), - trigger( - 'child2', - [ - transition( - '* => go, * => go-again', - [ - style({height: '0px'}), - animate('1s', style({height: '100px'})), - ]), - ]), - ], - template: ` - <div [@parent1]="parent1Exp" (@parent1.start)="track($event)" - [@parent2]="parent2Exp" (@parent2.start)="track($event)"> - <div [@child1]="child1Exp" (@child1.start)="track($event)" - [@child2]="child2Exp" (@child2.start)="track($event)"></div> - </div> + transition( + '* => *', + [ + query(':leave', [animate('1s', style({opacity: 0}))], {optional: true}), + query(':enter', [animate('1s', style({opacity: 1}))], {optional: true}) + ]) + ])], + template: ` + <div [@pageAnimation]="status"> + <header> + <div *ngIf="!loading" class="title">{{ title }}</div> + <div *ngIf="loading" class="loading">loading...</div> + </header> + <section> + <div class="page"> + <div *ngIf="page1" class="page1"> + <div *ngIf="true">page 1</div> + </div> + <div *ngIf="page2" class="page2"> + <div *ngIf="true">page 2</div> + </div> + </div> + </section> + </div> ` - }) - class Cmp { - public parent1Exp = ''; - public parent2Exp = ''; - public child1Exp = ''; - public child2Exp = ''; - public log: string[] = []; + }) + class Cmp { + get title() { + if (this.page1) { + return 'hello from page1'; + } + return 'greetings from page2'; + } - track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); } - } + page1 = false; + page2 = false; + loading = false; - TestBed.configureTestingModule({declarations: [Cmp]}); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - fixture.detectChanges(); - flushMicrotasks(); + get status() { + if (this.loading) return 'loading'; + if (this.page1) return 'page1'; + if (this.page2) return 'page2'; + return ''; + } + } - const cmp = fixture.componentInstance; - cmp.log = []; - cmp.parent1Exp = 'go'; - cmp.parent2Exp = 'go'; - cmp.child1Exp = 'go'; - cmp.child2Exp = 'go'; - fixture.detectChanges(); - flushMicrotasks(); + TestBed.configureTestingModule({declarations: [Cmp]}); - expect(cmp.log).toEqual( - ['parent1-start', 'parent2-start', 'child1-start', 'child2-start']); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.loading = true; + fixture.detectChanges(); + engine.flush(); - cmp.parent1Exp = 'go-again'; - cmp.parent2Exp = 'go-again'; - cmp.child1Exp = 'go-again'; - cmp.child2Exp = 'go-again'; - fixture.detectChanges(); - flushMicrotasks(); - })); + let players = getLog(); + resetLog(); + cancelAllPlayers(players); - it('should stretch the starting keyframe of a child animation queries are issued by the parent', - () => { - @Component({ - selector: 'parent-cmp', - animations: [trigger( - 'parent', - [transition( - '* => *', - [animate(1000, style({color: 'red'})), query('@child', animateChild())])])], - template: '<div [@parent]="exp"><child-cmp #child></child-cmp></div>' - }) - class ParentCmp { - public exp: any; + cmp.page1 = true; + cmp.loading = false; + fixture.detectChanges(); + engine.flush(); - @ViewChild('child') public childCmp: any; - } + let p1: MockAnimationPlayer; + let p2: MockAnimationPlayer; + let p3: MockAnimationPlayer; - @Component({ - selector: 'child-cmp', - animations: [trigger( - 'child', - [transition( - '* => *', [style({color: 'blue'}), animate(1000, style({color: 'red'}))])])], - template: '<div [@child]="exp" class="child"></div>' - }) - class ChildCmp { - public exp: any; - } + players = getLog(); + expect(players.length).toEqual(3); + [p1, p2, p3] = players; - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + expect(p1.element.classList.contains('loading')).toBe(true); + expect(p2.element.classList.contains('title')).toBe(true); + expect(p3.element.classList.contains('page1')).toBe(true); - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - fixture.detectChanges(); - engine.flush(); - resetLog(); + resetLog(); + cancelAllPlayers(players); - const cmp = fixture.componentInstance; - const childCmp = cmp.childCmp; + cmp.page1 = false; + cmp.loading = true; + fixture.detectChanges(); - cmp.exp = 1; - childCmp.exp = 1; - fixture.detectChanges(); - engine.flush(); - - expect(engine.players.length).toEqual(1); // child player, parent cover, parent player - const groupPlayer = (engine.players[0] as TransitionAnimationPlayer) - .getRealPlayer() as AnimationGroupPlayer; - const childPlayer = groupPlayer.players.find(player => { - if (player instanceof MockAnimationPlayer) { - return matchesElement(player.element, '.child'); - } - return false; - }) as MockAnimationPlayer; + players = getLog(); + cancelAllPlayers(players); - const keyframes = childPlayer.keyframes.map(kf => { - delete kf['offset']; - return kf; - }); + expect(players.length).toEqual(3); + [p1, p2, p3] = players; - expect(keyframes.length).toEqual(3); + expect(p1.element.classList.contains('title')).toBe(true); + expect(p2.element.classList.contains('page1')).toBe(true); + expect(p3.element.classList.contains('loading')).toBe(true); - const [k1, k2, k3] = keyframes; - expect(k1).toEqual(k2); - }); + resetLog(); + cancelAllPlayers(players); - it('should allow a parent trigger to control child triggers across multiple template boundaries even if there are no animations in between', - () => { - @Component({ - selector: 'parent-cmp', - animations: [ - trigger( - 'parentAnimation', - [ - transition( - '* => go', - [ - query(':self, @grandChildAnimation', style({opacity: 0})), - animate(1000, style({opacity: 1})), - query( - '@grandChildAnimation', - [ - animate(1000, style({opacity: 1})), - animateChild(), - ]), - ]), - ]), - ], - template: '<div [@parentAnimation]="exp"><child-cmp #child></child-cmp></div>' - }) - class ParentCmp { - public exp: any; + cmp.page2 = true; + cmp.loading = false; + fixture.detectChanges(); + engine.flush(); - @ViewChild('child') public innerCmp: any; - } + players = getLog(); + expect(players.length).toEqual(3); + [p1, p2, p3] = players; - @Component( - {selector: 'child-cmp', template: '<grandchild-cmp #grandchild></grandchild-cmp>'}) - class ChildCmp { - @ViewChild('grandchild') public innerCmp: any; - } + expect(p1.element.classList.contains('loading')).toBe(true); + expect(p2.element.classList.contains('title')).toBe(true); + expect(p3.element.classList.contains('page2')).toBe(true); + }); - @Component({ - selector: 'grandchild-cmp', - animations: [ - trigger( - 'grandChildAnimation', - [ - transition( - '* => go', - [ - style({width: '0px'}), - animate(1000, style({width: '200px'})), - ]), - ]), - ], - template: '<div [@grandChildAnimation]="exp"></div>' - }) - class GrandChildCmp { - public exp: any; + it('should emulate leave animation callbacks for all sub elements that have leave triggers within the component', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + animations: [ + trigger('parent', []), trigger('child', []), + trigger( + 'childWithAnimation', + [ + transition( + ':leave', + [ + animate(1000, style({background: 'red'})), + ]), + ]) + ], + template: ` + <div data-name="p" class="parent" @parent *ngIf="exp" (@parent.start)="callback($event)" (@parent.done)="callback($event)"> + <div data-name="c1" @child (@child.start)="callback($event)" (@child.done)="callback($event)"></div> + <div data-name="c2" @child (@child.start)="callback($event)" (@child.done)="callback($event)"></div> + <div data-name="c3" @childWithAnimation (@childWithAnimation.start)="callback($event)" (@childWithAnimation.done)="callback($event)"></div> + </div> + ` + }) + class Cmp { + // TODO(issue/24571): remove '!'. + public exp!: boolean; + public log: string[] = []; + callback(event: any) { + this.log.push(event.element.getAttribute('data-name') + '-' + event.phaseName); } + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = true; + fixture.detectChanges(); + flushMicrotasks(); + cmp.log = []; + + cmp.exp = false; + fixture.detectChanges(); + flushMicrotasks(); + expect(cmp.log).toEqual([ + 'c1-start', 'c1-done', 'c2-start', 'c2-done', 'p-start', 'c3-start', 'c3-done', 'p-done' + ]); + })); + + it('should build, but not run sub triggers when a parent animation is scheduled', () => { + @Component({ + selector: 'parent-cmp', + animations: + [trigger('parent', [transition('* => *', [animate(1000, style({opacity: 0}))])])], + template: '<div [@parent]="exp"><child-cmp #child></child-cmp></div>' + }) + class ParentCmp { + public exp: any; + + @ViewChild('child') public childCmp: any; + } + + @Component({ + selector: 'child-cmp', + animations: + [trigger('child', [transition('* => *', [animate(1000, style({color: 'red'}))])])], + template: '<div [@child]="exp"></div>' + }) + class ChildCmp { + public exp: any; + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + fixture.detectChanges(); + engine.flush(); + resetLog(); - TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp, GrandChildCmp]}); + const cmp = fixture.componentInstance; + const childCmp = cmp.childCmp; - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ParentCmp); - fixture.detectChanges(); - engine.flush(); - resetLog(); + cmp.exp = 1; + childCmp.exp = 1; + fixture.detectChanges(); + engine.flush(); - const cmp = fixture.componentInstance; - const grandChildCmp = cmp.innerCmp.innerCmp; + // we have 2 players, but the child is not used even though + // it is created. + const players = getLog(); + expect(players.length).toEqual(2); + expect(engine.players.length).toEqual(1); - cmp.exp = 'go'; - grandChildCmp.exp = 'go'; + expect((engine.players[0] as TransitionAnimationPlayer).getRealPlayer()).toBe(players[1]); + }); - fixture.detectChanges(); - engine.flush(); - const players = getLog(); - expect(players.length).toEqual(5); - const [p1, p2, p3, p4, p5] = players; + it('should fire and synchronize the start/done callbacks on sub triggers even if they are not allowed to animate within the animation', + fakeAsync(() => { + @Component({ + selector: 'parent-cmp', + animations: [ + trigger( + 'parent', + [ + transition( + '* => go', + [ + style({height: '0px'}), + animate(1000, style({height: '100px'})), + ]), + ]), + ], + template: ` + <div *ngIf="!remove" + [@parent]="exp" + (@parent.start)="track($event)" + (@parent.done)="track($event)"> + <child-cmp #child></child-cmp> + </div> + ` + }) + class ParentCmp { + @ViewChild('child') public childCmp: any; - expect(p5.keyframes).toEqual([ - {offset: 0, width: '0px'}, {offset: .67, width: '0px'}, {offset: 1, width: '200px'} - ]); + public exp: any; + public log: string[] = []; + public remove = false; + + track(event: any) { + this.log.push(`${event.triggerName}-${event.phaseName}`); + } + } + + @Component({ + selector: 'child-cmp', + animations: [ + trigger( + 'child', + [ + transition( + '* => go', + [ + style({width: '0px'}), + animate(1000, style({width: '100px'})), + ]), + ]), + ], + template: ` + <div [@child]="exp" + (@child.start)="track($event)" + (@child.done)="track($event)"></div> + ` + }) + class ChildCmp { + public exp: any; + public log: string[] = []; + track(event: any) { + this.log.push(`${event.triggerName}-${event.phaseName}`); + } + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + fixture.detectChanges(); + flushMicrotasks(); + + const cmp = fixture.componentInstance; + const child = cmp.childCmp; + + expect(cmp.log).toEqual(['parent-start', 'parent-done']); + expect(child.log).toEqual(['child-start', 'child-done']); + + cmp.log = []; + child.log = []; + cmp.exp = 'go'; + cmp.childCmp.exp = 'go'; + fixture.detectChanges(); + flushMicrotasks(); + + expect(cmp.log).toEqual(['parent-start']); + expect(child.log).toEqual(['child-start']); + + const players = engine.players; + expect(players.length).toEqual(1); + players[0].finish(); + + expect(cmp.log).toEqual(['parent-start', 'parent-done']); + expect(child.log).toEqual(['child-start', 'child-done']); + + cmp.log = []; + child.log = []; + cmp.remove = true; + fixture.detectChanges(); + flushMicrotasks(); + + expect(cmp.log).toEqual(['parent-start', 'parent-done']); + expect(child.log).toEqual(['child-start', 'child-done']); + })); + + it('should fire and synchronize the start/done callbacks on multiple blocked sub triggers', + fakeAsync(() => { + @Component({ + selector: 'cmp', + animations: [ + trigger( + 'parent1', + [ + transition( + '* => go, * => go-again', + [ + style({opacity: 0}), + animate('1s', style({opacity: 1})), + ]), + ]), + trigger( + 'parent2', + [ + transition( + '* => go, * => go-again', + [ + style({lineHeight: '0px'}), + animate('1s', style({lineHeight: '10px'})), + ]), + ]), + trigger( + 'child1', + [ + transition( + '* => go, * => go-again', + [ + style({width: '0px'}), + animate('1s', style({width: '100px'})), + ]), + ]), + trigger( + 'child2', + [ + transition( + '* => go, * => go-again', + [ + style({height: '0px'}), + animate('1s', style({height: '100px'})), + ]), + ]), + ], + template: ` + <div [@parent1]="parent1Exp" (@parent1.start)="track($event)" + [@parent2]="parent2Exp" (@parent2.start)="track($event)"> + <div [@child1]="child1Exp" (@child1.start)="track($event)" + [@child2]="child2Exp" (@child2.start)="track($event)"></div> + </div> + ` + }) + class Cmp { + public parent1Exp = ''; + public parent2Exp = ''; + public child1Exp = ''; + public child2Exp = ''; + public log: string[] = []; + + track(event: any) { + this.log.push(`${event.triggerName}-${event.phaseName}`); + } + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); + flushMicrotasks(); + + const cmp = fixture.componentInstance; + cmp.log = []; + cmp.parent1Exp = 'go'; + cmp.parent2Exp = 'go'; + cmp.child1Exp = 'go'; + cmp.child2Exp = 'go'; + fixture.detectChanges(); + flushMicrotasks(); + + expect(cmp.log).toEqual( + ['parent1-start', 'parent2-start', 'child1-start', 'child2-start']); + + cmp.parent1Exp = 'go-again'; + cmp.parent2Exp = 'go-again'; + cmp.child1Exp = 'go-again'; + cmp.child2Exp = 'go-again'; + fixture.detectChanges(); + flushMicrotasks(); + })); + + it('should stretch the starting keyframe of a child animation queries are issued by the parent', + () => { + @Component({ + selector: 'parent-cmp', + animations: [trigger( + 'parent', + [transition( + '* => *', + [animate(1000, style({color: 'red'})), query('@child', animateChild())])])], + template: '<div [@parent]="exp"><child-cmp #child></child-cmp></div>' + }) + class ParentCmp { + public exp: any; + + @ViewChild('child') public childCmp: any; + } + + @Component({ + selector: 'child-cmp', + animations: [trigger( + 'child', + [transition( + '* => *', [style({color: 'blue'}), animate(1000, style({color: 'red'}))])])], + template: '<div [@child]="exp" class="child"></div>' + }) + class ChildCmp { + public exp: any; + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + fixture.detectChanges(); + engine.flush(); + resetLog(); + + const cmp = fixture.componentInstance; + const childCmp = cmp.childCmp; + + cmp.exp = 1; + childCmp.exp = 1; + fixture.detectChanges(); + engine.flush(); + + expect(engine.players.length).toEqual(1); // child player, parent cover, parent player + const groupPlayer = (engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as + AnimationGroupPlayer; + const childPlayer = groupPlayer.players.find(player => { + if (player instanceof MockAnimationPlayer) { + return matchesElement(player.element, '.child'); + } + return false; + }) as MockAnimationPlayer; + + const keyframes = childPlayer.keyframes.map(kf => { + delete kf['offset']; + return kf; }); - it('should scope :enter queries between sub animations', () => { - @Component({ - selector: 'cmp', - animations: [ - trigger( - 'parent', - [ - transition(':enter', group([ - sequence([ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - query(':enter @child', animateChild()), - ])), - ]), - trigger( - 'child', - [ - transition( - ':enter', - [ - query( - ':enter .item', - [style({opacity: 0}), animate(1000, style({opacity: 1}))]), - ]), - ]), - ], - template: ` + expect(keyframes.length).toEqual(3); + + const [k1, k2, k3] = keyframes; + expect(k1).toEqual(k2); + }); + + it('should allow a parent trigger to control child triggers across multiple template boundaries even if there are no animations in between', + () => { + @Component({ + selector: 'parent-cmp', + animations: [ + trigger( + 'parentAnimation', + [ + transition( + '* => go', + [ + query(':self, @grandChildAnimation', style({opacity: 0})), + animate(1000, style({opacity: 1})), + query( + '@grandChildAnimation', + [ + animate(1000, style({opacity: 1})), + animateChild(), + ]), + ]), + ]), + ], + template: '<div [@parentAnimation]="exp"><child-cmp #child></child-cmp></div>' + }) + class ParentCmp { + public exp: any; + + @ViewChild('child') public innerCmp: any; + } + + @Component( + {selector: 'child-cmp', template: '<grandchild-cmp #grandchild></grandchild-cmp>'}) + class ChildCmp { + @ViewChild('grandchild') public innerCmp: any; + } + + @Component({ + selector: 'grandchild-cmp', + animations: [ + trigger( + 'grandChildAnimation', + [ + transition( + '* => go', + [ + style({width: '0px'}), + animate(1000, style({width: '200px'})), + ]), + ]), + ], + template: '<div [@grandChildAnimation]="exp"></div>' + }) + class GrandChildCmp { + public exp: any; + } + + TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp, GrandChildCmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ParentCmp); + fixture.detectChanges(); + engine.flush(); + resetLog(); + + const cmp = fixture.componentInstance; + const grandChildCmp = cmp.innerCmp.innerCmp; + + cmp.exp = 'go'; + grandChildCmp.exp = 'go'; + + fixture.detectChanges(); + engine.flush(); + const players = getLog(); + expect(players.length).toEqual(5); + const [p1, p2, p3, p4, p5] = players; + + expect(p5.keyframes).toEqual([ + {offset: 0, width: '0px'}, {offset: .67, width: '0px'}, {offset: 1, width: '200px'} + ]); + }); + + it('should scope :enter queries between sub animations', () => { + @Component({ + selector: 'cmp', + animations: [ + trigger( + 'parent', + [ + transition(':enter', group([ + sequence([ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + query(':enter @child', animateChild()), + ])), + ]), + trigger( + 'child', + [ + transition( + ':enter', + [ + query( + ':enter .item', + [style({opacity: 0}), animate(1000, style({opacity: 1}))]), + ]), + ]), + ], + template: ` <div @parent *ngIf="exp1" class="container"> <div *ngIf="exp2"> <div @child> @@ -3107,61 +3102,61 @@ import {HostListener} from '../../src/metadata/directives'; </div> </div> ` - }) - class Cmp { - public exp1: any; - public exp2: any; - public exp3: any; - } + }) + class Cmp { + public exp1: any; + public exp2: any; + public exp3: any; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - fixture.detectChanges(); - resetLog(); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); + resetLog(); - const cmp = fixture.componentInstance; - cmp.exp1 = true; - cmp.exp2 = true; - cmp.exp3 = true; - fixture.detectChanges(); + const cmp = fixture.componentInstance; + cmp.exp1 = true; + cmp.exp2 = true; + cmp.exp3 = true; + fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(2); + const players = getLog(); + expect(players.length).toEqual(2); - const [p1, p2] = players; - expect(p1.element.classList.contains('container')).toBeTruthy(); - expect(p2.element.classList.contains('item')).toBeTruthy(); - }); + const [p1, p2] = players; + expect(p1.element.classList.contains('container')).toBeTruthy(); + expect(p2.element.classList.contains('item')).toBeTruthy(); + }); - it('should scope :leave queries between sub animations', () => { - @Component({ - selector: 'cmp', - animations: [ - trigger( - 'parent', - [ - transition(':leave', group([ - sequence([ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - query(':leave @child', animateChild()), - ])), - ]), - trigger( - 'child', - [ - transition( - ':leave', - [ - query( - ':leave .item', - [style({opacity: 0}), animate(1000, style({opacity: 1}))]), - ]), - ]), - ], - template: ` + it('should scope :leave queries between sub animations', () => { + @Component({ + selector: 'cmp', + animations: [ + trigger( + 'parent', + [ + transition(':leave', group([ + sequence([ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + query(':leave @child', animateChild()), + ])), + ]), + trigger( + 'child', + [ + transition( + ':leave', + [ + query( + ':leave .item', + [style({opacity: 0}), animate(1000, style({opacity: 1}))]), + ]), + ]), + ], + template: ` <div @parent *ngIf="exp1" class="container"> <div *ngIf="exp2"> <div @child> @@ -3172,42 +3167,42 @@ import {HostListener} from '../../src/metadata/directives'; </div> </div> ` - }) - class Cmp { - public exp1: any; - public exp2: any; - public exp3: any; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp1 = true; - cmp.exp2 = true; - cmp.exp3 = true; - fixture.detectChanges(); - resetLog(); + }) + class Cmp { + public exp1: any; + public exp2: any; + public exp3: any; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp1 = true; + cmp.exp2 = true; + cmp.exp3 = true; + fixture.detectChanges(); + resetLog(); - cmp.exp1 = false; - fixture.detectChanges(); + cmp.exp1 = false; + fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(2); + const players = getLog(); + expect(players.length).toEqual(2); - const [p1, p2] = players; - expect(p1.element.classList.contains('container')).toBeTruthy(); - expect(p2.element.classList.contains('item')).toBeTruthy(); - }); + const [p1, p2] = players; + expect(p1.element.classList.contains('container')).toBeTruthy(); + expect(p2.element.classList.contains('item')).toBeTruthy(); }); + }); - describe('animation control flags', () => { - describe('[@.disabled]', () => { - it('should allow a parent animation to query and animate inner nodes that are in a disabled region', - () => { - @Component({ - selector: 'some-cmp', - template: ` + describe('animation control flags', () => { + describe('[@.disabled]', () => { + it('should allow a parent animation to query and animate inner nodes that are in a disabled region', + () => { + @Component({ + selector: 'some-cmp', + template: ` <div [@myAnimation]="exp"> <div [@.disabled]="disabledExp"> <div class="header"></div> @@ -3215,101 +3210,101 @@ import {HostListener} from '../../src/metadata/directives'; </div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - query('.header', animate(750, style({opacity: 0}))), - query('.footer', animate(250, style({opacity: 0}))), - ]), - ]), - ] - }) - class Cmp { - exp: any = ''; - disableExp = false; - } + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + query('.header', animate(750, style({opacity: 0}))), + query('.footer', animate(250, style({opacity: 0}))), + ]), + ]), + ] + }) + class Cmp { + exp: any = ''; + disableExp = false; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.disableExp = true; + fixture.detectChanges(); + resetLog(); + + cmp.exp = 'go'; + fixture.detectChanges(); + const players = getLog(); + expect(players.length).toEqual(2); + + const [p1, p2] = players; + expect(p1.duration).toEqual(750); + expect(p1.element.classList.contains('header')).toBeTrue(); + expect(p2.duration).toEqual(250); + expect(p2.element.classList.contains('footer')).toBeTrue(); + }); - TestBed.configureTestingModule({declarations: [Cmp]}); - - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.disableExp = true; - fixture.detectChanges(); - resetLog(); - - cmp.exp = 'go'; - fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(2); - - const [p1, p2] = players; - expect(p1.duration).toEqual(750); - expect(p1.element.classList.contains('header')); - expect(p2.duration).toEqual(250); - expect(p2.element.classList.contains('footer')); - }); - - it('should allow a parent animation to query and animate sub animations that are in a disabled region', - () => { - @Component({ - selector: 'some-cmp', - template: ` + it('should allow a parent animation to query and animate sub animations that are in a disabled region', + () => { + @Component({ + selector: 'some-cmp', + template: ` <div class="parent" [@parentAnimation]="exp"> <div [@.disabled]="disabledExp"> <div class="child" [@childAnimation]="exp"></div> </div> </div> `, - animations: [ - trigger( - 'parentAnimation', - [ - transition( - '* => go', - [ - query('@childAnimation', animateChild()), - animate(1000, style({opacity: 0})) - ]), - ]), - trigger( - 'childAnimation', - [ - transition('* => go', [animate(500, style({opacity: 0}))]), - ]), - ] - }) - class Cmp { - exp: any = ''; - disableExp = false; - } + animations: [ + trigger( + 'parentAnimation', + [ + transition( + '* => go', + [ + query('@childAnimation', animateChild()), + animate(1000, style({opacity: 0})) + ]), + ]), + trigger( + 'childAnimation', + [ + transition('* => go', [animate(500, style({opacity: 0}))]), + ]), + ] + }) + class Cmp { + exp: any = ''; + disableExp = false; + } - TestBed.configureTestingModule({declarations: [Cmp]}); + TestBed.configureTestingModule({declarations: [Cmp]}); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.disableExp = true; - fixture.detectChanges(); - resetLog(); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.disableExp = true; + fixture.detectChanges(); + resetLog(); - cmp.exp = 'go'; - fixture.detectChanges(); + cmp.exp = 'go'; + fixture.detectChanges(); - const players = getLog(); - expect(players.length).toEqual(2); + const players = getLog(); + expect(players.length).toEqual(2); - const [p1, p2] = players; - expect(p1.duration).toEqual(500); - expect(p1.element.classList.contains('child')); - expect(p2.duration).toEqual(1000); - expect(p2.element.classList.contains('parent')); - }); - }); + const [p1, p2] = players; + expect(p1.duration).toEqual(500); + expect(p1.element.classList.contains('child')).toBeTrue(); + expect(p2.duration).toEqual(1000); + expect(p2.element.classList.contains('parent')).toBeTrue(); + }); }); }); +}); })(); function cancelAllPlayers(players: AnimationPlayer[]) { diff --git a/packages/core/test/animation/animation_router_integration_spec.ts b/packages/core/test/animation/animation_router_integration_spec.ts index 9a33ef40bbe37..033a8a3eebdb9 100644 --- a/packages/core/test/animation/animation_router_integration_spec.ts +++ b/packages/core/test/animation/animation_router_integration_spec.ts @@ -10,376 +10,377 @@ import {AnimationDriver, ɵAnimationEngine} from '@angular/animations/browser'; import {TransitionAnimationPlayer} from '@angular/animations/browser/src/render/transition_animation_engine'; import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing'; import {Component, HostBinding} from '@angular/core'; -import {TestBed, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; +import {fakeAsync, flushMicrotasks, TestBed, tick} from '@angular/core/testing'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {ActivatedRoute, Router, RouterOutlet} from '@angular/router'; import {RouterTestingModule} from '@angular/router/testing'; (function() { - // these tests are only mean't to be run within the DOM (for now) - if (isNode) return; - - describe('Animation Router Tests', function() { - function getLog(): MockAnimationPlayer[] { - return MockAnimationDriver.log as MockAnimationPlayer[]; - } - - function resetLog() { MockAnimationDriver.log = []; } - - beforeEach(() => { - resetLog(); - TestBed.configureTestingModule({ - imports: [RouterTestingModule, BrowserAnimationsModule], - providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}] - }); +// these tests are only mean't to be run within the DOM (for now) +if (isNode) return; + +describe('Animation Router Tests', function() { + function getLog(): MockAnimationPlayer[] { + return MockAnimationDriver.log as MockAnimationPlayer[]; + } + + function resetLog() { + MockAnimationDriver.log = []; + } + + beforeEach(() => { + resetLog(); + TestBed.configureTestingModule({ + imports: [RouterTestingModule, BrowserAnimationsModule], + providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}] }); + }); - it('should query the old and new routes via :leave and :enter', fakeAsync(() => { - @Component({ - animations: [ - trigger( - 'routerAnimations', - [ - transition( - 'page1 => page2', - [ - query(':leave', animateChild()), - query(':enter', animateChild()), - ]), - ]), - ], - template: ` + it('should query the old and new routes via :leave and :enter', fakeAsync(() => { + @Component({ + animations: [ + trigger( + 'routerAnimations', + [ + transition( + 'page1 => page2', + [ + query(':leave', animateChild()), + query(':enter', animateChild()), + ]), + ]), + ], + template: ` <div [@routerAnimations]="prepareRouteAnimation(r)"> <router-outlet #r="outlet"></router-outlet> </div> ` - }) - class ContainerCmp { - constructor(public router: Router) {} - - prepareRouteAnimation(r: RouterOutlet) { - const animation = r.activatedRouteData['animation']; - const value = animation ? animation['value'] : null; - return value; - } - } - - @Component({ - selector: 'page1', - template: `page1`, - animations: [ - trigger( - 'page1Animation', - [ - transition( - ':leave', - [ - style({width: '200px'}), - animate(1000, style({width: '0px'})), - ]), - ]), - ] - }) - class Page1Cmp { - @HostBinding('@page1Animation') public doAnimate = true; - } - - @Component({ - selector: 'page2', - template: `page2`, - animations: [ - trigger( - 'page2Animation', - [ - transition( - ':enter', - [ - style({opacity: 0}), - animate(1000, style({opacity: 1})), - ]), - ]), - ] - }) - class Page2Cmp { - @HostBinding('@page2Animation') public doAnimate = true; + }) + class ContainerCmp { + constructor(public router: Router) {} + + prepareRouteAnimation(r: RouterOutlet) { + const animation = r.activatedRouteData['animation']; + const value = animation ? animation['value'] : null; + return value; } - - TestBed.configureTestingModule({ - declarations: [Page1Cmp, Page2Cmp, ContainerCmp], - imports: [RouterTestingModule.withRoutes([ - {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, - {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} - ])] - }); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.router.initialNavigation(); - tick(); - fixture.detectChanges(); - engine.flush(); - - cmp.router.navigateByUrl('/page1'); - tick(); - fixture.detectChanges(); - engine.flush(); - - cmp.router.navigateByUrl('/page2'); - tick(); - fixture.detectChanges(); - engine.flush(); - - const player = engine.players[0] !; - const groupPlayer = - (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; - const players = groupPlayer.players as MockAnimationPlayer[]; - - expect(players.length).toEqual(2); - const [p1, p2] = players; - - expect(p1.duration).toEqual(1000); - expect(p1.keyframes).toEqual([ - {offset: 0, width: '200px'}, - {offset: 1, width: '0px'}, - ]); - - expect(p2.duration).toEqual(2000); - expect(p2.keyframes).toEqual([ - {offset: 0, opacity: '0'}, - {offset: .5, opacity: '0'}, - {offset: 1, opacity: '1'}, - ]); - })); - - it('should allow inner enter animations to be emulated within a routed item', fakeAsync(() => { - @Component({ - animations: [ - trigger( - 'routerAnimations', - [ - transition( - 'page1 => page2', - [ - query(':enter', animateChild()), - ]), - ]), - ], - template: ` + } + + @Component({ + selector: 'page1', + template: `page1`, + animations: [ + trigger( + 'page1Animation', + [ + transition( + ':leave', + [ + style({width: '200px'}), + animate(1000, style({width: '0px'})), + ]), + ]), + ] + }) + class Page1Cmp { + @HostBinding('@page1Animation') public doAnimate = true; + } + + @Component({ + selector: 'page2', + template: `page2`, + animations: [ + trigger( + 'page2Animation', + [ + transition( + ':enter', + [ + style({opacity: 0}), + animate(1000, style({opacity: 1})), + ]), + ]), + ] + }) + class Page2Cmp { + @HostBinding('@page2Animation') public doAnimate = true; + } + + TestBed.configureTestingModule({ + declarations: [Page1Cmp, Page2Cmp, ContainerCmp], + imports: [RouterTestingModule.withRoutes([ + {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, + {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} + ])] + }); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.router.initialNavigation(); + tick(); + fixture.detectChanges(); + engine.flush(); + + cmp.router.navigateByUrl('/page1'); + tick(); + fixture.detectChanges(); + engine.flush(); + + cmp.router.navigateByUrl('/page2'); + tick(); + fixture.detectChanges(); + engine.flush(); + + const player = engine.players[0]!; + const groupPlayer = + (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; + const players = groupPlayer.players as MockAnimationPlayer[]; + + expect(players.length).toEqual(2); + const [p1, p2] = players; + + expect(p1.duration).toEqual(1000); + expect(p1.keyframes).toEqual([ + {offset: 0, width: '200px'}, + {offset: 1, width: '0px'}, + ]); + + expect(p2.duration).toEqual(2000); + expect(p2.keyframes).toEqual([ + {offset: 0, opacity: '0'}, + {offset: .5, opacity: '0'}, + {offset: 1, opacity: '1'}, + ]); + })); + + it('should allow inner enter animations to be emulated within a routed item', fakeAsync(() => { + @Component({ + animations: [ + trigger( + 'routerAnimations', + [ + transition( + 'page1 => page2', + [ + query(':enter', animateChild()), + ]), + ]), + ], + template: ` <div [@routerAnimations]="prepareRouteAnimation(r)"> <router-outlet #r="outlet"></router-outlet> </div> ` - }) - class ContainerCmp { - constructor(public router: Router) {} - - prepareRouteAnimation(r: RouterOutlet) { - const animation = r.activatedRouteData['animation']; - const value = animation ? animation['value'] : null; - return value; - } + }) + class ContainerCmp { + constructor(public router: Router) {} + + prepareRouteAnimation(r: RouterOutlet) { + const animation = r.activatedRouteData['animation']; + const value = animation ? animation['value'] : null; + return value; } + } - @Component({selector: 'page1', template: `page1`, animations: []}) - class Page1Cmp { - } + @Component({selector: 'page1', template: `page1`, animations: []}) + class Page1Cmp { + } - @Component({ - selector: 'page2', - template: ` + @Component({ + selector: 'page2', + template: ` <h1>Page 2</h1> <div *ngIf="exp" class="if-one" @ifAnimation></div> <div *ngIf="exp" class="if-two" @ifAnimation></div> `, - animations: [ - trigger( - 'page2Animation', - [ - transition( - ':enter', - [query('.if-one', animateChild()), query('.if-two', animateChild())]), - ]), - trigger( - 'ifAnimation', - [transition( - ':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]) - ] - }) - class Page2Cmp { - @HostBinding('@page2Animation') public doAnimate = true; - - public exp = true; - } - - TestBed.configureTestingModule({ - declarations: [Page1Cmp, Page2Cmp, ContainerCmp], - imports: [RouterTestingModule.withRoutes([ - {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, - {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} - ])] - }); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.router.initialNavigation(); - tick(); - fixture.detectChanges(); - engine.flush(); - - cmp.router.navigateByUrl('/page1'); - tick(); - fixture.detectChanges(); - engine.flush(); - - cmp.router.navigateByUrl('/page2'); - tick(); - fixture.detectChanges(); - engine.flush(); - - const player = engine.players[0] !; - const groupPlayer = - (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; - const players = groupPlayer.players as MockAnimationPlayer[]; - - expect(players.length).toEqual(2); - const [p1, p2] = players; - - expect(p1.keyframes).toEqual([ - {offset: 0, opacity: '0'}, - {offset: 1, opacity: '1'}, - ]); - - expect(p2.keyframes).toEqual([ - {offset: 0, opacity: '0'}, - {offset: .5, opacity: '0'}, - {offset: 1, opacity: '1'}, - ]); - })); - - it('should allow inner leave animations to be emulated within a routed item', fakeAsync(() => { - @Component({ - animations: [ - trigger( - 'routerAnimations', - [ - transition( - 'page1 => page2', - [ - query(':leave', animateChild()), - ]), - ]), - ], - template: ` + animations: [ + trigger( + 'page2Animation', + [ + transition( + ':enter', + [query('.if-one', animateChild()), query('.if-two', animateChild())]), + ]), + trigger( + 'ifAnimation', + [transition(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]) + ] + }) + class Page2Cmp { + @HostBinding('@page2Animation') public doAnimate = true; + + public exp = true; + } + + TestBed.configureTestingModule({ + declarations: [Page1Cmp, Page2Cmp, ContainerCmp], + imports: [RouterTestingModule.withRoutes([ + {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, + {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} + ])] + }); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.router.initialNavigation(); + tick(); + fixture.detectChanges(); + engine.flush(); + + cmp.router.navigateByUrl('/page1'); + tick(); + fixture.detectChanges(); + engine.flush(); + + cmp.router.navigateByUrl('/page2'); + tick(); + fixture.detectChanges(); + engine.flush(); + + const player = engine.players[0]!; + const groupPlayer = + (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; + const players = groupPlayer.players as MockAnimationPlayer[]; + + expect(players.length).toEqual(2); + const [p1, p2] = players; + + expect(p1.keyframes).toEqual([ + {offset: 0, opacity: '0'}, + {offset: 1, opacity: '1'}, + ]); + + expect(p2.keyframes).toEqual([ + {offset: 0, opacity: '0'}, + {offset: .5, opacity: '0'}, + {offset: 1, opacity: '1'}, + ]); + })); + + it('should allow inner leave animations to be emulated within a routed item', fakeAsync(() => { + @Component({ + animations: [ + trigger( + 'routerAnimations', + [ + transition( + 'page1 => page2', + [ + query(':leave', animateChild()), + ]), + ]), + ], + template: ` <div [@routerAnimations]="prepareRouteAnimation(r)"> <router-outlet #r="outlet"></router-outlet> </div> ` - }) - class ContainerCmp { - constructor(public router: Router) {} - - prepareRouteAnimation(r: RouterOutlet) { - const animation = r.activatedRouteData['animation']; - const value = animation ? animation['value'] : null; - return value; - } + }) + class ContainerCmp { + constructor(public router: Router) {} + + prepareRouteAnimation(r: RouterOutlet) { + const animation = r.activatedRouteData['animation']; + const value = animation ? animation['value'] : null; + return value; } + } - @Component({ - selector: 'page1', - template: ` + @Component({ + selector: 'page1', + template: ` <h1>Page 1</h1> <div *ngIf="exp" class="if-one" @ifAnimation></div> <div *ngIf="exp" class="if-two" @ifAnimation></div> `, - animations: [ - trigger( - 'page1Animation', - [ - transition( - ':leave', - [query('.if-one', animateChild()), query('.if-two', animateChild())]), - ]), - trigger( - 'ifAnimation', - [transition(':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])]), - ] - }) - class Page1Cmp { - @HostBinding('@page1Animation') public doAnimate = true; - - public exp = true; - } - - @Component({selector: 'page2', template: `page2`, animations: []}) - class Page2Cmp { - } - - TestBed.configureTestingModule({ - declarations: [Page1Cmp, Page2Cmp, ContainerCmp], - imports: [RouterTestingModule.withRoutes([ - {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, - {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} - ])] - }); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.router.initialNavigation(); - tick(); - fixture.detectChanges(); - engine.flush(); - - cmp.router.navigateByUrl('/page1'); - tick(); - fixture.detectChanges(); - engine.flush(); - - cmp.router.navigateByUrl('/page2'); - tick(); - fixture.detectChanges(); - engine.flush(); - - const player = engine.players[0] !; - const groupPlayer = - (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; - const players = groupPlayer.players as MockAnimationPlayer[]; - - expect(players.length).toEqual(2); - const [p1, p2] = players; - - expect(p1.keyframes).toEqual([ - {offset: 0, opacity: '1'}, - {offset: 1, opacity: '0'}, - ]); - - expect(p2.keyframes).toEqual([ - {offset: 0, opacity: '1'}, - {offset: .5, opacity: '1'}, - {offset: 1, opacity: '0'}, - ]); - })); - - it('should properly collect :enter / :leave router nodes even when another non-router *template component is within the trigger boundaries', - fakeAsync(() => { - @Component({ - selector: 'ani-cmp', - animations: [ - trigger( - 'pageAnimation', - [ - transition( - 'page1 => page2', - [ - query('.router-container :leave', animate('1s', style({opacity: 0}))), - query('.router-container :enter', animate('1s', style({opacity: 1}))), - ]), - ]), - ], - template: ` + animations: [ + trigger( + 'page1Animation', + [ + transition( + ':leave', + [query('.if-one', animateChild()), query('.if-two', animateChild())]), + ]), + trigger( + 'ifAnimation', + [transition(':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])]), + ] + }) + class Page1Cmp { + @HostBinding('@page1Animation') public doAnimate = true; + + public exp = true; + } + + @Component({selector: 'page2', template: `page2`, animations: []}) + class Page2Cmp { + } + + TestBed.configureTestingModule({ + declarations: [Page1Cmp, Page2Cmp, ContainerCmp], + imports: [RouterTestingModule.withRoutes([ + {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, + {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} + ])] + }); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.router.initialNavigation(); + tick(); + fixture.detectChanges(); + engine.flush(); + + cmp.router.navigateByUrl('/page1'); + tick(); + fixture.detectChanges(); + engine.flush(); + + cmp.router.navigateByUrl('/page2'); + tick(); + fixture.detectChanges(); + engine.flush(); + + const player = engine.players[0]!; + const groupPlayer = + (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; + const players = groupPlayer.players as MockAnimationPlayer[]; + + expect(players.length).toEqual(2); + const [p1, p2] = players; + + expect(p1.keyframes).toEqual([ + {offset: 0, opacity: '1'}, + {offset: 1, opacity: '0'}, + ]); + + expect(p2.keyframes).toEqual([ + {offset: 0, opacity: '1'}, + {offset: .5, opacity: '1'}, + {offset: 1, opacity: '0'}, + ]); + })); + + it('should properly collect :enter / :leave router nodes even when another non-router *template component is within the trigger boundaries', + fakeAsync(() => { + @Component({ + selector: 'ani-cmp', + animations: [ + trigger( + 'pageAnimation', + [ + transition( + 'page1 => page2', + [ + query('.router-container :leave', animate('1s', style({opacity: 0}))), + query('.router-container :enter', animate('1s', style({opacity: 1}))), + ]), + ]), + ], + template: ` <div [@pageAnimation]="prepRoute(outlet)"> <header> <div class="inner"> @@ -392,138 +393,144 @@ import {RouterTestingModule} from '@angular/router/testing'; </section> </div> ` - }) - class ContainerCmp { - loading = false; + }) + class ContainerCmp { + loading = false; - constructor(public router: Router) {} - - prepRoute(outlet: any) { return outlet.activatedRouteData['animation']; } - } + constructor(public router: Router) {} - @Component({selector: 'page1', template: `page1`}) - class Page1Cmp { + prepRoute(outlet: any) { + return outlet.activatedRouteData['animation']; } - - @Component({selector: 'page2', template: `page2`}) - class Page2Cmp { + } + + @Component({selector: 'page1', template: `page1`}) + class Page1Cmp { + } + + @Component({selector: 'page2', template: `page2`}) + class Page2Cmp { + } + + TestBed.configureTestingModule({ + declarations: [Page1Cmp, Page2Cmp, ContainerCmp], + imports: [RouterTestingModule.withRoutes([ + {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, + {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} + ])] + }); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.router.initialNavigation(); + tick(); + fixture.detectChanges(); + engine.flush(); + + cmp.router.navigateByUrl('/page1'); + tick(); + cmp.loading = true; + fixture.detectChanges(); + engine.flush(); + + cmp.router.navigateByUrl('/page2'); + tick(); + cmp.loading = false; + fixture.detectChanges(); + engine.flush(); + + const players = engine.players; + expect(players.length).toEqual(1); + const [p1] = players; + + const innerPlayers = + ((p1 as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer).players; + expect(innerPlayers.length).toEqual(2); + + const [ip1, ip2] = innerPlayers as any; + expect(ip1.element.innerText).toEqual('page1'); + expect(ip2.element.innerText).toEqual('page2'); + })); + + it('should allow a recursive set of :leave animations to occur for nested routes', + fakeAsync(() => { + @Component({selector: 'ani-cmp', template: '<router-outlet name="recur"></router-outlet>'}) + class ContainerCmp { + constructor(private _router: Router) {} + log: string[] = []; + + enter() { + this._router.navigateByUrl('/(recur:recur/nested)'); } - TestBed.configureTestingModule({ - declarations: [Page1Cmp, Page2Cmp, ContainerCmp], - imports: [RouterTestingModule.withRoutes([ - {path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')}, - {path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')} - ])] - }); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.router.initialNavigation(); - tick(); - fixture.detectChanges(); - engine.flush(); - - cmp.router.navigateByUrl('/page1'); - tick(); - cmp.loading = true; - fixture.detectChanges(); - engine.flush(); - - cmp.router.navigateByUrl('/page2'); - tick(); - cmp.loading = false; - fixture.detectChanges(); - engine.flush(); - - const players = engine.players; - expect(players.length).toEqual(1); - const [p1] = players; - - const innerPlayers = - ((p1 as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer).players; - expect(innerPlayers.length).toEqual(2); - - const [ip1, ip2] = innerPlayers as any; - expect(ip1.element.innerText).toEqual('page1'); - expect(ip2.element.innerText).toEqual('page2'); - })); - - it('should allow a recursive set of :leave animations to occur for nested routes', - fakeAsync(() => { - @Component({selector: 'ani-cmp', template: '<router-outlet name="recur"></router-outlet>'}) - class ContainerCmp { - constructor(private _router: Router) {} - log: string[] = []; - - enter() { this._router.navigateByUrl('/(recur:recur/nested)'); } - - leave() { this._router.navigateByUrl('/'); } + leave() { + this._router.navigateByUrl('/'); } - - @Component({ - selector: 'recur-page', - template: 'Depth: {{ depth }} \n <router-outlet></router-outlet>', - animations: [ - trigger( - 'pageAnimations', - [ - transition(':leave', [group([ - sequence([style({opacity: 1}), animate('1s', style({opacity: 0}))]), - query('@*', animateChild(), {optional: true}) - ])]), - ]), - ] - }) - class RecurPageCmp { - @HostBinding('@pageAnimations') public animatePage = true; - - @HostBinding('attr.data-depth') public depth = 0; - - constructor(private container: ContainerCmp, private route: ActivatedRoute) { - this.route.data.subscribe(data => { - this.container.log.push(`DEPTH ${data.depth}`); - this.depth = data.depth; - }); - } + } + + @Component({ + selector: 'recur-page', + template: 'Depth: {{ depth }} \n <router-outlet></router-outlet>', + animations: [ + trigger( + 'pageAnimations', + [ + transition(':leave', [group([ + sequence([style({opacity: 1}), animate('1s', style({opacity: 0}))]), + query('@*', animateChild(), {optional: true}) + ])]), + ]), + ] + }) + class RecurPageCmp { + @HostBinding('@pageAnimations') public animatePage = true; + + @HostBinding('attr.data-depth') public depth = 0; + + constructor(private container: ContainerCmp, private route: ActivatedRoute) { + this.route.data.subscribe(data => { + this.container.log.push(`DEPTH ${data.depth}`); + this.depth = data.depth; + }); } - - TestBed.configureTestingModule({ - declarations: [ContainerCmp, RecurPageCmp], - imports: [RouterTestingModule.withRoutes([{ - path: 'recur', - component: RecurPageCmp, - outlet: 'recur', - data: {depth: 0}, - children: [{path: 'nested', component: RecurPageCmp, data: {depth: 1}}] - }])] - }); - - const fixture = TestBed.createComponent(ContainerCmp); - const cmp = fixture.componentInstance; - cmp.enter(); - tick(); - fixture.detectChanges(); - flushMicrotasks(); - - expect(cmp.log).toEqual([ - 'DEPTH 0', - 'DEPTH 1', - ]); - - cmp.leave(); - tick(); - fixture.detectChanges(); - - const players = getLog(); - expect(players.length).toEqual(2); - - const [p1, p2] = players; - expect(p1.element.getAttribute('data-depth')).toEqual('0'); - expect(p2.element.getAttribute('data-depth')).toEqual('1'); - })); - }); + } + + TestBed.configureTestingModule({ + declarations: [ContainerCmp, RecurPageCmp], + imports: [RouterTestingModule.withRoutes([{ + path: 'recur', + component: RecurPageCmp, + outlet: 'recur', + data: {depth: 0}, + children: [{path: 'nested', component: RecurPageCmp, data: {depth: 1}}] + }])] + }); + + const fixture = TestBed.createComponent(ContainerCmp); + const cmp = fixture.componentInstance; + cmp.enter(); + tick(); + fixture.detectChanges(); + flushMicrotasks(); + + expect(cmp.log).toEqual([ + 'DEPTH 0', + 'DEPTH 1', + ]); + + cmp.leave(); + tick(); + fixture.detectChanges(); + + const players = getLog(); + expect(players.length).toEqual(2); + + const [p1, p2] = players; + expect(p1.element.getAttribute('data-depth')).toEqual('0'); + expect(p2.element.getAttribute('data-depth')).toEqual('1'); + })); +}); }); function makeAnimationData(value: string, params: {[key: string]: any} = {}): {[key: string]: any} { diff --git a/packages/core/test/animation/animations_with_css_keyframes_animations_integration_spec.ts b/packages/core/test/animation/animations_with_css_keyframes_animations_integration_spec.ts index 9590b533449ac..5bb6822b049e0 100644 --- a/packages/core/test/animation/animations_with_css_keyframes_animations_integration_spec.ts +++ b/packages/core/test/animation/animations_with_css_keyframes_animations_integration_spec.ts @@ -15,23 +15,22 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut import {TestBed} from '../../testing'; (function() { - // these tests are only mean't to be run within the DOM (for now) - // Buggy in Chromium 39, see https://github.com/angular/angular/issues/15793 - if (isNode) return; - - describe('animation integration tests using css keyframe animations', function() { - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [{provide: AnimationDriver, useClass: CssKeyframesDriver}], - imports: [BrowserAnimationsModule] - }); +// these tests are only mean't to be run within the DOM (for now) +// Buggy in Chromium 39, see https://github.com/angular/angular/issues/15793 +if (isNode) return; + +describe('animation integration tests using css keyframe animations', function() { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{provide: AnimationDriver, useClass: CssKeyframesDriver}], + imports: [BrowserAnimationsModule] }); + }); - it('should compute (*) animation styles for a container that is being removed', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should compute (*) animation styles for a container that is being removed', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div @auto *ngIf="exp"> <div style="line-height:20px;">1</div> <div style="line-height:20px;">2</div> @@ -40,46 +39,46 @@ import {TestBed} from '../../testing'; <div style="line-height:20px;">5</div> </div> `, - animations: [trigger( - 'auto', - [ - state('void', style({height: '0px'})), - state('*', style({height: '*'})), - transition('* => *', animate(1000)), - ])] - }) - class Cmp { - public exp: boolean = false; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(AnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = true; - fixture.detectChanges(); - - expect(engine.players.length).toEqual(1); - let player = getPlayer(engine) as CssKeyframesPlayer; - expect(player.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]); - - player.finish(); - if (browserDetection.isOldChrome) return; - - cmp.exp = false; - fixture.detectChanges(); - - player = getPlayer(engine) as CssKeyframesPlayer; - expect(player.keyframes).toEqual([{height: '100px', offset: 0}, {height: '0px', offset: 1}]); - }); + animations: [trigger( + 'auto', + [ + state('void', style({height: '0px'})), + state('*', style({height: '*'})), + transition('* => *', animate(1000)), + ])] + }) + class Cmp { + public exp: boolean = false; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(AnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = true; + fixture.detectChanges(); + + expect(engine.players.length).toEqual(1); + let player = getPlayer(engine) as CssKeyframesPlayer; + expect(player.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]); + + player.finish(); + if (browserDetection.isOldChrome) return; + + cmp.exp = false; + fixture.detectChanges(); + + player = getPlayer(engine) as CssKeyframesPlayer; + expect(player.keyframes).toEqual([{height: '100px', offset: 0}, {height: '0px', offset: 1}]); + }); - it('should cleanup all existing @keyframe <style> objects after the animation has finished', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should cleanup all existing @keyframe <style> objects after the animation has finished', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="myAnimationExp"> <div>1</div> <div>2</div> @@ -88,278 +87,278 @@ import {TestBed} from '../../testing'; <div>5</div> </div> `, - animations: [trigger( - 'myAnimation', - [ - transition( - '* => go', - [ - query( - 'div', - [ - style({opacity: 0}), - animate('1s', style({opacity: 0})), - ]), - ]), - ])] - }) - class Cmp { - public myAnimationExp = ''; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(AnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.myAnimationExp = 'go'; - fixture.detectChanges(); - - const webPlayer = <AnimationGroupPlayer>getPlayer(engine); - const players = webPlayer.players as CssKeyframesPlayer[]; - expect(players.length).toEqual(5); - - const head = document.querySelector('head') !; - const sheets: any[] = []; - for (let i = 0; i < 5; i++) { - const sheet = findStyleObjectWithKeyframes(i); - expect(head.contains(sheet)).toBeTruthy(); - sheets.push(sheet); - } - - cmp.myAnimationExp = 'go-back'; - fixture.detectChanges(); - - for (let i = 0; i < 5; i++) { - expect(head.contains(sheets[i])).toBeFalsy(); - } - }); - - it('should properly handle easing values that are apart of the sequence', () => { - @Component({ - selector: 'ani-cmp', - template: ` + animations: [trigger( + 'myAnimation', + [ + transition( + '* => go', + [ + query( + 'div', + [ + style({opacity: 0}), + animate('1s', style({opacity: 0})), + ]), + ]), + ])] + }) + class Cmp { + public myAnimationExp = ''; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(AnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.myAnimationExp = 'go'; + fixture.detectChanges(); + + const webPlayer = <AnimationGroupPlayer>getPlayer(engine); + const players = webPlayer.players as CssKeyframesPlayer[]; + expect(players.length).toEqual(5); + + const head = document.querySelector('head')!; + const sheets: any[] = []; + for (let i = 0; i < 5; i++) { + const sheet = findStyleObjectWithKeyframes(i); + expect(head.contains(sheet)).toBeTruthy(); + sheets.push(sheet); + } + + cmp.myAnimationExp = 'go-back'; + fixture.detectChanges(); + + for (let i = 0; i < 5; i++) { + expect(head.contains(sheets[i])).toBeFalsy(); + } + }); + + it('should properly handle easing values that are apart of the sequence', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #elm [@myAnimation]="myAnimationExp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => goSteps', - [ - style({opacity: 0}), - animate('1s ease-out', style({opacity: 1})), - ]), - transition( - '* => goKeyframes', - [ - animate('1s cubic-bezier(0.5, 1, 0.5, 1)', keyframes([ - style({opacity: 0}), - style({opacity: 0.5}), - style({opacity: 1}), - ])), - ]), - ]), - ] - }) - class Cmp { - @ViewChild('elm') public element: any; - - public myAnimationExp = ''; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(AnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.myAnimationExp = 'goSteps'; - fixture.detectChanges(); - - let kfElm = findStyleObjectWithKeyframes(); - const [r1, r2] = kfElm.sheet.cssRules[0].cssRules; - assertEasing(r1, 'ease-out'); - assertEasing(r2, ''); - - const element = cmp.element.nativeElement; - - const webPlayer = getPlayer(engine); - cmp.myAnimationExp = 'goKeyframes'; - fixture.detectChanges(); - - assertEasing(element, 'cubic-bezier(0.5,1,0.5,1)'); - }); + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => goSteps', + [ + style({opacity: 0}), + animate('1s ease-out', style({opacity: 1})), + ]), + transition( + '* => goKeyframes', + [ + animate('1s cubic-bezier(0.5, 1, 0.5, 1)', keyframes([ + style({opacity: 0}), + style({opacity: 0.5}), + style({opacity: 1}), + ])), + ]), + ]), + ] + }) + class Cmp { + @ViewChild('elm') public element: any; + + public myAnimationExp = ''; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(AnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.myAnimationExp = 'goSteps'; + fixture.detectChanges(); + + let kfElm = findStyleObjectWithKeyframes(); + const [r1, r2] = kfElm.sheet.cssRules[0].cssRules; + assertEasing(r1, 'ease-out'); + assertEasing(r2, ''); + + const element = cmp.element.nativeElement; + + const webPlayer = getPlayer(engine); + cmp.myAnimationExp = 'goKeyframes'; + fixture.detectChanges(); + + assertEasing(element, 'cubic-bezier(0.5,1,0.5,1)'); + }); - it('should restore existing style values once the animation completes', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should restore existing style values once the animation completes', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #elm [@myAnimation]="myAnimationExp"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - state('go', style({width: '200px'})), - transition( - '* => go', - [ - style({height: '100px', width: '100px'}), group([ - animate('1s', style({height: '200px'})), - animate('1s', style({width: '200px'})) - ]) - ]), - ]), - ] - }) - class Cmp { - @ViewChild('elm') public element: any; - - public myAnimationExp = ''; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(AnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - fixture.detectChanges(); - const element = cmp.element.nativeElement; - element.style['width'] = '50px'; - element.style['height'] = '50px'; - - assertStyle(element, 'width', '50px'); - assertStyle(element, 'height', '50px'); - - cmp.myAnimationExp = 'go'; - fixture.detectChanges(); - - const player = getPlayer(engine); - - assertStyle(element, 'width', '100px'); - assertStyle(element, 'height', '100px'); - - player.finish(); - - assertStyle(element, 'width', '200px'); - assertStyle(element, 'height', '50px'); - }); + animations: [ + trigger( + 'myAnimation', + [ + state('go', style({width: '200px'})), + transition( + '* => go', + [ + style({height: '100px', width: '100px'}), group([ + animate('1s', style({height: '200px'})), + animate('1s', style({width: '200px'})) + ]) + ]), + ]), + ] + }) + class Cmp { + @ViewChild('elm') public element: any; + + public myAnimationExp = ''; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(AnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + fixture.detectChanges(); + const element = cmp.element.nativeElement; + element.style['width'] = '50px'; + element.style['height'] = '50px'; + + assertStyle(element, 'width', '50px'); + assertStyle(element, 'height', '50px'); + + cmp.myAnimationExp = 'go'; + fixture.detectChanges(); + + const player = getPlayer(engine); + + assertStyle(element, 'width', '100px'); + assertStyle(element, 'height', '100px'); + + player.finish(); + + assertStyle(element, 'width', '200px'); + assertStyle(element, 'height', '50px'); + }); - it('should clean up 0 second animation styles (queried styles) that contain camel casing when complete', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should clean up 0 second animation styles (queried styles) that contain camel casing when complete', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #elm [@myAnimation]="myAnimationExp"> <div class="foo"></div> <div class="bar"></div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - state('go', style({width: '200px'})), - transition( - '* => go', - [ - query('.foo', [style({maxHeight: '0px'})]), - query( - '.bar', - [ - style({width: '0px'}), - animate('1s', style({width: '100px'})), - ]), - ]), - ]), - ] - }) - class Cmp { - @ViewChild('elm', {static: true}) public element: any; - - public myAnimationExp = ''; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(AnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - const elm = cmp.element.nativeElement; - const foo = elm.querySelector('.foo') as HTMLElement; - - cmp.myAnimationExp = 'go'; - fixture.detectChanges(); - - expect(foo.style.getPropertyValue('max-height')).toEqual('0px'); - - const player = engine.players.pop() !; - player.finish(); - - expect(foo.style.getPropertyValue('max-height')).toBeFalsy(); - }); - - it('should apply the `display` and `position` styles as regular inline styles for the duration of the animation', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + animations: [ + trigger( + 'myAnimation', + [ + state('go', style({width: '200px'})), + transition( + '* => go', + [ + query('.foo', [style({maxHeight: '0px'})]), + query( + '.bar', + [ + style({width: '0px'}), + animate('1s', style({width: '100px'})), + ]), + ]), + ]), + ] + }) + class Cmp { + @ViewChild('elm', {static: true}) public element: any; + + public myAnimationExp = ''; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(AnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + const elm = cmp.element.nativeElement; + const foo = elm.querySelector('.foo') as HTMLElement; + + cmp.myAnimationExp = 'go'; + fixture.detectChanges(); + + expect(foo.style.getPropertyValue('max-height')).toEqual('0px'); + + const player = engine.players.pop()!; + player.finish(); + + expect(foo.style.getPropertyValue('max-height')).toBeFalsy(); + }); + + it('should apply the `display` and `position` styles as regular inline styles for the duration of the animation', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #elm [@myAnimation]="myAnimationExp" style="display:table; position:fixed"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - state('go', style({display: 'inline-block'})), - transition( - '* => go', - [ - style({display: 'inline', position: 'absolute', opacity: 0}), - animate('1s', style({display: 'inline', opacity: 1, position: 'static'})), - animate('1s', style({display: 'flexbox', opacity: 0})), - ]) - ]), - ] - }) - class Cmp { - @ViewChild('elm', {static: true}) public element: any; - - public myAnimationExp = ''; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(AnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - const elm = cmp.element.nativeElement; - expect(elm.style.getPropertyValue('display')).toEqual('table'); - expect(elm.style.getPropertyValue('position')).toEqual('fixed'); - - cmp.myAnimationExp = 'go'; - fixture.detectChanges(); - - expect(elm.style.getPropertyValue('display')).toEqual('inline'); - expect(elm.style.getPropertyValue('position')).toEqual('absolute'); - - const player = engine.players.pop() !; - player.finish(); - player.destroy(); - - expect(elm.style.getPropertyValue('display')).toEqual('inline-block'); - expect(elm.style.getPropertyValue('position')).toEqual('fixed'); - }); - }); + animations: [ + trigger( + 'myAnimation', + [ + state('go', style({display: 'inline-block'})), + transition( + '* => go', + [ + style({display: 'inline', position: 'absolute', opacity: 0}), + animate('1s', style({display: 'inline', opacity: 1, position: 'static'})), + animate('1s', style({display: 'flexbox', opacity: 0})), + ]) + ]), + ] + }) + class Cmp { + @ViewChild('elm', {static: true}) public element: any; + + public myAnimationExp = ''; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(AnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + const elm = cmp.element.nativeElement; + expect(elm.style.getPropertyValue('display')).toEqual('table'); + expect(elm.style.getPropertyValue('position')).toEqual('fixed'); + + cmp.myAnimationExp = 'go'; + fixture.detectChanges(); + + expect(elm.style.getPropertyValue('display')).toEqual('inline'); + expect(elm.style.getPropertyValue('position')).toEqual('absolute'); + + const player = engine.players.pop()!; + player.finish(); + player.destroy(); + + expect(elm.style.getPropertyValue('display')).toEqual('inline-block'); + expect(elm.style.getPropertyValue('position')).toEqual('fixed'); + }); +}); })(); function getPlayer(engine: AnimationEngine, index = 0) { - return (engine.players[index] as any) !.getRealPlayer(); + return (engine.players[index] as any)!.getRealPlayer(); } function findStyleObjectWithKeyframes(index?: number): any|null { diff --git a/packages/core/test/animation/animations_with_web_animations_integration_spec.ts b/packages/core/test/animation/animations_with_web_animations_integration_spec.ts index eee0c9307b2a7..dbda33c59e6e9 100644 --- a/packages/core/test/animation/animations_with_web_animations_integration_spec.ts +++ b/packages/core/test/animation/animations_with_web_animations_integration_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {animate, query, state, style, transition, trigger} from '@angular/animations'; -import {AnimationDriver, ɵAnimationEngine, ɵWebAnimationsDriver, ɵWebAnimationsPlayer, ɵsupportsWebAnimations} from '@angular/animations/browser'; +import {AnimationDriver, ɵAnimationEngine, ɵsupportsWebAnimations, ɵWebAnimationsDriver, ɵWebAnimationsPlayer} from '@angular/animations/browser'; import {TransitionAnimationPlayer} from '@angular/animations/browser/src/render/transition_animation_engine'; import {AnimationGroupPlayer} from '@angular/animations/src/players/animation_group_player'; import {Component, ViewChild} from '@angular/core'; @@ -15,23 +15,22 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; (function() { - // these tests are only mean't to be run within the DOM (for now) - // Buggy in Chromium 39, see https://github.com/angular/angular/issues/15793 - if (isNode || !ɵsupportsWebAnimations()) return; - - describe('animation integration tests using web animations', function() { - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [{provide: AnimationDriver, useClass: ɵWebAnimationsDriver}], - imports: [BrowserAnimationsModule] - }); +// these tests are only mean't to be run within the DOM (for now) +// Buggy in Chromium 39, see https://github.com/angular/angular/issues/15793 +if (isNode || !ɵsupportsWebAnimations()) return; + +describe('animation integration tests using web animations', function() { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{provide: AnimationDriver, useClass: ɵWebAnimationsDriver}], + imports: [BrowserAnimationsModule] }); + }); - it('should compute (*) animation styles for a container that is being removed', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should compute (*) animation styles for a container that is being removed', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div @auto *ngIf="exp"> <div style="line-height:20px;">1</div> <div style="line-height:20px;">2</div> @@ -40,55 +39,53 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut <div style="line-height:20px;">5</div> </div> `, - animations: [trigger( - 'auto', - [ - state('void', style({height: '0px'})), state('*', style({height: '*'})), - transition('* => *', animate(1000)) - ])] - }) - class Cmp { - public exp: boolean = false; - } + animations: [trigger( + 'auto', + [ + state('void', style({height: '0px'})), state('*', style({height: '*'})), + transition('* => *', animate(1000)) + ])] + }) + class Cmp { + public exp: boolean = false; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = true; + fixture.detectChanges(); - TestBed.configureTestingModule({declarations: [Cmp]}); + expect(engine.players.length).toEqual(1); + let webPlayer = + (engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + expect(webPlayer.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]); - cmp.exp = true; + webPlayer.finish(); + + if (!browserDetection.isOldChrome) { + cmp.exp = false; fixture.detectChanges(); + engine.flush(); expect(engine.players.length).toEqual(1); - let webPlayer = + webPlayer = (engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; expect(webPlayer.keyframes).toEqual([ - {height: '0px', offset: 0}, {height: '100px', offset: 1} + {height: '100px', offset: 0}, {height: '0px', offset: 1} ]); + } + }); - webPlayer.finish(); - - if (!browserDetection.isOldChrome) { - cmp.exp = false; - fixture.detectChanges(); - engine.flush(); - - expect(engine.players.length).toEqual(1); - webPlayer = (engine.players[0] as TransitionAnimationPlayer) - .getRealPlayer() as ɵWebAnimationsPlayer; - - expect(webPlayer.keyframes).toEqual([ - {height: '100px', offset: 0}, {height: '0px', offset: 1} - ]); - } - }); - - it('should compute (!) animation styles for a container that is being inserted', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should compute (!) animation styles for a container that is being inserted', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div @auto *ngIf="exp"> <div style="line-height:20px;">1</div> <div style="line-height:20px;">2</div> @@ -97,95 +94,92 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut <div style="line-height:20px;">5</div> </div> `, - animations: [trigger( - 'auto', - [transition( - ':enter', [style({height: '!'}), animate(1000, style({height: '120px'}))])])] - }) - class Cmp { - public exp: boolean = false; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = true; - fixture.detectChanges(); - engine.flush(); - - expect(engine.players.length).toEqual(1); - let webPlayer = - (engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; - - expect(webPlayer.keyframes).toEqual([ - {height: '100px', offset: 0}, {height: '120px', offset: 1} - ]); - }); + animations: [trigger( + 'auto', + [transition(':enter', [style({height: '!'}), animate(1000, style({height: '120px'}))])])] + }) + class Cmp { + public exp: boolean = false; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = true; + fixture.detectChanges(); + engine.flush(); + + expect(engine.players.length).toEqual(1); + let webPlayer = + (engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; + + expect(webPlayer.keyframes).toEqual([ + {height: '100px', offset: 0}, {height: '120px', offset: 1} + ]); + }); - it('should compute pre (!) and post (*) animation styles with different dom states', () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should compute pre (!) and post (*) animation styles with different dom states', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp" #parent> <div *ngFor="let item of items" class="child" style="line-height:20px"> - {{ item }} </div> </div> `, - animations: [trigger( - 'myAnimation', - [transition('* => *', [style({height: '!'}), animate(1000, style({height: '*'}))])])] - }) - class Cmp { - // TODO(issue/24571): remove '!'. - public exp !: number; - public items = [0, 1, 2, 3, 4]; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 1; - fixture.detectChanges(); - engine.flush(); - - expect(engine.players.length).toEqual(1); - let player = engine.players[0]; - let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; - - expect(webPlayer.keyframes).toEqual([ - {height: '0px', offset: 0}, {height: '100px', offset: 1} - ]); - - // we destroy the player because since it has started and is - // at 0ms duration a height value of `0px` will be extracted - // from the element and passed into the follow-up animation. - player.destroy(); - - cmp.exp = 2; - cmp.items = [0, 1, 2, 6]; - fixture.detectChanges(); - engine.flush(); - - expect(engine.players.length).toEqual(1); - player = engine.players[0]; - webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; - - expect(webPlayer.keyframes).toEqual([ - {height: '100px', offset: 0}, {height: '80px', offset: 1} - ]); - }); + animations: [trigger( + 'myAnimation', + [transition('* => *', [style({height: '!'}), animate(1000, style({height: '*'}))])])] + }) + class Cmp { + // TODO(issue/24571): remove '!'. + public exp!: number; + public items = [0, 1, 2, 3, 4]; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 1; + fixture.detectChanges(); + engine.flush(); + + expect(engine.players.length).toEqual(1); + let player = engine.players[0]; + let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; + + expect(webPlayer.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]); + + // we destroy the player because since it has started and is + // at 0ms duration a height value of `0px` will be extracted + // from the element and passed into the follow-up animation. + player.destroy(); + + cmp.exp = 2; + cmp.items = [0, 1, 2, 6]; + fixture.detectChanges(); + engine.flush(); + + expect(engine.players.length).toEqual(1); + player = engine.players[0]; + webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; + + expect(webPlayer.keyframes).toEqual([ + {height: '100px', offset: 0}, {height: '80px', offset: 1} + ]); + }); - it('should treat * styles as ! when a removal animation is being rendered', () => { - @Component({ - selector: 'ani-cmp', - styles: [` + it('should treat * styles as ! when a removal animation is being rendered', () => { + @Component({ + selector: 'ani-cmp', + styles: [` .box { width: 500px; overflow:hidden; @@ -195,58 +189,60 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut text-align:center; } `], - template: ` + template: ` <button (click)="toggle()">Open / Close</button> <hr /> <div *ngIf="exp" @slide class="box"> ... </div> `, - animations: [trigger( - 'slide', - [ - state('void', style({height: '0px'})), - state('*', style({height: '*'})), - transition('* => *', animate('500ms')), - ])] - }) - class Cmp { - exp = false; - - toggle() { this.exp = !this.exp; } + animations: [trigger( + 'slide', + [ + state('void', style({height: '0px'})), + state('*', style({height: '*'})), + transition('* => *', animate('500ms')), + ])] + }) + class Cmp { + exp = false; + + toggle() { + this.exp = !this.exp; } + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = true; + fixture.detectChanges(); + + let player = engine.players[0]!; + let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; + expect(webPlayer.keyframes).toEqual([ + {height: '0px', offset: 0}, + {height: '300px', offset: 1}, + ]); + player.finish(); + + cmp.exp = false; + fixture.detectChanges(); + + player = engine.players[0]!; + webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; + expect(webPlayer.keyframes).toEqual([ + {height: '300px', offset: 0}, + {height: '0px', offset: 1}, + ]); + }); - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = true; - fixture.detectChanges(); - - let player = engine.players[0] !; - let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; - expect(webPlayer.keyframes).toEqual([ - {height: '0px', offset: 0}, - {height: '300px', offset: 1}, - ]); - player.finish(); - - cmp.exp = false; - fixture.detectChanges(); - - player = engine.players[0] !; - webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; - expect(webPlayer.keyframes).toEqual([ - {height: '300px', offset: 0}, - {height: '0px', offset: 1}, - ]); - }); - - it('should treat * styles as ! for queried items that are collected in a container that is being removed', - () => { - @Component({ + it('should treat * styles as ! for queried items that are collected in a container that is being removed', + () => { + @Component({ selector: 'my-app', styles: [` .list .outer { @@ -287,235 +283,236 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut ] }) class Cmp { - items: any[] = []; + items: any[] = []; - get exp() { return this.items.length ? 'full' : 'empty'; } - - empty() { this.items = []; } - - full() { this.items = [0, 1, 2, 3, 4]; } + get exp() { + return this.items.length ? 'full' : 'empty'; } - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.empty(); - fixture.detectChanges(); - let player = engine.players[0] !as TransitionAnimationPlayer; - player.finish(); - - cmp.full(); - fixture.detectChanges(); - - player = engine.players[0] !as TransitionAnimationPlayer; - let queriedPlayers = - ((player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer) - .players; - expect(queriedPlayers.length).toEqual(5); - - let i = 0; - for (i = 0; i < queriedPlayers.length; i++) { - let player = queriedPlayers[i] as ɵWebAnimationsPlayer; - expect(player.keyframes).toEqual([ - {height: '0px', offset: 0}, - {height: '50px', offset: 1}, - ]); - player.finish(); + empty() { + this.items = []; } - cmp.empty(); - fixture.detectChanges(); - - player = engine.players[0] !as TransitionAnimationPlayer; - queriedPlayers = - ((player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer) - .players; - expect(queriedPlayers.length).toEqual(5); - - for (i = 0; i < queriedPlayers.length; i++) { - let player = queriedPlayers[i] as ɵWebAnimationsPlayer; - expect(player.keyframes).toEqual([ - {height: '50px', offset: 0}, - {height: '0px', offset: 1}, - ]); + full() { + this.items = [0, 1, 2, 3, 4]; } - }); - - it('should compute intermediate styles properly when an animation is cancelled', () => { - @Component({ - selector: 'ani-cmp', - template: ` + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.empty(); + fixture.detectChanges(); + let player = engine.players[0]! as TransitionAnimationPlayer; + player.finish(); + + cmp.full(); + fixture.detectChanges(); + + player = engine.players[0]! as TransitionAnimationPlayer; + let queriedPlayers = + ((player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer).players; + expect(queriedPlayers.length).toEqual(5); + + let i = 0; + for (i = 0; i < queriedPlayers.length; i++) { + let player = queriedPlayers[i] as ɵWebAnimationsPlayer; + expect(player.keyframes).toEqual([ + {height: '0px', offset: 0}, + {height: '50px', offset: 1}, + ]); + player.finish(); + } + + cmp.empty(); + fixture.detectChanges(); + + player = engine.players[0]! as TransitionAnimationPlayer; + queriedPlayers = + ((player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer).players; + expect(queriedPlayers.length).toEqual(5); + + for (i = 0; i < queriedPlayers.length; i++) { + let player = queriedPlayers[i] as ɵWebAnimationsPlayer; + expect(player.keyframes).toEqual([ + {height: '50px', offset: 0}, + {height: '0px', offset: 1}, + ]); + } + }); + + it('should compute intermediate styles properly when an animation is cancelled', () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp">...</div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => a', - [ - style({width: 0, height: 0}), - animate('1s', style({width: '300px', height: '600px'})), - ]), - transition('* => b', [animate('1s', style({opacity: 0}))]), - ]), - ] - }) - class Cmp { - // TODO(issue/24571): remove '!'. - public exp !: string; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'a'; - fixture.detectChanges(); - - let player = engine.players[0] !; - let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; - webPlayer.setPosition(0.5); - - cmp.exp = 'b'; - fixture.detectChanges(); - - player = engine.players[0] !; - webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; - expect(approximate(parseFloat(webPlayer.keyframes[0]['width'] as string), 150)) - .toBeLessThan(0.05); - expect(approximate(parseFloat(webPlayer.keyframes[0]['height'] as string), 300)) - .toBeLessThan(0.05); - }); + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => a', + [ + style({width: 0, height: 0}), + animate('1s', style({width: '300px', height: '600px'})), + ]), + transition('* => b', [animate('1s', style({opacity: 0}))]), + ]), + ] + }) + class Cmp { + // TODO(issue/24571): remove '!'. + public exp!: string; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'a'; + fixture.detectChanges(); + + let player = engine.players[0]!; + let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; + webPlayer.setPosition(0.5); + + cmp.exp = 'b'; + fixture.detectChanges(); + + player = engine.players[0]!; + webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer; + expect(approximate(parseFloat(webPlayer.keyframes[0]['width'] as string), 150)) + .toBeLessThan(0.05); + expect(approximate(parseFloat(webPlayer.keyframes[0]['height'] as string), 300)) + .toBeLessThan(0.05); + }); - it('should compute intermediate styles properly for multiple queried elements when an animation is cancelled', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + it('should compute intermediate styles properly for multiple queried elements when an animation is cancelled', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div [@myAnimation]="exp"> <div *ngFor="let item of items" class="target"></div> </div> `, - animations: [ - trigger( - 'myAnimation', - [ - transition( - '* => full', [query( - '.target', - [ - style({width: 0, height: 0}), - animate('1s', style({width: '500px', height: '1000px'})), - ])]), - transition( - '* => empty', [query('.target', [animate('1s', style({opacity: 0}))])]), - ]), - ] - }) - class Cmp { - // TODO(issue/24571): remove '!'. - public exp !: string; - public items: any[] = []; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - cmp.exp = 'full'; - cmp.items = [0, 1, 2, 3, 4]; - fixture.detectChanges(); - - let player = engine.players[0] !; - let groupPlayer = - (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; - let players = groupPlayer.players; - expect(players.length).toEqual(5); - - for (let i = 0; i < players.length; i++) { - const p = players[i] as ɵWebAnimationsPlayer; - p.setPosition(0.5); - } - - cmp.exp = 'empty'; - cmp.items = []; - fixture.detectChanges(); - - player = engine.players[0]; - groupPlayer = - (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; - players = groupPlayer.players; - - expect(players.length).toEqual(5); - for (let i = 0; i < players.length; i++) { - const p = players[i] as ɵWebAnimationsPlayer; - expect(approximate(parseFloat(p.keyframes[0]['width'] as string), 250)) - .toBeLessThan(0.05); - expect(approximate(parseFloat(p.keyframes[0]['height'] as string), 500)) - .toBeLessThan(0.05); - } - }); - - it('should apply the `display` and `position` styles as regular inline styles for the duration of the animation', - () => { - @Component({ - selector: 'ani-cmp', - template: ` + animations: [ + trigger( + 'myAnimation', + [ + transition( + '* => full', [query( + '.target', + [ + style({width: 0, height: 0}), + animate('1s', style({width: '500px', height: '1000px'})), + ])]), + transition('* => empty', [query('.target', [animate('1s', style({opacity: 0}))])]), + ]), + ] + }) + class Cmp { + // TODO(issue/24571): remove '!'. + public exp!: string; + public items: any[] = []; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + cmp.exp = 'full'; + cmp.items = [0, 1, 2, 3, 4]; + fixture.detectChanges(); + + let player = engine.players[0]!; + let groupPlayer = + (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; + let players = groupPlayer.players; + expect(players.length).toEqual(5); + + for (let i = 0; i < players.length; i++) { + const p = players[i] as ɵWebAnimationsPlayer; + p.setPosition(0.5); + } + + cmp.exp = 'empty'; + cmp.items = []; + fixture.detectChanges(); + + player = engine.players[0]; + groupPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer; + players = groupPlayer.players; + + expect(players.length).toEqual(5); + for (let i = 0; i < players.length; i++) { + const p = players[i] as ɵWebAnimationsPlayer; + expect(approximate(parseFloat(p.keyframes[0]['width'] as string), 250)).toBeLessThan(0.05); + expect(approximate(parseFloat(p.keyframes[0]['height'] as string), 500)) + .toBeLessThan(0.05); + } + }); + + it('should apply the `display` and `position` styles as regular inline styles for the duration of the animation', + () => { + @Component({ + selector: 'ani-cmp', + template: ` <div #elm [@myAnimation]="myAnimationExp" style="display:table; position:fixed"></div> `, - animations: [ - trigger( - 'myAnimation', - [ - state('go', style({display: 'inline-block'})), - transition( - '* => go', - [ - style({display: 'inline', position: 'absolute', opacity: 0}), - animate('1s', style({display: 'inline', opacity: 1, position: 'static'})), - animate('1s', style({display: 'flexbox', opacity: 0})), - ]) - ]), - ] - }) - class Cmp { - @ViewChild('elm', {static: true}) public element: any; - - public myAnimationExp = ''; - } - - TestBed.configureTestingModule({declarations: [Cmp]}); - - const engine = TestBed.inject(ɵAnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - const elm = cmp.element.nativeElement; - expect(elm.style.getPropertyValue('display')).toEqual('table'); - expect(elm.style.getPropertyValue('position')).toEqual('fixed'); - - cmp.myAnimationExp = 'go'; - fixture.detectChanges(); - - expect(elm.style.getPropertyValue('display')).toEqual('inline'); - expect(elm.style.getPropertyValue('position')).toEqual('absolute'); - - const player = engine.players.pop() !; - player.finish(); - player.destroy(); - - expect(elm.style.getPropertyValue('display')).toEqual('inline-block'); - expect(elm.style.getPropertyValue('position')).toEqual('fixed'); - }); - }); + animations: [ + trigger( + 'myAnimation', + [ + state('go', style({display: 'inline-block'})), + transition( + '* => go', + [ + style({display: 'inline', position: 'absolute', opacity: 0}), + animate('1s', style({display: 'inline', opacity: 1, position: 'static'})), + animate('1s', style({display: 'flexbox', opacity: 0})), + ]) + ]), + ] + }) + class Cmp { + @ViewChild('elm', {static: true}) public element: any; + + public myAnimationExp = ''; + } + + TestBed.configureTestingModule({declarations: [Cmp]}); + + const engine = TestBed.inject(ɵAnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + const elm = cmp.element.nativeElement; + expect(elm.style.getPropertyValue('display')).toEqual('table'); + expect(elm.style.getPropertyValue('position')).toEqual('fixed'); + + cmp.myAnimationExp = 'go'; + fixture.detectChanges(); + + expect(elm.style.getPropertyValue('display')).toEqual('inline'); + expect(elm.style.getPropertyValue('position')).toEqual('absolute'); + + const player = engine.players.pop()!; + player.finish(); + player.destroy(); + + expect(elm.style.getPropertyValue('display')).toEqual('inline-block'); + expect(elm.style.getPropertyValue('position')).toEqual('fixed'); + }); +}); })(); function approximate(value: number, target: number) { diff --git a/packages/core/test/application_init_spec.ts b/packages/core/test/application_init_spec.ts index 05069e561a37d..2d5ec25db079b 100644 --- a/packages/core/test/application_init_spec.ts +++ b/packages/core/test/application_init_spec.ts @@ -7,12 +7,12 @@ */ import {Injector} from '@angular/core'; import {APP_INITIALIZER, ApplicationInitStatus} from '@angular/core/src/application_init'; -import {TestBed, async, inject} from '../testing'; + +import {async, inject, TestBed} from '../testing'; { describe('ApplicationInitStatus', () => { describe('no initializers', () => { - it('should return true for `done`', async(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { (status as any).runInitializers(); @@ -22,7 +22,9 @@ import {TestBed, async, inject} from '../testing'; it('should return a promise that resolves immediately for `donePromise`', async(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => { (status as any).runInitializers(); - status.donePromise.then(() => { expect(status.done).toBe(true); }); + status.donePromise.then(() => { + expect(status.done).toBe(true); + }); }))); }); @@ -34,10 +36,14 @@ import {TestBed, async, inject} from '../testing'; let initializerFactory = (injector: Injector) => { return () => { const initStatus = injector.get(ApplicationInitStatus); - initStatus.donePromise.then(() => { expect(completerResolver).toBe(true); }); + initStatus.donePromise.then(() => { + expect(completerResolver).toBe(true); + }); }; }; - promise = new Promise((res) => { resolve = res; }); + promise = new Promise((res) => { + resolve = res; + }); TestBed.configureTestingModule({ providers: [ {provide: APP_INITIALIZER, multi: true, useValue: () => promise}, diff --git a/packages/core/test/application_module_spec.ts b/packages/core/test/application_module_spec.ts index 2cb36d26b5c0e..c730302934539 100644 --- a/packages/core/test/application_module_spec.ts +++ b/packages/core/test/application_module_spec.ts @@ -16,8 +16,9 @@ import {describe, expect, inject, it} from '../testing/src/testing_internal'; { describe('Application module', () => { - it('should set the default locale to "en-US"', - inject([LOCALE_ID], (defaultLocale: string) => { expect(defaultLocale).toEqual('en-US'); })); + it('should set the default locale to "en-US"', inject([LOCALE_ID], (defaultLocale: string) => { + expect(defaultLocale).toEqual('en-US'); + })); it('should set the default currency code to "USD"', inject([DEFAULT_CURRENCY_CODE], (defaultCurrencyCode: string) => { diff --git a/packages/core/test/application_ref_integration_spec.ts b/packages/core/test/application_ref_integration_spec.ts index 10cbee4ce2bfb..7395ccdff2aaf 100644 --- a/packages/core/test/application_ref_integration_spec.ts +++ b/packages/core/test/application_ref_integration_spec.ts @@ -17,13 +17,16 @@ ivyEnabled && describe('ApplicationRef bootstrap', () => { selector: 'hello-world', template: '<div>Hello {{ name }}</div>', }) - class HelloWorldComponent implements OnInit, - DoCheck { + class HelloWorldComponent implements OnInit, DoCheck { log: string[] = []; name = 'World'; - ngOnInit(): void { this.log.push('OnInit'); } - ngDoCheck(): void { this.log.push('DoCheck'); } + ngOnInit(): void { + this.log.push('OnInit'); + } + ngDoCheck(): void { + this.log.push('DoCheck'); + } } @NgModule({ @@ -34,7 +37,7 @@ ivyEnabled && describe('ApplicationRef bootstrap', () => { class MyAppModule { } - it('should bootstrap hello world', withBody('<hello-world></hello-world>', async() => { + it('should bootstrap hello world', withBody('<hello-world></hello-world>', async () => { const MyAppModuleFactory = new NgModuleFactory(MyAppModule); const moduleRef = await getTestBed().platform.bootstrapModuleFactory(MyAppModuleFactory, {ngZone: 'noop'}); @@ -58,7 +61,7 @@ ivyEnabled && describe('ApplicationRef bootstrap', () => { })); it('should expose the `window.ng` global utilities', - withBody('<hello-world></hello-world>', async() => { + withBody('<hello-world></hello-world>', async () => { const MyAppModuleFactory = new NgModuleFactory(MyAppModule); const moduleRef = await getTestBed().platform.bootstrapModuleFactory(MyAppModuleFactory, {ngZone: 'noop'}); diff --git a/packages/core/test/application_ref_spec.ts b/packages/core/test/application_ref_spec.ts index 8639cbe0db02a..da2ddb7e6de56 100644 --- a/packages/core/test/application_ref_spec.ts +++ b/packages/core/test/application_ref_spec.ts @@ -19,7 +19,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; import {onlyInIvy} from '@angular/private/testing'; import {NoopNgZone} from '../src/zone/ng_zone'; -import {ComponentFixtureNoNgZone, TestBed, async, inject, withModule} from '../testing'; +import {async, ComponentFixtureNoNgZone, inject, TestBed, withModule} from '../testing'; @Component({selector: 'bootstrap-app', template: 'hello'}) class SomeComponent { @@ -29,7 +29,9 @@ class SomeComponent { describe('bootstrap', () => { let mockConsole: MockConsole; - beforeEach(() => { mockConsole = new MockConsole(); }); + beforeEach(() => { + mockConsole = new MockConsole(); + }); function createRootEl(selector = 'bootstrap-app') { const doc = TestBed.inject(DOCUMENT); @@ -47,7 +49,7 @@ class SomeComponent { function createModule(providers?: any[]): Type<any>; function createModule(options: CreateModuleOptions): Type<any>; - function createModule(providersOrOptions: any[] | CreateModuleOptions | undefined): Type<any> { + function createModule(providersOrOptions: any[]|CreateModuleOptions|undefined): Type<any> { let options: CreateModuleOptions = {}; if (Array.isArray(providersOrOptions)) { options = {providers: providersOrOptions}; @@ -98,8 +100,7 @@ class SomeComponent { createRootEl(); const modFactory = compiler.compileModuleSync(SomeModule); const module = modFactory.create(TestBed); - const cmpFactory = - module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !; + const cmpFactory = module.componentFactoryResolver.resolveComponentFactory(SomeComponent)!; const component = app.bootstrap(cmpFactory); // The component should see the child module providers @@ -128,8 +129,7 @@ class SomeComponent { createRootEl('custom-selector'); const modFactory = compiler.compileModuleSync(SomeModule); const module = modFactory.create(TestBed); - const cmpFactory = - module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !; + const cmpFactory = module.componentFactoryResolver.resolveComponentFactory(SomeComponent)!; const component = app.bootstrap(cmpFactory, 'custom-selector'); // The component should see the child module providers @@ -137,7 +137,9 @@ class SomeComponent { }))); describe('ApplicationRef', () => { - beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); }); + beforeEach(() => { + TestBed.configureTestingModule({imports: [createModule()]}); + }); it('should throw when reentering tick', () => { @Component({template: '{{reenter()}}'}) @@ -175,7 +177,9 @@ class SomeComponent { providers: [{ provide: APP_BOOTSTRAP_LISTENER, multi: true, - useValue: (compRef: any) => { capturedCompRefs.push(compRef); } + useValue: (compRef: any) => { + capturedCompRefs.push(compRef); + } }] }); }); @@ -214,7 +218,9 @@ class SomeComponent { it('should wait for asynchronous app initializers', async(() => { let resolve: (result: any) => void; - const promise: Promise<any> = new Promise((res) => { resolve = res; }); + const promise: Promise<any> = new Promise((res) => { + resolve = res; + }); let initializerDone = false; setTimeout(() => { resolve(true); @@ -224,13 +230,20 @@ class SomeComponent { defaultPlatform .bootstrapModule( createModule([{provide: APP_INITIALIZER, useValue: () => promise, multi: true}])) - .then(_ => { expect(initializerDone).toBe(true); }); + .then(_ => { + expect(initializerDone).toBe(true); + }); })); it('should rethrow sync errors even if the exceptionHandler is not rethrowing', async(() => { defaultPlatform - .bootstrapModule(createModule( - [{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}])) + .bootstrapModule(createModule([{ + provide: APP_INITIALIZER, + useValue: () => { + throw 'Test'; + }, + multi: true + }])) .then(() => expect(false).toBe(true), (e) => { expect(e).toBe('Test'); // Error rethrown will be seen by the exception handler since it's after @@ -306,7 +319,7 @@ class SomeComponent { }); })); - it('should resolve component resources when creating module factory', async() => { + it('should resolve component resources when creating module factory', async () => { @Component({ selector: 'with-templates-app', templateUrl: '/test-template.html', @@ -328,7 +341,7 @@ class SomeComponent { }); onlyInIvy('We only need to define `LOCALE_ID` for runtime i18n') - .it('should define `LOCALE_ID`', async() => { + .it('should define `LOCALE_ID`', async () => { @Component({ selector: 'i18n-app', templateUrl: '', @@ -343,7 +356,7 @@ class SomeComponent { expect(getLocaleId()).toEqual('ro'); }); - it('should wait for APP_INITIALIZER to set providers for `LOCALE_ID`', async() => { + it('should wait for APP_INITIALIZER to set providers for `LOCALE_ID`', async () => { let locale: string = ''; const testModule = createModule({ @@ -365,7 +378,9 @@ class SomeComponent { })); it('should wait for asynchronous app initializers', async(() => { let resolve: (result: any) => void; - const promise: Promise<any> = new Promise((res) => { resolve = res; }); + const promise: Promise<any> = new Promise((res) => { + resolve = res; + }); let initializerDone = false; setTimeout(() => { resolve(true); @@ -373,7 +388,7 @@ class SomeComponent { }, 1); const compilerFactory: CompilerFactory = - defaultPlatform.injector.get(CompilerFactory, null) !; + defaultPlatform.injector.get(CompilerFactory, null)!; const moduleFactory = compilerFactory.createCompiler().compileModuleSync( createModule([{provide: APP_INITIALIZER, useValue: () => promise, multi: true}])); defaultPlatform.bootstrapModuleFactory(moduleFactory).then(_ => { @@ -383,9 +398,14 @@ class SomeComponent { it('should rethrow sync errors even if the exceptionHandler is not rethrowing', async(() => { const compilerFactory: CompilerFactory = - defaultPlatform.injector.get(CompilerFactory, null) !; - const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule( - [{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}])); + defaultPlatform.injector.get(CompilerFactory, null)!; + const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule([{ + provide: APP_INITIALIZER, + useValue: () => { + throw 'Test'; + }, + multi: true + }])); expect(() => defaultPlatform.bootstrapModuleFactory(moduleFactory)).toThrow('Test'); // Error rethrown will be seen by the exception handler since it's after // construction. @@ -395,7 +415,7 @@ class SomeComponent { it('should rethrow promise errors even if the exceptionHandler is not rethrowing', async(() => { const compilerFactory: CompilerFactory = - defaultPlatform.injector.get(CompilerFactory, null) !; + defaultPlatform.injector.get(CompilerFactory, null)!; const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule( [{provide: APP_INITIALIZER, useValue: () => Promise.reject('Test'), multi: true}])); defaultPlatform.bootstrapModuleFactory(moduleFactory) @@ -415,15 +435,13 @@ class SomeComponent { @Component({template: '<ng-container #vc></ng-container>'}) class ContainerComp { // TODO(issue/24571): remove '!'. - @ViewChild('vc', {read: ViewContainerRef}) - vc !: ViewContainerRef; + @ViewChild('vc', {read: ViewContainerRef}) vc!: ViewContainerRef; } @Component({template: '<ng-template #t>Dynamic content</ng-template>'}) class EmbeddedViewComp { // TODO(issue/24571): remove '!'. - @ViewChild(TemplateRef, {static: true}) - tplRef !: TemplateRef<Object>; + @ViewChild(TemplateRef, {static: true}) tplRef!: TemplateRef<Object>; } beforeEach(() => { @@ -497,7 +515,7 @@ class SomeComponent { vc.insert(hostView); expect(() => appRef.attachView(hostView)) .toThrowError('This view is already attached to a ViewContainer!'); - hostView = vc.detach(0) !; + hostView = vc.detach(0)!; appRef.attachView(hostView); expect(() => vc.insert(hostView)) @@ -516,7 +534,9 @@ class SomeComponent { class ClickComp { text: string = '1'; - onClick() { this.text += '1'; } + onClick() { + this.text += '1'; + } } @Component({selector: 'micro-task-comp', template: `<span>{{text}}</span>`}) @@ -524,7 +544,9 @@ class SomeComponent { text: string = '1'; ngOnInit() { - Promise.resolve(null).then((_) => { this.text += '1'; }); + Promise.resolve(null).then((_) => { + this.text += '1'; + }); } } @@ -533,7 +555,9 @@ class SomeComponent { text: string = '1'; ngOnInit() { - setTimeout(() => { this.text += '1'; }, 10); + setTimeout(() => { + this.text += '1'; + }, 10); } } @@ -544,7 +568,9 @@ class SomeComponent { ngOnInit() { Promise.resolve(null).then((_) => { this.text += '1'; - setTimeout(() => { this.text += '1'; }, 10); + setTimeout(() => { + this.text += '1'; + }, 10); }); } } @@ -556,7 +582,9 @@ class SomeComponent { ngOnInit() { setTimeout(() => { this.text += '1'; - Promise.resolve(null).then((_: any) => { this.text += '1'; }); + Promise.resolve(null).then((_: any) => { + this.text += '1'; + }); }, 10); } } @@ -572,7 +600,9 @@ class SomeComponent { }); }); - afterEach(() => { expect(stableCalled).toBe(true, 'isStable did not emit true on stable'); }); + afterEach(() => { + expect(stableCalled).toBe(true, 'isStable did not emit true on stable'); + }); function expectStableTexts(component: Type<any>, expected: string[]) { const fixture = TestBed.createComponent(component); @@ -593,26 +623,34 @@ class SomeComponent { }); } - it('isStable should fire on synchronous component loading', - async(() => { expectStableTexts(SyncComp, ['1']); })); + it('isStable should fire on synchronous component loading', async(() => { + expectStableTexts(SyncComp, ['1']); + })); - it('isStable should fire after a microtask on init is completed', - async(() => { expectStableTexts(MicroTaskComp, ['11']); })); + it('isStable should fire after a microtask on init is completed', async(() => { + expectStableTexts(MicroTaskComp, ['11']); + })); - it('isStable should fire after a macrotask on init is completed', - async(() => { expectStableTexts(MacroTaskComp, ['11']); })); + it('isStable should fire after a macrotask on init is completed', async(() => { + expectStableTexts(MacroTaskComp, ['11']); + })); it('isStable should fire only after chain of micro and macrotasks on init are completed', - async(() => { expectStableTexts(MicroMacroTaskComp, ['111']); })); + async(() => { + expectStableTexts(MicroMacroTaskComp, ['111']); + })); it('isStable should fire only after chain of macro and microtasks on init are completed', - async(() => { expectStableTexts(MacroMicroTaskComp, ['111']); })); + async(() => { + expectStableTexts(MacroMicroTaskComp, ['111']); + })); describe('unstable', () => { let unstableCalled = false; - afterEach( - () => { expect(unstableCalled).toBe(true, 'isStable did not emit false on unstable'); }); + afterEach(() => { + expect(unstableCalled).toBe(true, 'isStable did not emit false on unstable'); + }); function expectUnstable(appRef: ApplicationRef) { appRef.isStable.subscribe({ diff --git a/packages/core/test/bundling/animation_world/index.ts b/packages/core/test/bundling/animation_world/index.ts index 6c68aa888b50d..224eb1fe9e68b 100644 --- a/packages/core/test/bundling/animation_world/index.ts +++ b/packages/core/test/bundling/animation_world/index.ts @@ -30,17 +30,20 @@ class MakeColorGreyDirective { this._textColor = null; } - toggle() { this._backgroundColor ? this.off() : this.on(); } + toggle() { + this._backgroundColor ? this.off() : this.on(); + } } @Component({selector: 'box-with-overridden-styles', template: '...'}) class BoxWithOverriddenStylesComponent { public active = false; - @HostBinding('style') - styles = {}; + @HostBinding('style') styles = {}; - constructor() { this.onInActive(); } + constructor() { + this.onInActive(); + } @HostListener('click', ['$event']) toggle() { @@ -102,9 +105,13 @@ class AnimationWorldComponent { private _hostElement: HTMLElement; public styles: {[key: string]: any}|null = null; - constructor(element: ElementRef) { this._hostElement = element.nativeElement; } + constructor(element: ElementRef) { + this._hostElement = element.nativeElement; + } - makeClass(item: any) { return `record-${item.value}`; } + makeClass(item: any) { + return `record-${item.value}`; + } toggleActive(item: any, makeColorGrey: MakeColorGreyDirective) { item.active = !item.active; diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 7470bbe21eb97..d3b1605e1801a 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -461,6 +461,9 @@ { "name": "isFactory" }, + { + "name": "isInlineTemplate" + }, { "name": "isLContainer" }, diff --git a/packages/core/test/bundling/cyclic_import/integration_spec.ts b/packages/core/test/bundling/cyclic_import/integration_spec.ts index e1a2d4f3181e3..26b9a61d038db 100644 --- a/packages/core/test/bundling/cyclic_import/integration_spec.ts +++ b/packages/core/test/bundling/cyclic_import/integration_spec.ts @@ -17,10 +17,11 @@ const UTF8 = { const PACKAGE = 'angular/packages/core/test/bundling/cyclic_import'; describe('treeshaking with uglify', () => { - let content: string; const contentPath = require.resolve(path.join(PACKAGE, 'bundle.min_debug.js')); - beforeAll(() => { content = fs.readFileSync(contentPath, UTF8); }); + beforeAll(() => { + content = fs.readFileSync(contentPath, UTF8); + }); describe('functional test in domino', () => { it('should render hello world when not minified', withBody('<trigger></trigger>', () => { diff --git a/packages/core/test/bundling/hello_world/treeshaking_spec.ts b/packages/core/test/bundling/hello_world/treeshaking_spec.ts index f4a76c7dc693b..e6646baff5306 100644 --- a/packages/core/test/bundling/hello_world/treeshaking_spec.ts +++ b/packages/core/test/bundling/hello_world/treeshaking_spec.ts @@ -17,21 +17,24 @@ const UTF8 = { const PACKAGE = 'angular/packages/core/test/bundling/hello_world'; describe('treeshaking with uglify', () => { - let content: string; const contentPath = require.resolve(path.join(PACKAGE, 'bundle.min_debug.js')); - beforeAll(() => { content = fs.readFileSync(contentPath, UTF8); }); + beforeAll(() => { + content = fs.readFileSync(contentPath, UTF8); + }); - it('should drop unused TypeScript helpers', - () => { expect(content).not.toContain('__asyncGenerator'); }); + it('should drop unused TypeScript helpers', () => { + expect(content).not.toContain('__asyncGenerator'); + }); it('should not contain rxjs from commonjs distro', () => { expect(content).not.toContain('commonjsGlobal'); expect(content).not.toContain('createCommonjsModule'); }); - it('should not contain zone.js', - () => { expect(content).not.toContain('global[\'Zone\'] = Zone'); }); + it('should not contain zone.js', () => { + expect(content).not.toContain('global[\'Zone\'] = Zone'); + }); describe('functional test in domino', () => { it('should render hello world when not minified', diff --git a/packages/core/test/bundling/hello_world_r2/treeshaking_spec.ts b/packages/core/test/bundling/hello_world_r2/treeshaking_spec.ts index 9f9eedd37508c..55309b5965980 100644 --- a/packages/core/test/bundling/hello_world_r2/treeshaking_spec.ts +++ b/packages/core/test/bundling/hello_world_r2/treeshaking_spec.ts @@ -16,13 +16,15 @@ const UTF8 = { const PACKAGE = 'angular/packages/core/test/bundling/hello_world_r2'; describe('treeshaking with uglify', () => { - let content: string; const contentPath = require.resolve(path.join(PACKAGE, 'bundle.min_debug.js')); - beforeAll(() => { content = fs.readFileSync(contentPath, UTF8); }); + beforeAll(() => { + content = fs.readFileSync(contentPath, UTF8); + }); - it('should drop unused TypeScript helpers', - () => { expect(content).not.toContain('__asyncGenerator'); }); + it('should drop unused TypeScript helpers', () => { + expect(content).not.toContain('__asyncGenerator'); + }); it('should not contain rxjs from commonjs distro', () => { expect(content).not.toContain('commonjsGlobal'); diff --git a/packages/core/test/bundling/injection/treeshaking_spec.ts b/packages/core/test/bundling/injection/treeshaking_spec.ts index 03a82172d0965..7c5529f74b440 100644 --- a/packages/core/test/bundling/injection/treeshaking_spec.ts +++ b/packages/core/test/bundling/injection/treeshaking_spec.ts @@ -11,6 +11,7 @@ import {INJECTOR, ScopedService} from './usage'; describe('functional test for injection system bundling', () => { - it('should be able to inject the scoped service', - () => { expect(INJECTOR.get(ScopedService) instanceof ScopedService).toBe(true); }); + it('should be able to inject the scoped service', () => { + expect(INJECTOR.get(ScopedService) instanceof ScopedService).toBe(true); + }); }); diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index b68bdac5a856c..b0a7253a54bef 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -860,6 +860,9 @@ { "name": "isInHostBindings" }, + { + "name": "isInlineTemplate" + }, { "name": "isJsObject" }, diff --git a/packages/core/test/bundling/todo/index.ts b/packages/core/test/bundling/todo/index.ts index 532f3077c5a59..4c9b3da2c4ff9 100644 --- a/packages/core/test/bundling/todo/index.ts +++ b/packages/core/test/bundling/todo/index.ts @@ -15,9 +15,13 @@ class Todo { editing: boolean; // TODO(issue/24571): remove '!'. - private _title !: string; - get title() { return this._title; } - set title(value: string) { this._title = value.trim(); } + private _title!: string; + get title() { + return this._title; + } + set title(value: string) { + this._title = value.trim(); + } constructor(title: string, public completed: boolean = false) { this.editing = false; @@ -39,21 +43,37 @@ class TodoStore { return this.todos.filter((todo: Todo) => todo.completed === completed); } - allCompleted() { return this.todos.length === this.getCompleted().length; } + allCompleted() { + return this.todos.length === this.getCompleted().length; + } - setAllTo(completed: boolean) { this.todos.forEach((t: Todo) => t.completed = completed); } + setAllTo(completed: boolean) { + this.todos.forEach((t: Todo) => t.completed = completed); + } - removeCompleted() { this.todos = this.getWithCompleted(false); } + removeCompleted() { + this.todos = this.getWithCompleted(false); + } - getRemaining() { return this.getWithCompleted(false); } + getRemaining() { + return this.getWithCompleted(false); + } - getCompleted() { return this.getWithCompleted(true); } + getCompleted() { + return this.getWithCompleted(true); + } - toggleCompletion(todo: Todo) { todo.completed = !todo.completed; } + toggleCompletion(todo: Todo) { + todo.completed = !todo.completed; + } - remove(todo: Todo) { this.todos.splice(this.todos.indexOf(todo), 1); } + remove(todo: Todo) { + this.todos.splice(this.todos.indexOf(todo), 1); + } - add(title: string) { this.todos.push(new Todo(title)); } + add(title: string) { + this.todos.push(new Todo(title)); + } } @Component({ diff --git a/packages/core/test/bundling/todo/todo_e2e_spec.ts b/packages/core/test/bundling/todo/todo_e2e_spec.ts index 2beca5a55e51b..cb2b461912e00 100644 --- a/packages/core/test/bundling/todo/todo_e2e_spec.ts +++ b/packages/core/test/bundling/todo/todo_e2e_spec.ts @@ -21,13 +21,13 @@ const BUNDLES = ['bundle.js', 'bundle.min_debug.js', 'bundle.min.js']; describe('functional test for todo', () => { BUNDLES.forEach(bundle => { describe(bundle, () => { - it('should render todo', withBody('<todo-app></todo-app>', async() => { + it('should render todo', withBody('<todo-app></todo-app>', async () => { require(path.join(PACKAGE, bundle)); - const toDoAppComponent = getComponent(document.querySelector('todo-app') !); + const toDoAppComponent = getComponent(document.querySelector('todo-app')!); expect(document.body.textContent).toContain('todos'); expect(document.body.textContent).toContain('Demonstrate Components'); expect(document.body.textContent).toContain('4 items left'); - document.querySelector('button') !.click(); + document.querySelector('button')!.click(); await whenRendered(toDoAppComponent); expect(document.body.textContent).toContain('3 items left'); })); diff --git a/packages/core/test/bundling/todo_i18n/index.ts b/packages/core/test/bundling/todo_i18n/index.ts index d57b51649b9fb..3613fefcff0fc 100644 --- a/packages/core/test/bundling/todo_i18n/index.ts +++ b/packages/core/test/bundling/todo_i18n/index.ts @@ -13,42 +13,64 @@ import {Component, Injectable, NgModule, ViewEncapsulation, ɵmarkDirty as markD class Todo { editing: boolean; - get title() { return this._title; } - set title(value: string) { this._title = value.trim(); } + get title() { + return this._title; + } + set title(value: string) { + this._title = value.trim(); + } - constructor(private _title: string, public completed: boolean = false) { this.editing = false; } + constructor(private _title: string, public completed: boolean = false) { + this.editing = false; + } } @Injectable({providedIn: 'root'}) class TodoStore { todos: Array<Todo> = [ - new Todo($localize `Demonstrate Components`), - new Todo($localize `Demonstrate Structural Directives`, true), + new Todo($localize`Demonstrate Components`), + new Todo($localize`Demonstrate Structural Directives`, true), // Using a placeholder - new Todo($localize `Demonstrate ${'NgModules'}:value:`), - new Todo($localize `Demonstrate zoneless change detection`), - new Todo($localize `Demonstrate internationalization`), + new Todo($localize`Demonstrate ${'NgModules'}:value:`), + new Todo($localize`Demonstrate zoneless change detection`), + new Todo($localize`Demonstrate internationalization`), ]; private getWithCompleted(completed: boolean) { return this.todos.filter((todo: Todo) => todo.completed === completed); } - allCompleted() { return this.todos.length === this.getCompleted().length; } + allCompleted() { + return this.todos.length === this.getCompleted().length; + } - setAllTo(completed: boolean) { this.todos.forEach((t: Todo) => t.completed = completed); } + setAllTo(completed: boolean) { + this.todos.forEach((t: Todo) => t.completed = completed); + } - removeCompleted() { this.todos = this.getWithCompleted(false); } + removeCompleted() { + this.todos = this.getWithCompleted(false); + } - getRemaining() { return this.getWithCompleted(false); } + getRemaining() { + return this.getWithCompleted(false); + } - getCompleted() { return this.getWithCompleted(true); } + getCompleted() { + return this.getWithCompleted(true); + } - toggleCompletion(todo: Todo) { todo.completed = !todo.completed; } + toggleCompletion(todo: Todo) { + todo.completed = !todo.completed; + } - remove(todo: Todo) { this.todos.splice(this.todos.indexOf(todo), 1); } + remove(todo: Todo) { + this.todos.splice(this.todos.indexOf(todo), 1); + } - add(title: string) { this.todos.push(new Todo(title)); } + add(title: string) { + this.todos.push(new Todo(title)); + } } @Component({ diff --git a/packages/core/test/bundling/todo_i18n/todo_e2e_spec.ts b/packages/core/test/bundling/todo_i18n/todo_e2e_spec.ts index 74d560ab0e97b..dbe8737749bdc 100644 --- a/packages/core/test/bundling/todo_i18n/todo_e2e_spec.ts +++ b/packages/core/test/bundling/todo_i18n/todo_e2e_spec.ts @@ -20,17 +20,17 @@ const BUNDLES = ['bundle.js', 'bundle.min_debug.js', 'bundle.min.js']; describe('functional test for todo i18n', () => { BUNDLES.forEach(bundle => { describe(bundle, () => { - it('should render todo i18n', withBody('<todo-app></todo-app>', async() => { + it('should render todo i18n', withBody('<todo-app></todo-app>', async () => { clearTranslations(); require(path.join(PACKAGE, bundle)); - const toDoAppComponent = getComponent(document.querySelector('todo-app') !); + const toDoAppComponent = getComponent(document.querySelector('todo-app')!); expect(document.body.textContent).toContain('liste de tâches'); expect(document.body.textContent).toContain('Démontrer les components'); expect(document.body.textContent).toContain('Démontrer NgModules'); expect(document.body.textContent).toContain('4 tâches restantes'); - expect(document.querySelector('.new-todo') !.getAttribute('placeholder')) + expect(document.querySelector('.new-todo')!.getAttribute('placeholder')) .toEqual(`Qu'y a-t-il à faire ?`); - document.querySelector('button') !.click(); + document.querySelector('button')!.click(); await whenRendered(toDoAppComponent); expect(document.body.textContent).toContain('3 tâches restantes'); })); diff --git a/packages/core/test/bundling/todo_r2/index.ts b/packages/core/test/bundling/todo_r2/index.ts index 16b2404c30058..2db25ed4c1bac 100644 --- a/packages/core/test/bundling/todo_r2/index.ts +++ b/packages/core/test/bundling/todo_r2/index.ts @@ -16,9 +16,13 @@ class Todo { editing: boolean; // TODO(issue/24571): remove '!'. - private _title !: string; - get title() { return this._title; } - set title(value: string) { this._title = value.trim(); } + private _title!: string; + get title() { + return this._title; + } + set title(value: string) { + this._title = value.trim(); + } constructor(title: string, public completed: boolean = false) { this.editing = false; @@ -40,21 +44,37 @@ class TodoStore { return this.todos.filter((todo: Todo) => todo.completed === completed); } - allCompleted() { return this.todos.length === this.getCompleted().length; } + allCompleted() { + return this.todos.length === this.getCompleted().length; + } - setAllTo(completed: boolean) { this.todos.forEach((t: Todo) => t.completed = completed); } + setAllTo(completed: boolean) { + this.todos.forEach((t: Todo) => t.completed = completed); + } - removeCompleted() { this.todos = this.getWithCompleted(false); } + removeCompleted() { + this.todos = this.getWithCompleted(false); + } - getRemaining() { return this.getWithCompleted(false); } + getRemaining() { + return this.getWithCompleted(false); + } - getCompleted() { return this.getWithCompleted(true); } + getCompleted() { + return this.getWithCompleted(true); + } - toggleCompletion(todo: Todo) { todo.completed = !todo.completed; } + toggleCompletion(todo: Todo) { + todo.completed = !todo.completed; + } - remove(todo: Todo) { this.todos.splice(this.todos.indexOf(todo), 1); } + remove(todo: Todo) { + this.todos.splice(this.todos.indexOf(todo), 1); + } - add(title: string) { this.todos.push(new Todo(title)); } + add(title: string) { + this.todos.push(new Todo(title)); + } } @Component({ @@ -120,14 +140,18 @@ class TodoStore { class ToDoAppComponent { newTodoText = ''; - constructor(public todoStore: TodoStore) { (window as any).toDoAppComponent = this; } + constructor(public todoStore: TodoStore) { + (window as any).toDoAppComponent = this; + } stopEditing(todo: Todo, editedTitle: string) { todo.title = editedTitle; todo.editing = false; } - cancelEditingTodo(todo: Todo) { todo.editing = false; } + cancelEditingTodo(todo: Todo) { + todo.editing = false; + } updateEditingTodo(todo: Todo, editedTitle: string) { editedTitle = editedTitle.trim(); @@ -140,13 +164,21 @@ class ToDoAppComponent { todo.title = editedTitle; } - editTodo(todo: Todo) { todo.editing = true; } + editTodo(todo: Todo) { + todo.editing = true; + } - removeCompleted() { this.todoStore.removeCompleted(); } + removeCompleted() { + this.todoStore.removeCompleted(); + } - toggleCompletion(todo: Todo) { this.todoStore.toggleCompletion(todo); } + toggleCompletion(todo: Todo) { + this.todoStore.toggleCompletion(todo); + } - remove(todo: Todo) { this.todoStore.remove(todo); } + remove(todo: Todo) { + this.todoStore.remove(todo); + } addTodo() { if (this.newTodoText.trim().length) { @@ -158,7 +190,9 @@ class ToDoAppComponent { @NgModule({declarations: [ToDoAppComponent], imports: [CommonModule, BrowserModule]}) class ToDoAppModule { - ngDoBootstrap(app: any) { app.bootstrap(ToDoAppComponent); } + ngDoBootstrap(app: any) { + app.bootstrap(ToDoAppComponent); + } } (window as any).waitForApp = diff --git a/packages/core/test/bundling/todo_r2/todo_e2e_spec.ts b/packages/core/test/bundling/todo_r2/todo_e2e_spec.ts index 1037bec8f2ff3..48bb04f4d752d 100644 --- a/packages/core/test/bundling/todo_r2/todo_e2e_spec.ts +++ b/packages/core/test/bundling/todo_r2/todo_e2e_spec.ts @@ -21,9 +21,9 @@ describe('functional test for todo', () => { BUNDLES.forEach(bundle => { describe(bundle, () => { it('should place styles on the elements within the component', - withBody('<todo-app></todo-app>', async() => { + withBody('<todo-app></todo-app>', async () => { require(path.join(PACKAGE, bundle)); - await(window as any).waitForApp; + await (window as any).waitForApp; const toDoAppComponent = (window as any).toDoAppComponent; await whenRendered(toDoAppComponent); diff --git a/packages/core/test/change_detection/change_detector_util_spec.ts b/packages/core/test/change_detection/change_detector_util_spec.ts index f6a58a17976f0..74636f090e823 100644 --- a/packages/core/test/change_detection/change_detector_util_spec.ts +++ b/packages/core/test/change_detection/change_detector_util_spec.ts @@ -49,8 +49,9 @@ import {devModeEqual} from '@angular/core/src/change_detection/change_detection_ expect(devModeEqual(null, {})).toBe(false); }); - it('should return true for other objects', - () => { expect(devModeEqual({}, {})).toBe(true); }); + it('should return true for other objects', () => { + expect(devModeEqual({}, {})).toBe(true); + }); }); }); } diff --git a/packages/core/test/change_detection/differs/default_iterable_differ_spec.ts b/packages/core/test/change_detection/differs/default_iterable_differ_spec.ts index 8a9bf2a845908..dcdf0f94fe3a3 100644 --- a/packages/core/test/change_detection/differs/default_iterable_differ_spec.ts +++ b/packages/core/test/change_detection/differs/default_iterable_differ_spec.ts @@ -14,13 +14,17 @@ import {iterableChangesAsString, iterableDifferToString} from '../../change_dete class ItemWithId { constructor(private id: string) {} - toString() { return `{id: ${this.id}}`; } + toString() { + return `{id: ${this.id}}`; + } } class ComplexItem { constructor(private id: string, private color: string) {} - toString() { return `{id: ${this.id}, color: ${this.color}}`; } + toString() { + return `{id: ${this.id}, color: ${this.color}}`; + } } // TODO(vicb): UnmodifiableListView / frozen object when implemented @@ -29,7 +33,9 @@ class ComplexItem { describe('DefaultIterableDiffer', function() { let differ: DefaultIterableDiffer<any>; - beforeEach(() => { differ = new DefaultIterableDiffer(); }); + beforeEach(() => { + differ = new DefaultIterableDiffer(); + }); it('should support list and iterables', () => { const f = new DefaultIterableDifferFactory(); @@ -47,10 +53,9 @@ class ComplexItem { l.list = [1]; differ.check(l); - expect(iterableDifferToString(differ)).toEqual(iterableChangesAsString({ - collection: ['1[null->0]'], - additions: ['1[null->0]'] - })); + expect(iterableDifferToString(differ)) + .toEqual( + iterableChangesAsString({collection: ['1[null->0]'], additions: ['1[null->0]']})); l.list = [2, 1]; differ.check(l); @@ -69,10 +74,9 @@ class ComplexItem { l.push('a'); differ.check(l); - expect(iterableDifferToString(differ)).toEqual(iterableChangesAsString({ - collection: ['a[null->0]'], - additions: ['a[null->0]'] - })); + expect(iterableDifferToString(differ)) + .toEqual( + iterableChangesAsString({collection: ['a[null->0]'], additions: ['a[null->0]']})); l.push('b'); differ.check(l); @@ -148,10 +152,9 @@ class ComplexItem { l.push('a'); differ.check(l); - expect(iterableDifferToString(differ)).toEqual(iterableChangesAsString({ - collection: ['a[null->0]'], - additions: ['a[null->0]'] - })); + expect(iterableDifferToString(differ)) + .toEqual( + iterableChangesAsString({collection: ['a[null->0]'], additions: ['a[null->0]']})); l.push('b'); differ.check(l); @@ -299,7 +302,7 @@ class ComplexItem { describe('forEachOperation', () => { function stringifyItemChange( - record: any, p: number | null, c: number | null, originalIndex: number) { + record: any, p: number|null, c: number|null, originalIndex: number) { const suffix = originalIndex == null ? '' : ' [o=' + originalIndex + ']'; const value = record.item; if (record.currentIndex == null) { @@ -312,13 +315,13 @@ class ComplexItem { } function modifyArrayUsingOperation( - arr: number[], endData: any[], prev: number | null, next: number | null) { - let value: number = null !; + arr: number[], endData: any[], prev: number|null, next: number|null) { + let value: number = null!; if (prev == null) { // "next" index is guaranteed to be set since the previous index is // not defined and therefore a new entry is added. - value = endData[next !]; - arr.splice(next !, 0, value); + value = endData[next!]; + arr.splice(next!, 0, value); } else if (next == null) { value = arr[prev]; arr.splice(prev, 1); @@ -335,11 +338,11 @@ class ComplexItem { const startData = [0, 1, 2, 3, 4, 5]; const endData = [6, 2, 7, 0, 4, 8]; - differ = differ.diff(startData) !; - differ = differ.diff(endData) !; + differ = differ.diff(startData)!; + differ = differ.diff(endData)!; const operations: string[] = []; - differ.forEachOperation((item: any, prev: number | null, next: number | null) => { + differ.forEachOperation((item: any, prev: number|null, next: number|null) => { const value = modifyArrayUsingOperation(startData, endData, prev, next); operations.push(stringifyItemChange(item, prev, next, item.previousIndex)); }); @@ -358,11 +361,11 @@ class ComplexItem { const startData = [0, 1, 2, 3]; const endData = [2, 1]; - differ = differ.diff(startData) !; - differ = differ.diff(endData) !; + differ = differ.diff(startData)!; + differ = differ.diff(endData)!; const operations: string[] = []; - differ.forEachOperation((item: any, prev: number | null, next: number | null) => { + differ.forEachOperation((item: any, prev: number|null, next: number|null) => { modifyArrayUsingOperation(startData, endData, prev, next); operations.push(stringifyItemChange(item, prev, next, item.previousIndex)); }); @@ -378,11 +381,11 @@ class ComplexItem { const startData = [1, 2, 3, 4, 5, 6]; const endData = [3, 6, 4, 9, 1, 2]; - differ = differ.diff(startData) !; - differ = differ.diff(endData) !; + differ = differ.diff(startData)!; + differ = differ.diff(endData)!; const operations: string[] = []; - differ.forEachOperation((item: any, prev: number | null, next: number | null) => { + differ.forEachOperation((item: any, prev: number|null, next: number|null) => { modifyArrayUsingOperation(startData, endData, prev, next); operations.push(stringifyItemChange(item, prev, next, item.previousIndex)); }); @@ -399,11 +402,11 @@ class ComplexItem { const startData = [0, 1, 2, 3, 4]; const endData = [4, 1, 2, 3, 0, 5]; - differ = differ.diff(startData) !; - differ = differ.diff(endData) !; + differ = differ.diff(startData)!; + differ = differ.diff(endData)!; const operations: string[] = []; - differ.forEachOperation((item: any, prev: number | null, next: number | null) => { + differ.forEachOperation((item: any, prev: number|null, next: number|null) => { modifyArrayUsingOperation(startData, endData, prev, next); operations.push(stringifyItemChange(item, prev, next, item.previousIndex)); }); @@ -420,11 +423,11 @@ class ComplexItem { const startData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; const endData = [10, 11, 1, 5, 7, 8, 0, 5, 3, 6]; - differ = differ.diff(startData) !; - differ = differ.diff(endData) !; + differ = differ.diff(startData)!; + differ = differ.diff(endData)!; const operations: string[] = []; - differ.forEachOperation((item: any, prev: number | null, next: number | null) => { + differ.forEachOperation((item: any, prev: number|null, next: number|null) => { modifyArrayUsingOperation(startData, endData, prev, next); operations.push(stringifyItemChange(item, prev, next, item.previousIndex)); }); @@ -446,11 +449,11 @@ class ComplexItem { const startData = [1, 2, 3, 4]; const endData = [5, 6, 7, 8]; - differ = differ.diff(startData) !; - differ = differ.diff(endData) !; + differ = differ.diff(startData)!; + differ = differ.diff(endData)!; const operations: string[] = []; - differ.forEachOperation((item: any, prev: number | null, next: number | null) => { + differ.forEachOperation((item: any, prev: number|null, next: number|null) => { const value = modifyArrayUsingOperation(startData, endData, prev, next); operations.push(stringifyItemChange(item, prev, next, item.previousIndex)); }); @@ -471,7 +474,7 @@ class ComplexItem { it('should treat null as an empty list', () => { differ.diff(['a', 'b']); - expect(iterableDifferToString(differ.diff(null !) !)).toEqual(iterableChangesAsString({ + expect(iterableDifferToString(differ.diff(null!)!)).toEqual(iterableChangesAsString({ previous: ['a[0->null]', 'b[1->null]'], removals: ['a[0->null]', 'b[1->null]'] })); @@ -490,7 +493,9 @@ class ComplexItem { const buildItemList = (list: string[]) => list.map((val) => new ItemWithId(val)); - beforeEach(() => { differ = new DefaultIterableDiffer(trackByItemId); }); + beforeEach(() => { + differ = new DefaultIterableDiffer(trackByItemId); + }); it('should treat the collection as dirty if identity changes', () => { differ.diff(buildItemList(['a'])); @@ -539,7 +544,6 @@ class ComplexItem { previous: ['{id: a}[0->1]', '{id: b}[1->0]', '{id: c}'], moves: ['{id: b}[1->0]', '{id: a}[0->1]'] })); - }); it('should track duplicate reinsertion normally', () => { @@ -555,7 +559,6 @@ class ComplexItem { moves: ['{id: a}[0->1]', '{id: a}[1->2]'], additions: ['{id: b}[null->0]'] })); - }); it('should track removals normally', () => { @@ -577,7 +580,9 @@ class ComplexItem { const trackByIndex = (index: number, item: any): number => index; - beforeEach(() => { differ = new DefaultIterableDiffer(trackByIndex); }); + beforeEach(() => { + differ = new DefaultIterableDiffer(trackByIndex); + }); it('should track removals normally', () => { differ.check(['a', 'b', 'c', 'd']); diff --git a/packages/core/test/change_detection/differs/default_keyvalue_differ_spec.ts b/packages/core/test/change_detection/differs/default_keyvalue_differ_spec.ts index bba585f5b069e..86435ed8e2843 100644 --- a/packages/core/test/change_detection/differs/default_keyvalue_differ_spec.ts +++ b/packages/core/test/change_detection/differs/default_keyvalue_differ_spec.ts @@ -23,7 +23,9 @@ import {kvChangesAsString, testChangesAsString} from '../../change_detection/uti m = new Map(); }); - afterEach(() => { differ = null !; }); + afterEach(() => { + differ = null!; + }); it('should detect additions', () => { differ.check(m); @@ -66,7 +68,6 @@ import {kvChangesAsString, testChangesAsString} from '../../change_detection/uti expect(record.previousValue).toEqual(10); expect(record.currentValue).toEqual(20); }); - }); it('should do basic map watching', () => { @@ -183,7 +184,6 @@ import {kvChangesAsString, testChangesAsString} from '../../change_detection/uti previous: ['a[A->null]', 'd[D->null]'], removals: ['a[A->null]', 'd[D->null]'] })); - }); it('should work regardless key order', () => { @@ -221,7 +221,6 @@ import {kvChangesAsString, testChangesAsString} from '../../change_detection/uti removals: ['b[b->null]'] })); }); - }); describe('diff', () => { diff --git a/packages/core/test/change_detection/iterable.ts b/packages/core/test/change_detection/iterable.ts index 97e2401bcd4af..528dca0524ce0 100644 --- a/packages/core/test/change_detection/iterable.ts +++ b/packages/core/test/change_detection/iterable.ts @@ -10,7 +10,11 @@ import {getSymbolIterator} from '@angular/core/src/util/symbol'; export class TestIterable { list: number[]; - constructor() { this.list = []; } + constructor() { + this.list = []; + } - [getSymbolIterator()]() { return (this.list as any)[getSymbolIterator()](); } + [getSymbolIterator()]() { + return (this.list as any)[getSymbolIterator()](); + } } diff --git a/packages/core/test/change_detection/util.ts b/packages/core/test/change_detection/util.ts index 97d68f52dca92..a29a188453533 100644 --- a/packages/core/test/change_detection/util.ts +++ b/packages/core/test/change_detection/util.ts @@ -47,9 +47,14 @@ function icrAsString<V>(icr: IterableChangeRecord<V>): string { stringify(icr.previousIndex) + '->' + stringify(icr.currentIndex) + ']'; } -export function iterableChangesAsString( - {collection = [] as any, previous = [] as any, additions = [] as any, moves = [] as any, - removals = [] as any, identityChanges = [] as any}): string { +export function iterableChangesAsString({ + collection = [] as any, + previous = [] as any, + additions = [] as any, + moves = [] as any, + removals = [] as any, + identityChanges = [] as any +}): string { return 'collection: ' + collection.join(', ') + '\n' + 'previous: ' + previous.join(', ') + '\n' + 'additions: ' + additions.join(', ') + '\n' + diff --git a/packages/core/test/component_fixture_spec.ts b/packages/core/test/component_fixture_spec.ts index 882bf548cb0a7..b40a78b06a401 100644 --- a/packages/core/test/component_fixture_spec.ts +++ b/packages/core/test/component_fixture_spec.ts @@ -7,7 +7,7 @@ */ import {Component, Injectable, Input} from '@angular/core'; -import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBed, async, withModule} from '@angular/core/testing'; +import {async, ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBed, withModule} from '@angular/core/testing'; import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -15,7 +15,9 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; @Injectable() class SimpleComp { simpleBinding: string; - constructor() { this.simpleBinding = 'Simple'; } + constructor() { + this.simpleBinding = 'Simple'; + } } @Component({ @@ -31,7 +33,9 @@ class MyIfComp { class AutoDetectComp { text: string = '1'; - click() { this.text += '1'; } + click() { + this.text += '1'; + } } @Component({selector: 'async-comp', template: `<span (click)='click()'>{{text}}</span>`}) @@ -39,7 +43,9 @@ class AsyncComp { text: string = '1'; click() { - Promise.resolve(null).then((_) => { this.text += '1'; }); + Promise.resolve(null).then((_) => { + this.text += '1'; + }); } } @@ -49,7 +55,9 @@ class AsyncChildComp { @Input() set text(value: string) { - Promise.resolve(null).then((_) => { this.localText = value; }); + Promise.resolve(null).then((_) => { + this.localText = value; + }); } } @@ -60,7 +68,9 @@ class AsyncChildComp { class AsyncChangeComp { text: string = '1'; - click() { this.text += '1'; } + click() { + this.text += '1'; + } } @Component({selector: 'async-timeout-comp', template: `<span (click)='click()'>{{text}}</span>`}) @@ -68,7 +78,9 @@ class AsyncTimeoutComp { text: string = '1'; click() { - setTimeout(() => { this.text += '1'; }, 10); + setTimeout(() => { + this.text += '1'; + }, 10); } } @@ -78,7 +90,11 @@ class NestedAsyncTimeoutComp { text: string = '1'; click() { - setTimeout(() => { setTimeout(() => { this.text += '1'; }, 10); }, 10); + setTimeout(() => { + setTimeout(() => { + this.text += '1'; + }, 10); + }, 10); } } @@ -94,7 +110,6 @@ class NestedAsyncTimeoutComp { })); it('should auto detect changes if autoDetectChanges is called', () => { - const componentFixture = TestBed.createComponent(AutoDetectComp); expect(componentFixture.ngZone).not.toBeNull(); componentFixture.autoDetectChanges(); @@ -109,7 +124,6 @@ class NestedAsyncTimeoutComp { it('should auto detect changes if ComponentFixtureAutoDetect is provided as true', withModule({providers: [{provide: ComponentFixtureAutoDetect, useValue: true}]}, () => { - const componentFixture = TestBed.createComponent(AutoDetectComp); expect(componentFixture.nativeElement).toHaveText('1'); @@ -181,7 +195,6 @@ class NestedAsyncTimeoutComp { it('should wait for macroTask(setTimeout) while checking for whenStable ' + '(no autoDetectChanges)', async(() => { - const componentFixture = TestBed.createComponent(AsyncTimeoutComp); componentFixture.detectChanges(); expect(componentFixture.nativeElement).toHaveText('1'); @@ -203,7 +216,6 @@ class NestedAsyncTimeoutComp { it('should wait for nested macroTasks(setTimeout) while checking for whenStable ' + '(autoDetectChanges)', async(() => { - const componentFixture = TestBed.createComponent(NestedAsyncTimeoutComp); componentFixture.autoDetectChanges(); @@ -225,7 +237,6 @@ class NestedAsyncTimeoutComp { it('should wait for nested macroTasks(setTimeout) while checking for whenStable ' + '(no autoDetectChanges)', async(() => { - const componentFixture = TestBed.createComponent(NestedAsyncTimeoutComp); componentFixture.detectChanges(); expect(componentFixture.nativeElement).toHaveText('1'); @@ -245,7 +256,6 @@ class NestedAsyncTimeoutComp { })); it('should stabilize after async task in change detection (autoDetectChanges)', async(() => { - const componentFixture = TestBed.createComponent(AsyncChangeComp); componentFixture.autoDetectChanges(); @@ -255,13 +265,13 @@ class NestedAsyncTimeoutComp { const element = componentFixture.debugElement.children[0]; dispatchEvent(element.nativeElement, 'click'); - componentFixture.whenStable().then( - (_) => { expect(componentFixture.nativeElement).toHaveText('11'); }); + componentFixture.whenStable().then((_) => { + expect(componentFixture.nativeElement).toHaveText('11'); + }); }); })); it('should stabilize after async task in change detection(no autoDetectChanges)', async(() => { - const componentFixture = TestBed.createComponent(AsyncChangeComp); componentFixture.detectChanges(); componentFixture.whenStable().then((_) => { @@ -290,7 +300,6 @@ class NestedAsyncTimeoutComp { }); it('calling autoDetectChanges raises an error', () => { - const componentFixture = TestBed.createComponent(SimpleComp); expect(() => { componentFixture.autoDetectChanges(); @@ -298,7 +307,6 @@ class NestedAsyncTimeoutComp { }); it('should instantiate a component with valid DOM', async(() => { - const componentFixture = TestBed.createComponent(SimpleComp); expect(componentFixture.ngZone).toBeNull(); @@ -307,7 +315,6 @@ class NestedAsyncTimeoutComp { })); it('should allow changing members of the component', async(() => { - const componentFixture = TestBed.createComponent(MyIfComp); componentFixture.detectChanges(); @@ -316,9 +323,7 @@ class NestedAsyncTimeoutComp { componentFixture.componentInstance.showMore = true; componentFixture.detectChanges(); expect(componentFixture.nativeElement).toHaveText('MyIf(More)'); - })); }); - }); } diff --git a/packages/core/test/debug/debug_node_spec.ts b/packages/core/test/debug/debug_node_spec.ts index 255a8057ddbd4..0ebe8c3e60191 100644 --- a/packages/core/test/debug/debug_node_spec.ts +++ b/packages/core/test/debug/debug_node_spec.ts @@ -10,7 +10,7 @@ import {CommonModule, NgIfContext, ɵgetDOM as getDOM} from '@angular/common'; import {Component, DebugElement, DebugNode, Directive, ElementRef, EmbeddedViewRef, EventEmitter, HostBinding, Injectable, Input, NO_ERRORS_SCHEMA, OnInit, Output, Renderer2, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; import {NgZone} from '@angular/core/src/zone'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {createMouseEvent, hasClass} from '@angular/platform-browser/testing/src/browser_util'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -20,18 +20,26 @@ import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; class Logger { logs: string[]; - constructor() { this.logs = []; } + constructor() { + this.logs = []; + } - add(thing: string) { this.logs.push(thing); } + add(thing: string) { + this.logs.push(thing); + } } @Directive({selector: '[message]', inputs: ['message']}) class MessageDir { logger: Logger; - constructor(logger: Logger) { this.logger = logger; } + constructor(logger: Logger) { + this.logger = logger; + } - set message(newMessage: string) { this.logger.add(newMessage); } + set message(newMessage: string) { + this.logger.add(newMessage); + } } @Directive({selector: '[with-title]', inputs: ['title']}) @@ -49,7 +57,9 @@ class WithTitleDir { class ChildComp { childBinding: string; - constructor() { this.childBinding = 'Original'; } + constructor() { + this.childBinding = 'Original'; + } } @Component({ @@ -63,14 +73,18 @@ class ChildComp { }) class ParentComp { parentBinding: string; - constructor() { this.parentBinding = 'OriginalParent'; } + constructor() { + this.parentBinding = 'OriginalParent'; + } } @Directive({selector: 'custom-emitter', outputs: ['myevent']}) class CustomEmitter { myevent: EventEmitter<any>; - constructor() { this.myevent = new EventEmitter(); } + constructor() { + this.myevent = new EventEmitter(); + } } @Component({ @@ -87,9 +101,13 @@ class EventsComp { this.customed = false; } - handleClick() { this.clicked = true; } + handleClick() { + this.clicked = true; + } - handleCustom() { this.customed = true; } + handleCustom() { + this.customed = true; + } } @Component({ @@ -111,7 +129,9 @@ class ConditionalContentComp { }) class ConditionalParentComp { parentBinding: string; - constructor() { this.parentBinding = 'OriginalParent'; } + constructor() { + this.parentBinding = 'OriginalParent'; + } } @Component({ @@ -124,7 +144,9 @@ class ConditionalParentComp { }) class UsingFor { stuff: string[]; - constructor() { this.stuff = ['one', 'two', 'three']; } + constructor() { + this.stuff = ['one', 'two', 'three']; + } } @Directive({selector: '[mydir]', exportAs: 'mydir'}) @@ -154,12 +176,12 @@ class LocalsComp { }) class BankAccount { // TODO(issue/24571): remove '!'. - @Input() bank !: string; + @Input() bank!: string; // TODO(issue/24571): remove '!'. - @Input('account') id !: string; + @Input('account') id!: string; // TODO(issue/24571): remove '!'. - normalizedBankName !: string; + normalizedBankName!: string; } @Component({ @@ -168,7 +190,7 @@ class BankAccount { ` }) class SimpleContentComp { - @ViewChild('content') content !: ElementRef; + @ViewChild('content') content!: ElementRef; } @Component({ @@ -199,8 +221,7 @@ class TestCmptWithRenderer { @Component({selector: 'host-class-binding', template: ''}) class HostClassBindingCmp { - @HostBinding('class') - hostClasses = 'class-one class-two'; + @HostBinding('class') hostClasses = 'class-one class-two'; } @Component({selector: 'test-cmpt-vcref', template: `<div></div>`}) @@ -525,11 +546,15 @@ class TestCmptWithPropInterpolation { class ViewManipulatingDirective { constructor(private _vcRef: ViewContainerRef, private _tplRef: TemplateRef<any>) {} - insert() { this._vcRef.createEmbeddedView(this._tplRef); } + insert() { + this._vcRef.createEmbeddedView(this._tplRef); + } removeFromTheDom() { const viewRef = this._vcRef.get(0) as EmbeddedViewRef<any>; - viewRef.rootNodes.forEach(rootNode => { getDOM().remove(rootNode); }); + viewRef.rootNodes.forEach(rootNode => { + getDOM().remove(rootNode); + }); } } @@ -596,7 +621,6 @@ class TestCmptWithPropInterpolation { describe('DebugElement.query with dynamically created descendant elements', () => { let fixture: ComponentFixture<{}>; beforeEach(() => { - @Directive({ selector: '[dir]', }) @@ -627,11 +651,11 @@ class TestCmptWithPropInterpolation { TestBed.configureTestingModule({declarations: [MyComponent, MyDir]}); fixture = TestBed.createComponent(MyComponent); fixture.detectChanges(); - }); - it('should find the dynamic elements from fixture root', - () => { expect(fixture.debugElement.query(By.css('.myclass'))).toBeTruthy(); }); + it('should find the dynamic elements from fixture root', () => { + expect(fixture.debugElement.query(By.css('.myclass'))).toBeTruthy(); + }); it('can use a dynamic element as root for another query', () => { const inner = fixture.debugElement.query(By.css('.inner')); @@ -660,15 +684,17 @@ class TestCmptWithPropInterpolation { el = fixture.debugElement; }); - it('when searching for elements by name', - () => { expect(() => el.query(e => e.name === 'any search text')).not.toThrow(); }); + it('when searching for elements by name', () => { + expect(() => el.query(e => e.name === 'any search text')).not.toThrow(); + }); it('when searching for elements by their attributes', () => { - expect(() => el.query(e => e.attributes !['name'] === 'any attribute')).not.toThrow(); + expect(() => el.query(e => e.attributes!['name'] === 'any attribute')).not.toThrow(); }); - it('when searching for elements by their classes', - () => { expect(() => el.query(e => e.classes['any class'] === true)).not.toThrow(); }); + it('when searching for elements by their classes', () => { + expect(() => el.query(e => e.classes['any class'] === true)).not.toThrow(); + }); it('when searching for elements by their styles', () => { expect(() => el.query(e => e.styles['any style'] === 'any value')).not.toThrow(); @@ -678,23 +704,29 @@ class TestCmptWithPropInterpolation { expect(() => el.query(e => e.properties['any prop'] === 'any value')).not.toThrow(); }); - it('when searching by componentInstance', - () => { expect(() => el.query(e => e.componentInstance === null)).not.toThrow(); }); + it('when searching by componentInstance', () => { + expect(() => el.query(e => e.componentInstance === null)).not.toThrow(); + }); - it('when searching by context', - () => { expect(() => el.query(e => e.context === null)).not.toThrow(); }); + it('when searching by context', () => { + expect(() => el.query(e => e.context === null)).not.toThrow(); + }); - it('when searching by listeners', - () => { expect(() => el.query(e => e.listeners.length === 0)).not.toThrow(); }); + it('when searching by listeners', () => { + expect(() => el.query(e => e.listeners.length === 0)).not.toThrow(); + }); - it('when searching by references', - () => { expect(() => el.query(e => e.references === null)).not.toThrow(); }); + it('when searching by references', () => { + expect(() => el.query(e => e.references === null)).not.toThrow(); + }); - it('when searching by providerTokens', - () => { expect(() => el.query(e => e.providerTokens.length === 0)).not.toThrow(); }); + it('when searching by providerTokens', () => { + expect(() => el.query(e => e.providerTokens.length === 0)).not.toThrow(); + }); - it('when searching by injector', - () => { expect(() => el.query(e => e.injector === null)).not.toThrow(); }); + it('when searching by injector', () => { + expect(() => el.query(e => e.injector === null)).not.toThrow(); + }); onlyInIvy('VE does not match elements created outside Angular context') .it('when using the out-of-context element as the DebugElement query root', () => { @@ -746,7 +778,7 @@ class TestCmptWithPropInterpolation { fixture = TestBed.createComponent(LocalsComp); fixture.detectChanges(); - expect(fixture.debugElement.children[0].references !['alice']).toBeAnInstanceOf(MyDir); + expect(fixture.debugElement.children[0].references!['alice']).toBeAnInstanceOf(MyDir); }); it('should allow injecting from the element injector', () => { @@ -941,7 +973,6 @@ class TestCmptWithPropInterpolation { }); describe('componentInstance on DebugNode', () => { - it('should return component associated with a node if a node is a component host', () => { TestBed.overrideTemplate(TestCmpt, `<parent-comp></parent-comp>`); fixture = TestBed.createComponent(TestCmpt); @@ -1199,7 +1230,9 @@ class TestCmptWithPropInterpolation { }) class App { visible = true; - cancel() { calls++; } + cancel() { + calls++; + } } TestBed.configureTestingModule({declarations: [App, CancelButton]}); @@ -1218,7 +1251,6 @@ class TestCmptWithPropInterpolation { expect(calls).toBe(1, 'Expected calls to stay 1 after destroying the node.'); }); - }); it('should not error when accessing node name', () => { @@ -1234,7 +1266,7 @@ class TestCmptWithPropInterpolation { // Node.ELEMENT_NODE while (node) { superParentName = node.name; - node = node.parent !; + node = node.parent!; } expect(superParentName).not.toEqual(''); }); @@ -1257,13 +1289,16 @@ class TestCmptWithPropInterpolation { it('does not call event listeners added outside angular context', () => { let listenerCalled = false; const eventToTrigger = createMouseEvent('mouseenter'); - function listener() { listenerCalled = true; } + function listener() { + listenerCalled = true; + } @Component({template: ''}) class MyComp { constructor(private readonly zone: NgZone, private readonly element: ElementRef) {} ngOnInit() { - this.zone.runOutsideAngular( - () => { this.element.nativeElement.addEventListener('mouseenter', listener); }); + this.zone.runOutsideAngular(() => { + this.element.nativeElement.addEventListener('mouseenter', listener); + }); } } const fixture = diff --git a/packages/core/test/dev_mode_spec.ts b/packages/core/test/dev_mode_spec.ts index 926bfd86d39b5..d64f0cdd9dec9 100644 --- a/packages/core/test/dev_mode_spec.ts +++ b/packages/core/test/dev_mode_spec.ts @@ -10,6 +10,8 @@ import {isDevMode} from '@angular/core'; { describe('dev mode', () => { - it('is enabled in our tests by default', () => { expect(isDevMode()).toBe(true); }); + it('is enabled in our tests by default', () => { + expect(isDevMode()).toBe(true); + }); }); } diff --git a/packages/core/test/di/injector_spec.ts b/packages/core/test/di/injector_spec.ts index aef7d802f3926..7db573fbadc9a 100644 --- a/packages/core/test/di/injector_spec.ts +++ b/packages/core/test/di/injector_spec.ts @@ -22,7 +22,8 @@ import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; .toThrowError('NullInjectorError: No provider for someToken!'); }); - it('should return the default value', - () => { expect(Injector.NULL.get('someToken', 'notFound')).toEqual('notFound'); }); + it('should return the default value', () => { + expect(Injector.NULL.get('someToken', 'notFound')).toEqual('notFound'); + }); }); } diff --git a/packages/core/test/di/r3_injector_spec.ts b/packages/core/test/di/r3_injector_spec.ts index fb48a634b8d4e..680c2c9ff3017 100644 --- a/packages/core/test/di/r3_injector_spec.ts +++ b/packages/core/test/di/r3_injector_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {INJECTOR, InjectFlags, InjectionToken, Injector, Optional, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵinject} from '@angular/core'; -import {R3Injector, createInjector} from '@angular/core/src/di/r3_injector'; +import {InjectFlags, InjectionToken, INJECTOR, Injector, Optional, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵinject} from '@angular/core'; +import {createInjector, R3Injector} from '@angular/core/src/di/r3_injector'; import {expect} from '@angular/platform-browser/testing/src/matchers'; describe('InjectorDef-based createInjector()', () => { @@ -64,7 +64,7 @@ describe('InjectorDef-based createInjector()', () => { providedIn: null, // ChildService is derived from ServiceWithDep, so the factory function here must do the right // thing and create an instance of the requested type if one is given. - factory: (t?: typeof ServiceWithDep) => new (t || ServiceWithDep)(ɵɵinject(Service)), + factory: (t?: typeof ServiceWithDep) => new(t || ServiceWithDep)(ɵɵinject(Service)), }); } @@ -114,7 +114,9 @@ describe('InjectorDef-based createInjector()', () => { factory: () => new DeepService(), }); - ngOnDestroy(): void { deepServiceDestroyed = true; } + ngOnDestroy(): void { + deepServiceDestroyed = true; + } } let eagerServiceCreated: boolean = false; @@ -125,20 +127,31 @@ describe('InjectorDef-based createInjector()', () => { factory: () => new EagerService(), }); - constructor() { eagerServiceCreated = true; } + constructor() { + eagerServiceCreated = true; + } } let deepModuleCreated: boolean = false; class DeepModule { - constructor(eagerService: EagerService) { deepModuleCreated = true; } + constructor(eagerService: EagerService) { + deepModuleCreated = true; + } static ɵinj = ɵɵdefineInjector({ factory: () => new DeepModule(ɵɵinject(EagerService)), imports: undefined, - providers: [ - EagerService, - {provide: DeepService, useFactory: () => { throw new Error('Not overridden!'); }}, - ], + providers: + [ + EagerService, + { + provide: DeepService, + useFactory: + () => { + throw new Error('Not overridden!'); + } + }, + ], }); static safe() { @@ -171,22 +184,23 @@ describe('InjectorDef-based createInjector()', () => { static ɵinj = ɵɵdefineInjector({ factory: () => new Module(), imports: [IntermediateModule], - providers: [ - ChildService, - ServiceWithDep, - ServiceWithOptionalDep, - ServiceWithMultiDep, - {provide: LOCALE, multi: true, useValue: 'en'}, - {provide: LOCALE, multi: true, useValue: 'es'}, - {provide: PRIMITIVE_VALUE, useValue: 'foo'}, - {provide: UNDEFINED_VALUE, useValue: undefined}, - Service, - {provide: SERVICE_TOKEN, useExisting: Service}, - CircularA, - CircularB, - {provide: STATIC_TOKEN, useClass: StaticService, deps: [Service]}, - InjectorWithDep, - ], + providers: + [ + ChildService, + ServiceWithDep, + ServiceWithOptionalDep, + ServiceWithMultiDep, + {provide: LOCALE, multi: true, useValue: 'en'}, + {provide: LOCALE, multi: true, useValue: 'es'}, + {provide: PRIMITIVE_VALUE, useValue: 'foo'}, + {provide: UNDEFINED_VALUE, useValue: undefined}, + Service, + {provide: SERVICE_TOKEN, useExisting: Service}, + CircularA, + CircularB, + {provide: STATIC_TOKEN, useClass: StaticService, deps: [Service]}, + InjectorWithDep, + ], }); } @@ -224,7 +238,9 @@ describe('InjectorDef-based createInjector()', () => { factory: () => new ScopedService(), }); - ngOnDestroy(): void { scopedServiceDestroyed = true; } + ngOnDestroy(): void { + scopedServiceDestroyed = true; + } } class WrongScopeService { @@ -252,10 +268,11 @@ describe('InjectorDef-based createInjector()', () => { class WithProvidersTest { static ɵinj = ɵɵdefineInjector({ factory: () => new WithProvidersTest(), - imports: [ - {ngModule: MultiProviderA, providers: [{provide: LOCALE, multi: true, useValue: 'C'}]}, - MultiProviderB - ], + imports: + [ + {ngModule: MultiProviderA, providers: [{provide: LOCALE, multi: true, useValue: 'C'}]}, + MultiProviderB + ], providers: [], }); } @@ -276,7 +293,9 @@ describe('InjectorDef-based createInjector()', () => { imports: undefined, providers: [], }); - constructor() { moduleRegistrations.push('ChildModule'); } + constructor() { + moduleRegistrations.push('ChildModule'); + } } class RootModule { @@ -285,7 +304,9 @@ describe('InjectorDef-based createInjector()', () => { imports: [ChildModule], providers: [], }); - constructor() { moduleRegistrations.push('RootModule'); } + constructor() { + moduleRegistrations.push('RootModule'); + } } createInjector(RootModule); expect(moduleRegistrations).toEqual(['ChildModule', 'RootModule']); @@ -297,8 +318,9 @@ describe('InjectorDef-based createInjector()', () => { expect(injector.get(Service)).toBe(instance); }); - it('returns the default value if a provider isn\'t present', - () => { expect(injector.get(ServiceTwo, null)).toBeNull(); }); + it('returns the default value if a provider isn\'t present', () => { + expect(injector.get(ServiceTwo, null)).toBeNull(); + }); it('should throw when no provider defined', () => { expect(() => injector.get(ServiceTwo)) @@ -373,14 +395,17 @@ describe('InjectorDef-based createInjector()', () => { expect(instance.dep).toBe(injector.get(Service)); }); - it('allows injecting itself via INJECTOR', - () => { expect(injector.get(INJECTOR)).toBe(injector); }); + it('allows injecting itself via INJECTOR', () => { + expect(injector.get(INJECTOR)).toBe(injector); + }); - it('allows injecting itself via Injector', - () => { expect(injector.get(Injector)).toBe(injector); }); + it('allows injecting itself via Injector', () => { + expect(injector.get(Injector)).toBe(injector); + }); - it('allows injecting a deeply imported service', - () => { expect(injector.get(DeepService) instanceof DeepService).toBeTruthy(); }); + it('allows injecting a deeply imported service', () => { + expect(injector.get(DeepService) instanceof DeepService).toBeTruthy(); + }); it('allows injecting a scoped service', () => { const instance = injector.get(ScopedService); @@ -393,8 +418,9 @@ describe('InjectorDef-based createInjector()', () => { expect(instance instanceof ChildService).toBe(true); }); - it('does not create instances of a service not in scope', - () => { expect(injector.get(WrongScopeService, null)).toBeNull(); }); + it('does not create instances of a service not in scope', () => { + expect(injector.get(WrongScopeService, null)).toBeNull(); + }); it('eagerly instantiates the injectordef types', () => { expect(deepModuleCreated).toBe(true, 'DeepModule not instantiated'); @@ -432,11 +458,13 @@ describe('InjectorDef-based createInjector()', () => { }); describe('error handling', () => { - it('throws an error when a token is not found', - () => { expect(() => injector.get(ServiceTwo)).toThrow(); }); + it('throws an error when a token is not found', () => { + expect(() => injector.get(ServiceTwo)).toThrow(); + }); - it('throws an error on circular deps', - () => { expect(() => injector.get(CircularA)).toThrow(); }); + it('throws an error on circular deps', () => { + expect(() => injector.get(CircularA)).toThrow(); + }); it('should throw when it can\'t resolve all arguments', () => { class MissingArgumentType { diff --git a/packages/core/test/di/reflective_injector_spec.ts b/packages/core/test/di/reflective_injector_spec.ts index 139f8d1eab64a..38bb93b0a4a81 100644 --- a/packages/core/test/di/reflective_injector_spec.ts +++ b/packages/core/test/di/reflective_injector_spec.ts @@ -6,17 +6,20 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, Injectable, InjectionToken, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core'; +import {forwardRef, Inject, Injectable, InjectionToken, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self} from '@angular/core'; import {ReflectiveInjector_} from '@angular/core/src/di/reflective_injector'; import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider'; import {getOriginalError} from '@angular/core/src/errors'; import {expect} from '@angular/platform-browser/testing/src/matchers'; + import {stringify} from '../../src/util/stringify'; class Engine {} class BrokenEngine { - constructor() { throw new Error('Broken Engine'); } + constructor() { + throw new Error('Broken Engine'); + } } class DashboardSoftware {} @@ -66,464 +69,456 @@ class NoAnnotations { constructor(secretDependency: any) {} } -function factoryFn(a: any){} +function factoryFn(a: any) {} (function() { - const dynamicProviders = [ - {provide: 'provider0', useValue: 1}, {provide: 'provider1', useValue: 1}, - {provide: 'provider2', useValue: 1}, {provide: 'provider3', useValue: 1}, - {provide: 'provider4', useValue: 1}, {provide: 'provider5', useValue: 1}, - {provide: 'provider6', useValue: 1}, {provide: 'provider7', useValue: 1}, - {provide: 'provider8', useValue: 1}, {provide: 'provider9', useValue: 1}, - {provide: 'provider10', useValue: 1} - ]; - - function createInjector( - providers: Provider[], parent?: ReflectiveInjector | null): ReflectiveInjector_ { - const resolvedProviders = ReflectiveInjector.resolve(providers.concat(dynamicProviders)); - if (parent != null) { - return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders); - } else { - return <ReflectiveInjector_>ReflectiveInjector.fromResolvedProviders(resolvedProviders); - } +const dynamicProviders = [ + {provide: 'provider0', useValue: 1}, {provide: 'provider1', useValue: 1}, + {provide: 'provider2', useValue: 1}, {provide: 'provider3', useValue: 1}, + {provide: 'provider4', useValue: 1}, {provide: 'provider5', useValue: 1}, + {provide: 'provider6', useValue: 1}, {provide: 'provider7', useValue: 1}, + {provide: 'provider8', useValue: 1}, {provide: 'provider9', useValue: 1}, + {provide: 'provider10', useValue: 1} +]; + +function createInjector( + providers: Provider[], parent?: ReflectiveInjector|null): ReflectiveInjector_ { + const resolvedProviders = ReflectiveInjector.resolve(providers.concat(dynamicProviders)); + if (parent != null) { + return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders); + } else { + return <ReflectiveInjector_>ReflectiveInjector.fromResolvedProviders(resolvedProviders); } +} - describe(`injector`, () => { - - it('should instantiate a class without dependencies', () => { - const injector = createInjector([Engine]); - const engine = injector.get(Engine); - - expect(engine).toBeAnInstanceOf(Engine); - }); - - it('should resolve dependencies based on type information', () => { - const injector = createInjector([Engine, Car]); - const car = injector.get(Car); +describe(`injector`, () => { + it('should instantiate a class without dependencies', () => { + const injector = createInjector([Engine]); + const engine = injector.get(Engine); - expect(car).toBeAnInstanceOf(Car); - expect(car.engine).toBeAnInstanceOf(Engine); - }); + expect(engine).toBeAnInstanceOf(Engine); + }); - it('should resolve dependencies based on @Inject annotation', () => { - const injector = createInjector([TurboEngine, Engine, CarWithInject]); - const car = injector.get(CarWithInject); + it('should resolve dependencies based on type information', () => { + const injector = createInjector([Engine, Car]); + const car = injector.get(Car); - expect(car).toBeAnInstanceOf(CarWithInject); - expect(car.engine).toBeAnInstanceOf(TurboEngine); - }); + expect(car).toBeAnInstanceOf(Car); + expect(car.engine).toBeAnInstanceOf(Engine); + }); - it('should throw when no type and not @Inject (class case)', () => { - expect(() => createInjector([NoAnnotations])) - .toThrowError( - 'Cannot resolve all parameters for \'NoAnnotations\'(?). ' + - 'Make sure that all the parameters are decorated with Inject or have valid type annotations ' + - 'and that \'NoAnnotations\' is decorated with Injectable.'); - }); + it('should resolve dependencies based on @Inject annotation', () => { + const injector = createInjector([TurboEngine, Engine, CarWithInject]); + const car = injector.get(CarWithInject); - it('should throw when no type and not @Inject (factory case)', () => { - expect(() => createInjector([{provide: 'someToken', useFactory: factoryFn}])) - .toThrowError( - 'Cannot resolve all parameters for \'factoryFn\'(?). ' + - 'Make sure that all the parameters are decorated with Inject or have valid type annotations ' + - 'and that \'factoryFn\' is decorated with Injectable.'); - }); + expect(car).toBeAnInstanceOf(CarWithInject); + expect(car.engine).toBeAnInstanceOf(TurboEngine); + }); - it('should cache instances', () => { - const injector = createInjector([Engine]); + it('should throw when no type and not @Inject (class case)', () => { + expect(() => createInjector([NoAnnotations])) + .toThrowError( + 'Cannot resolve all parameters for \'NoAnnotations\'(?). ' + + 'Make sure that all the parameters are decorated with Inject or have valid type annotations ' + + 'and that \'NoAnnotations\' is decorated with Injectable.'); + }); - const e1 = injector.get(Engine); - const e2 = injector.get(Engine); + it('should throw when no type and not @Inject (factory case)', () => { + expect(() => createInjector([{provide: 'someToken', useFactory: factoryFn}])) + .toThrowError( + 'Cannot resolve all parameters for \'factoryFn\'(?). ' + + 'Make sure that all the parameters are decorated with Inject or have valid type annotations ' + + 'and that \'factoryFn\' is decorated with Injectable.'); + }); - expect(e1).toBe(e2); - }); + it('should cache instances', () => { + const injector = createInjector([Engine]); - it('should provide to a value', () => { - const injector = createInjector([{provide: Engine, useValue: 'fake engine'}]); + const e1 = injector.get(Engine); + const e2 = injector.get(Engine); - const engine = injector.get(Engine); - expect(engine).toEqual('fake engine'); - }); + expect(e1).toBe(e2); + }); - it('should inject dependencies instance of InjectionToken', () => { - const TOKEN = new InjectionToken<string>('token'); + it('should provide to a value', () => { + const injector = createInjector([{provide: Engine, useValue: 'fake engine'}]); - const injector = createInjector([ - {provide: TOKEN, useValue: 'by token'}, - {provide: Engine, useFactory: (v: string) => v, deps: [[TOKEN]]}, - ]); + const engine = injector.get(Engine); + expect(engine).toEqual('fake engine'); + }); - const engine = injector.get(Engine); - expect(engine).toEqual('by token'); - }); + it('should inject dependencies instance of InjectionToken', () => { + const TOKEN = new InjectionToken<string>('token'); - it('should provide to a factory', () => { - function sportsCarFactory(e: any) { return new SportsCar(e); } + const injector = createInjector([ + {provide: TOKEN, useValue: 'by token'}, + {provide: Engine, useFactory: (v: string) => v, deps: [[TOKEN]]}, + ]); - const injector = - createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]); + const engine = injector.get(Engine); + expect(engine).toEqual('by token'); + }); - const car = injector.get(Car); - expect(car).toBeAnInstanceOf(SportsCar); - expect(car.engine).toBeAnInstanceOf(Engine); - }); + it('should provide to a factory', () => { + function sportsCarFactory(e: any) { + return new SportsCar(e); + } - it('should supporting provider to null', () => { - const injector = createInjector([{provide: Engine, useValue: null}]); - const engine = injector.get(Engine); - expect(engine).toBeNull(); - }); + const injector = + createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]); - it('should provide to an alias', () => { - const injector = createInjector([ - Engine, {provide: SportsCar, useClass: SportsCar}, {provide: Car, useExisting: SportsCar} - ]); + const car = injector.get(Car); + expect(car).toBeAnInstanceOf(SportsCar); + expect(car.engine).toBeAnInstanceOf(Engine); + }); - const car = injector.get(Car); - const sportsCar = injector.get(SportsCar); - expect(car).toBeAnInstanceOf(SportsCar); - expect(car).toBe(sportsCar); - }); + it('should supporting provider to null', () => { + const injector = createInjector([{provide: Engine, useValue: null}]); + const engine = injector.get(Engine); + expect(engine).toBeNull(); + }); - it('should support multiProviders', () => { - const injector = createInjector([ - Engine, {provide: Car, useClass: SportsCar, multi: true}, - {provide: Car, useClass: CarWithOptionalEngine, multi: true} - ]); + it('should provide to an alias', () => { + const injector = createInjector([ + Engine, {provide: SportsCar, useClass: SportsCar}, {provide: Car, useExisting: SportsCar} + ]); - const cars = injector.get(Car); - expect(cars.length).toEqual(2); - expect(cars[0]).toBeAnInstanceOf(SportsCar); - expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine); - }); + const car = injector.get(Car); + const sportsCar = injector.get(SportsCar); + expect(car).toBeAnInstanceOf(SportsCar); + expect(car).toBe(sportsCar); + }); - it('should support multiProviders that are created using useExisting', () => { - const injector = - createInjector([Engine, SportsCar, {provide: Car, useExisting: SportsCar, multi: true}]); + it('should support multiProviders', () => { + const injector = createInjector([ + Engine, {provide: Car, useClass: SportsCar, multi: true}, + {provide: Car, useClass: CarWithOptionalEngine, multi: true} + ]); - const cars = injector.get(Car); - expect(cars.length).toEqual(1); - expect(cars[0]).toBe(injector.get(SportsCar)); - }); + const cars = injector.get(Car); + expect(cars.length).toEqual(2); + expect(cars[0]).toBeAnInstanceOf(SportsCar); + expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine); + }); - it('should throw when the aliased provider does not exist', () => { - const injector = createInjector([{provide: 'car', useExisting: SportsCar}]); - const e = `No provider for ${stringify(SportsCar)}! (car -> ${stringify(SportsCar)})`; - expect(() => injector.get('car')).toThrowError(e); - }); + it('should support multiProviders that are created using useExisting', () => { + const injector = + createInjector([Engine, SportsCar, {provide: Car, useExisting: SportsCar, multi: true}]); - it('should handle forwardRef in useExisting', () => { - const injector = createInjector([ - {provide: 'originalEngine', useClass: forwardRef(() => Engine)}, - {provide: 'aliasedEngine', useExisting: <any>forwardRef(() => 'originalEngine')} - ]); - expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine); - }); + const cars = injector.get(Car); + expect(cars.length).toEqual(1); + expect(cars[0]).toBe(injector.get(SportsCar)); + }); - it('should support overriding factory dependencies', () => { - const injector = createInjector( - [Engine, {provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]}]); + it('should throw when the aliased provider does not exist', () => { + const injector = createInjector([{provide: 'car', useExisting: SportsCar}]); + const e = `No provider for ${stringify(SportsCar)}! (car -> ${stringify(SportsCar)})`; + expect(() => injector.get('car')).toThrowError(e); + }); - const car = injector.get(Car); - expect(car).toBeAnInstanceOf(SportsCar); - expect(car.engine).toBeAnInstanceOf(Engine); - }); + it('should handle forwardRef in useExisting', () => { + const injector = createInjector([ + {provide: 'originalEngine', useClass: forwardRef(() => Engine)}, + {provide: 'aliasedEngine', useExisting: <any>forwardRef(() => 'originalEngine')} + ]); + expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine); + }); - it('should support optional dependencies', () => { - const injector = createInjector([CarWithOptionalEngine]); + it('should support overriding factory dependencies', () => { + const injector = createInjector( + [Engine, {provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]}]); - const car = injector.get(CarWithOptionalEngine); - expect(car.engine).toEqual(null); - }); + const car = injector.get(Car); + expect(car).toBeAnInstanceOf(SportsCar); + expect(car.engine).toBeAnInstanceOf(Engine); + }); - it('should flatten passed-in providers', () => { - const injector = createInjector([[[Engine, Car]]]); + it('should support optional dependencies', () => { + const injector = createInjector([CarWithOptionalEngine]); - const car = injector.get(Car); - expect(car).toBeAnInstanceOf(Car); - }); + const car = injector.get(CarWithOptionalEngine); + expect(car.engine).toEqual(null); + }); - it('should use the last provider when there are multiple providers for same token', () => { - const injector = createInjector( - [{provide: Engine, useClass: Engine}, {provide: Engine, useClass: TurboEngine}]); + it('should flatten passed-in providers', () => { + const injector = createInjector([[[Engine, Car]]]); - expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); - }); + const car = injector.get(Car); + expect(car).toBeAnInstanceOf(Car); + }); - it('should use non-type tokens', () => { - const injector = createInjector([{provide: 'token', useValue: 'value'}]); + it('should use the last provider when there are multiple providers for same token', () => { + const injector = createInjector( + [{provide: Engine, useClass: Engine}, {provide: Engine, useClass: TurboEngine}]); - expect(injector.get('token')).toEqual('value'); - }); + expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); + }); - it('should throw when given invalid providers', () => { - expect(() => createInjector(<any>['blah'])) - .toThrowError( - 'Invalid provider - only instances of Provider and Type are allowed, got: blah'); - }); + it('should use non-type tokens', () => { + const injector = createInjector([{provide: 'token', useValue: 'value'}]); - it('should provide itself', () => { - const parent = createInjector([]); - const child = parent.resolveAndCreateChild([]); + expect(injector.get('token')).toEqual('value'); + }); - expect(child.get(Injector)).toBe(child); - }); + it('should throw when given invalid providers', () => { + expect(() => createInjector(<any>['blah'])) + .toThrowError( + 'Invalid provider - only instances of Provider and Type are allowed, got: blah'); + }); - it('should throw when no provider defined', () => { - const injector = createInjector([]); - expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!'); - }); + it('should provide itself', () => { + const parent = createInjector([]); + const child = parent.resolveAndCreateChild([]); - it('should show the full path when no provider', () => { - const injector = createInjector([CarWithDashboard, Engine, Dashboard]); - expect(() => injector.get(CarWithDashboard)) - .toThrowError( - `No provider for DashboardSoftware! (${stringify(CarWithDashboard)} -> ${stringify(Dashboard)} -> DashboardSoftware)`); - }); + expect(child.get(Injector)).toBe(child); + }); - it('should throw when trying to instantiate a cyclic dependency', () => { - const injector = createInjector([Car, {provide: Engine, useClass: CyclicEngine}]); + it('should throw when no provider defined', () => { + const injector = createInjector([]); + expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!'); + }); - expect(() => injector.get(Car)) - .toThrowError( - `Cannot instantiate cyclic dependency! (${stringify(Car)} -> ${stringify(Engine)} -> ${stringify(Car)})`); - }); + it('should show the full path when no provider', () => { + const injector = createInjector([CarWithDashboard, Engine, Dashboard]); + expect(() => injector.get(CarWithDashboard)) + .toThrowError(`No provider for DashboardSoftware! (${stringify(CarWithDashboard)} -> ${ + stringify(Dashboard)} -> DashboardSoftware)`); + }); - it('should show the full path when error happens in a constructor', () => { - const providers = - ReflectiveInjector.resolve([Car, {provide: Engine, useClass: BrokenEngine}]); - const injector = new ReflectiveInjector_(providers); - - try { - injector.get(Car); - throw 'Must throw'; - } catch (e) { - expect(e.message).toContain( - `Error during instantiation of Engine! (${stringify(Car)} -> Engine)`); - expect(getOriginalError(e) instanceof Error).toBeTruthy(); - expect(e.keys[0].token).toEqual(Engine); - } - }); + it('should throw when trying to instantiate a cyclic dependency', () => { + const injector = createInjector([Car, {provide: Engine, useClass: CyclicEngine}]); - it('should instantiate an object after a failed attempt', () => { - let isBroken = true; + expect(() => injector.get(Car)) + .toThrowError(`Cannot instantiate cyclic dependency! (${stringify(Car)} -> ${ + stringify(Engine)} -> ${stringify(Car)})`); + }); - const injector = createInjector([ - Car, {provide: Engine, useFactory: (() => isBroken ? new BrokenEngine() : new Engine())} - ]); + it('should show the full path when error happens in a constructor', () => { + const providers = ReflectiveInjector.resolve([Car, {provide: Engine, useClass: BrokenEngine}]); + const injector = new ReflectiveInjector_(providers); + + try { + injector.get(Car); + throw 'Must throw'; + } catch (e) { + expect(e.message).toContain( + `Error during instantiation of Engine! (${stringify(Car)} -> Engine)`); + expect(getOriginalError(e) instanceof Error).toBeTruthy(); + expect(e.keys[0].token).toEqual(Engine); + } + }); - expect(() => injector.get(Car)) - .toThrowError('Broken Engine: Error during instantiation of Engine! (Car -> Engine).'); + it('should instantiate an object after a failed attempt', () => { + let isBroken = true; - isBroken = false; + const injector = createInjector( + [Car, {provide: Engine, useFactory: (() => isBroken ? new BrokenEngine() : new Engine())}]); - expect(injector.get(Car)).toBeAnInstanceOf(Car); - }); + expect(() => injector.get(Car)) + .toThrowError('Broken Engine: Error during instantiation of Engine! (Car -> Engine).'); - it('should support null values', () => { - const injector = createInjector([{provide: 'null', useValue: null}]); - expect(injector.get('null')).toBe(null); - }); + isBroken = false; + expect(injector.get(Car)).toBeAnInstanceOf(Car); }); + it('should support null values', () => { + const injector = createInjector([{provide: 'null', useValue: null}]); + expect(injector.get('null')).toBe(null); + }); +}); - describe('child', () => { - it('should load instances from parent injector', () => { - const parent = ReflectiveInjector.resolveAndCreate([Engine]); - const child = parent.resolveAndCreateChild([]); - const engineFromParent = parent.get(Engine); - const engineFromChild = child.get(Engine); +describe('child', () => { + it('should load instances from parent injector', () => { + const parent = ReflectiveInjector.resolveAndCreate([Engine]); + const child = parent.resolveAndCreateChild([]); - expect(engineFromChild).toBe(engineFromParent); - }); + const engineFromParent = parent.get(Engine); + const engineFromChild = child.get(Engine); - it('should not use the child providers when resolving the dependencies of a parent provider', - () => { - const parent = ReflectiveInjector.resolveAndCreate([Car, Engine]); - const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]); + expect(engineFromChild).toBe(engineFromParent); + }); - const carFromChild = child.get(Car); - expect(carFromChild.engine).toBeAnInstanceOf(Engine); - }); + it('should not use the child providers when resolving the dependencies of a parent provider', + () => { + const parent = ReflectiveInjector.resolveAndCreate([Car, Engine]); + const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]); - it('should create new instance in a child injector', () => { - const parent = ReflectiveInjector.resolveAndCreate([Engine]); - const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]); + const carFromChild = child.get(Car); + expect(carFromChild.engine).toBeAnInstanceOf(Engine); + }); - const engineFromParent = parent.get(Engine); - const engineFromChild = child.get(Engine); + it('should create new instance in a child injector', () => { + const parent = ReflectiveInjector.resolveAndCreate([Engine]); + const child = parent.resolveAndCreateChild([{provide: Engine, useClass: TurboEngine}]); - expect(engineFromParent).not.toBe(engineFromChild); - expect(engineFromChild).toBeAnInstanceOf(TurboEngine); - }); + const engineFromParent = parent.get(Engine); + const engineFromChild = child.get(Engine); - it('should give access to parent', () => { - const parent = ReflectiveInjector.resolveAndCreate([]); - const child = parent.resolveAndCreateChild([]); - expect(child.parent).toBe(parent); - }); + expect(engineFromParent).not.toBe(engineFromChild); + expect(engineFromChild).toBeAnInstanceOf(TurboEngine); }); - describe('resolveAndInstantiate', () => { - it('should instantiate an object in the context of the injector', () => { - const inj = ReflectiveInjector.resolveAndCreate([Engine]); - const car = inj.resolveAndInstantiate(Car); - expect(car).toBeAnInstanceOf(Car); - expect(car.engine).toBe(inj.get(Engine)); - }); - - it('should not store the instantiated object in the injector', () => { - const inj = ReflectiveInjector.resolveAndCreate([Engine]); - inj.resolveAndInstantiate(Car); - expect(() => inj.get(Car)).toThrowError(); - }); + it('should give access to parent', () => { + const parent = ReflectiveInjector.resolveAndCreate([]); + const child = parent.resolveAndCreateChild([]); + expect(child.parent).toBe(parent); }); - - describe('instantiate', () => { - it('should instantiate an object in the context of the injector', () => { - const inj = ReflectiveInjector.resolveAndCreate([Engine]); - const car = inj.instantiateResolved(ReflectiveInjector.resolve([Car])[0]); - expect(car).toBeAnInstanceOf(Car); - expect(car.engine).toBe(inj.get(Engine)); - }); +}); + +describe('resolveAndInstantiate', () => { + it('should instantiate an object in the context of the injector', () => { + const inj = ReflectiveInjector.resolveAndCreate([Engine]); + const car = inj.resolveAndInstantiate(Car); + expect(car).toBeAnInstanceOf(Car); + expect(car.engine).toBe(inj.get(Engine)); }); - describe('depedency resolution', () => { - describe('@Self()', () => { - it('should return a dependency from self', () => { - const inj = ReflectiveInjector.resolveAndCreate([ - Engine, - {provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]} - ]); - - expect(inj.get(Car)).toBeAnInstanceOf(Car); - }); + it('should not store the instantiated object in the injector', () => { + const inj = ReflectiveInjector.resolveAndCreate([Engine]); + inj.resolveAndInstantiate(Car); + expect(() => inj.get(Car)).toThrowError(); + }); +}); + +describe('instantiate', () => { + it('should instantiate an object in the context of the injector', () => { + const inj = ReflectiveInjector.resolveAndCreate([Engine]); + const car = inj.instantiateResolved(ReflectiveInjector.resolve([Car])[0]); + expect(car).toBeAnInstanceOf(Car); + expect(car.engine).toBe(inj.get(Engine)); + }); +}); - it('should throw when not requested provider on self', () => { - const parent = ReflectiveInjector.resolveAndCreate([Engine]); - const child = parent.resolveAndCreateChild( - [{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}]); +describe('depedency resolution', () => { + describe('@Self()', () => { + it('should return a dependency from self', () => { + const inj = ReflectiveInjector.resolveAndCreate([ + Engine, {provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]} + ]); - expect(() => child.get(Car)) - .toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`); - }); + expect(inj.get(Car)).toBeAnInstanceOf(Car); }); - describe('default', () => { - it('should not skip self', () => { - const parent = ReflectiveInjector.resolveAndCreate([Engine]); - const child = parent.resolveAndCreateChild([ - {provide: Engine, useClass: TurboEngine}, - {provide: Car, useFactory: (e: Engine) => new Car(e), deps: [Engine]} - ]); + it('should throw when not requested provider on self', () => { + const parent = ReflectiveInjector.resolveAndCreate([Engine]); + const child = parent.resolveAndCreateChild( + [{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}]); - expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine); - }); + expect(() => child.get(Car)) + .toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`); }); }); - describe('resolve', () => { - it('should resolve and flatten', () => { - const providers = ReflectiveInjector.resolve([Engine, [BrokenEngine]]); - providers.forEach(function(b) { - if (!b) return; // the result is a sparse array - expect(b instanceof ResolvedReflectiveProvider_).toBe(true); - }); - }); + describe('default', () => { + it('should not skip self', () => { + const parent = ReflectiveInjector.resolveAndCreate([Engine]); + const child = parent.resolveAndCreateChild([ + {provide: Engine, useClass: TurboEngine}, + {provide: Car, useFactory: (e: Engine) => new Car(e), deps: [Engine]} + ]); - it('should support multi providers', () => { - const provider = ReflectiveInjector.resolve([ - {provide: Engine, useClass: BrokenEngine, multi: true}, - {provide: Engine, useClass: TurboEngine, multi: true} - ])[0]; + expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine); + }); + }); +}); - expect(provider.key.token).toBe(Engine); - expect(provider.multiProvider).toEqual(true); - expect(provider.resolvedFactories.length).toEqual(2); +describe('resolve', () => { + it('should resolve and flatten', () => { + const providers = ReflectiveInjector.resolve([Engine, [BrokenEngine]]); + providers.forEach(function(b) { + if (!b) return; // the result is a sparse array + expect(b instanceof ResolvedReflectiveProvider_).toBe(true); }); + }); + it('should support multi providers', () => { + const provider = ReflectiveInjector.resolve([ + {provide: Engine, useClass: BrokenEngine, multi: true}, + {provide: Engine, useClass: TurboEngine, multi: true} + ])[0]; - it('should support providers as hash', () => { - const provider = ReflectiveInjector.resolve([ - {provide: Engine, useClass: BrokenEngine, multi: true}, - {provide: Engine, useClass: TurboEngine, multi: true} - ])[0]; + expect(provider.key.token).toBe(Engine); + expect(provider.multiProvider).toEqual(true); + expect(provider.resolvedFactories.length).toEqual(2); + }); - expect(provider.key.token).toBe(Engine); - expect(provider.multiProvider).toEqual(true); - expect(provider.resolvedFactories.length).toEqual(2); - }); - it('should support multi providers with only one provider', () => { - const provider = - ReflectiveInjector.resolve([{provide: Engine, useClass: BrokenEngine, multi: true}])[0]; + it('should support providers as hash', () => { + const provider = ReflectiveInjector.resolve([ + {provide: Engine, useClass: BrokenEngine, multi: true}, + {provide: Engine, useClass: TurboEngine, multi: true} + ])[0]; - expect(provider.key.token).toBe(Engine); - expect(provider.multiProvider).toEqual(true); - expect(provider.resolvedFactories.length).toEqual(1); - }); + expect(provider.key.token).toBe(Engine); + expect(provider.multiProvider).toEqual(true); + expect(provider.resolvedFactories.length).toEqual(2); + }); - it('should throw when mixing multi providers with regular providers', () => { - expect(() => { - ReflectiveInjector.resolve( - [{provide: Engine, useClass: BrokenEngine, multi: true}, Engine]); - }).toThrowError(/Cannot mix multi providers and regular providers/); + it('should support multi providers with only one provider', () => { + const provider = + ReflectiveInjector.resolve([{provide: Engine, useClass: BrokenEngine, multi: true}])[0]; - expect(() => { - ReflectiveInjector.resolve( - [Engine, {provide: Engine, useClass: BrokenEngine, multi: true}]); - }).toThrowError(/Cannot mix multi providers and regular providers/); - }); + expect(provider.key.token).toBe(Engine); + expect(provider.multiProvider).toEqual(true); + expect(provider.resolvedFactories.length).toEqual(1); + }); - it('should resolve forward references', () => { - const providers = ReflectiveInjector.resolve([ - forwardRef(() => Engine), - [{provide: forwardRef(() => BrokenEngine), useClass: forwardRef(() => Engine)}], { - provide: forwardRef(() => String), - useFactory: () => 'OK', - deps: [forwardRef(() => Engine)] - } - ]); + it('should throw when mixing multi providers with regular providers', () => { + expect(() => { + ReflectiveInjector.resolve([{provide: Engine, useClass: BrokenEngine, multi: true}, Engine]); + }).toThrowError(/Cannot mix multi providers and regular providers/); - const engineProvider = providers[0]; - const brokenEngineProvider = providers[1]; - const stringProvider = providers[2]; + expect(() => { + ReflectiveInjector.resolve([Engine, {provide: Engine, useClass: BrokenEngine, multi: true}]); + }).toThrowError(/Cannot mix multi providers and regular providers/); + }); - expect(engineProvider.resolvedFactories[0].factory() instanceof Engine).toBe(true); - expect(brokenEngineProvider.resolvedFactories[0].factory() instanceof Engine).toBe(true); - expect(stringProvider.resolvedFactories[0].dependencies[0].key) - .toEqual(ReflectiveKey.get(Engine)); - }); + it('should resolve forward references', () => { + const providers = ReflectiveInjector.resolve([ + forwardRef(() => Engine), + [{provide: forwardRef(() => BrokenEngine), useClass: forwardRef(() => Engine)}], + {provide: forwardRef(() => String), useFactory: () => 'OK', deps: [forwardRef(() => Engine)]} + ]); + + const engineProvider = providers[0]; + const brokenEngineProvider = providers[1]; + const stringProvider = providers[2]; + + expect(engineProvider.resolvedFactories[0].factory() instanceof Engine).toBe(true); + expect(brokenEngineProvider.resolvedFactories[0].factory() instanceof Engine).toBe(true); + expect(stringProvider.resolvedFactories[0].dependencies[0].key) + .toEqual(ReflectiveKey.get(Engine)); + }); - it('should support overriding factory dependencies with dependency annotations', () => { - const providers = ReflectiveInjector.resolve([{ - provide: 'token', - useFactory: (e: any /** TODO #9100 */) => 'result', - deps: [[new Inject('dep')]] - }]); + it('should support overriding factory dependencies with dependency annotations', () => { + const providers = ReflectiveInjector.resolve([{ + provide: 'token', + useFactory: (e: any /** TODO #9100 */) => 'result', + deps: [[new Inject('dep')]] + }]); - const provider = providers[0]; + const provider = providers[0]; - expect(provider.resolvedFactories[0].dependencies[0].key.token).toEqual('dep'); - }); + expect(provider.resolvedFactories[0].dependencies[0].key.token).toEqual('dep'); + }); - it('should allow declaring dependencies with flat arrays', () => { - const resolved = ReflectiveInjector.resolve( - [{provide: 'token', useFactory: (e: any) => e, deps: [new Inject('dep')]}]); - const nestedResolved = ReflectiveInjector.resolve( - [{provide: 'token', useFactory: (e: any) => e, deps: [[new Inject('dep')]]}]); - expect(resolved[0].resolvedFactories[0].dependencies[0].key.token) - .toEqual(nestedResolved[0].resolvedFactories[0].dependencies[0].key.token); - }); + it('should allow declaring dependencies with flat arrays', () => { + const resolved = ReflectiveInjector.resolve( + [{provide: 'token', useFactory: (e: any) => e, deps: [new Inject('dep')]}]); + const nestedResolved = ReflectiveInjector.resolve( + [{provide: 'token', useFactory: (e: any) => e, deps: [[new Inject('dep')]]}]); + expect(resolved[0].resolvedFactories[0].dependencies[0].key.token) + .toEqual(nestedResolved[0].resolvedFactories[0].dependencies[0].key.token); }); +}); - describe('displayName', () => { - it('should work', () => { - expect((<ReflectiveInjector_>ReflectiveInjector.resolveAndCreate([Engine, BrokenEngine])) - .displayName) - .toEqual('ReflectiveInjector(providers: [ "Engine" , "BrokenEngine" ])'); - }); +describe('displayName', () => { + it('should work', () => { + expect((<ReflectiveInjector_>ReflectiveInjector.resolveAndCreate([Engine, BrokenEngine])) + .displayName) + .toEqual('ReflectiveInjector(providers: [ "Engine" , "BrokenEngine" ])'); }); +}); })(); diff --git a/packages/core/test/di/reflective_key_spec.ts b/packages/core/test/di/reflective_key_spec.ts index fe82a254a14d1..1b9e3588a1934 100644 --- a/packages/core/test/di/reflective_key_spec.ts +++ b/packages/core/test/di/reflective_key_spec.ts @@ -12,16 +12,20 @@ import {KeyRegistry} from '@angular/core/src/di/reflective_key'; describe('key', function() { let registry: KeyRegistry; - beforeEach(function() { registry = new KeyRegistry(); }); + beforeEach(function() { + registry = new KeyRegistry(); + }); - it('should be equal to another key if type is the same', - function() { expect(registry.get('car')).toBe(registry.get('car')); }); + it('should be equal to another key if type is the same', function() { + expect(registry.get('car')).toBe(registry.get('car')); + }); - it('should not be equal to another key if types are different', - function() { expect(registry.get('car')).not.toBe(registry.get('porsche')); }); - - it('should return the passed in key', - function() { expect(registry.get(registry.get('car'))).toBe(registry.get('car')); }); + it('should not be equal to another key if types are different', function() { + expect(registry.get('car')).not.toBe(registry.get('porsche')); + }); + it('should return the passed in key', function() { + expect(registry.get(registry.get('car'))).toBe(registry.get('car')); + }); }); } diff --git a/packages/core/test/di/static_injector_spec.ts b/packages/core/test/di/static_injector_spec.ts index 007f9f7b4ed79..59386386200da 100644 --- a/packages/core/test/di/static_injector_spec.ts +++ b/packages/core/test/di/static_injector_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, InjectFlags, InjectionToken, Injector, Optional, Self, SkipSelf, forwardRef} from '@angular/core'; +import {forwardRef, Inject, InjectFlags, InjectionToken, Injector, Optional, Self, SkipSelf} from '@angular/core'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, modifiedInIvy} from '@angular/private/testing'; @@ -18,7 +18,9 @@ class Engine { class BrokenEngine { static PROVIDER = {provide: Engine, useClass: BrokenEngine, deps: []}; - constructor() { throw new Error('Broken Engine'); } + constructor() { + throw new Error('Broken Engine'); + } } class DashboardSoftware { @@ -75,7 +77,7 @@ class NoAnnotations { constructor(secretDependency: any) {} } -function factoryFn(a: any){} +function factoryFn(a: any) {} { const dynamicProviders = [ @@ -88,7 +90,6 @@ function factoryFn(a: any){} ]; modifiedInIvy('Ivy uses R3Injector').describe(`StaticInjector`, () => { - it('should instantiate a class without dependencies', () => { const injector = Injector.create([Engine.PROVIDER]); const engine = injector.get(Engine); @@ -133,7 +134,9 @@ function factoryFn(a: any){} }); it('should provide to a factory', () => { - function sportsCarFactory(e: any) { return new SportsCar(e); } + function sportsCarFactory(e: any) { + return new SportsCar(e); + } const injector = Injector.create( [Engine.PROVIDER, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]); @@ -187,26 +190,22 @@ function factoryFn(a: any){} it('should throw when the aliased provider does not exist', () => { const injector = Injector.create([{provide: 'car', useExisting: SportsCar}]); - const e = - `StaticInjectorError[car -> ${stringify(SportsCar)}]: \n NullInjectorError: No provider for ${stringify(SportsCar)}!`; + const e = `StaticInjectorError[car -> ${ + stringify(SportsCar)}]: \n NullInjectorError: No provider for ${stringify(SportsCar)}!`; expect(() => injector.get('car')).toThrowError(e); }); it('should handle forwardRef in useExisting', () => { const injector = Injector.create([ - {provide: 'originalEngine', useClass: forwardRef(() => Engine), deps: []}, { - provide: 'aliasedEngine', - useExisting: <any>forwardRef(() => 'originalEngine'), - deps: [] - } + {provide: 'originalEngine', useClass: forwardRef(() => Engine), deps: []}, + {provide: 'aliasedEngine', useExisting: <any>forwardRef(() => 'originalEngine'), deps: []} ]); expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine); }); it('should support overriding factory dependencies', () => { const injector = Injector.create([ - Engine.PROVIDER, - {provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]} + Engine.PROVIDER, {provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]} ]); const car = injector.get<Car>(Car); @@ -249,9 +248,9 @@ function factoryFn(a: any){} }); it('should throw when missing deps', () => { - expect(() => Injector.create(<any>[{provide: Engine, useClass: Engine}])) - .toThrowError( - 'StaticInjectorError[{provide:Engine, useClass:Engine}]: \'deps\' required'); + expect(() => Injector.create(<any>[ + {provide: Engine, useClass: Engine} + ])).toThrowError('StaticInjectorError[{provide:Engine, useClass:Engine}]: \'deps\' required'); }); it('should throw when using reflective API', () => { @@ -289,7 +288,8 @@ function factoryFn(a: any){} Injector.create([CarWithDashboard.PROVIDER, Engine.PROVIDER, Dashboard.PROVIDER]); expect(() => injector.get(CarWithDashboard)) .toThrowError( - `StaticInjectorError[${stringify(CarWithDashboard)} -> ${stringify(Dashboard)} -> DashboardSoftware]: \n` + + `StaticInjectorError[${stringify(CarWithDashboard)} -> ${ + stringify(Dashboard)} -> DashboardSoftware]: \n` + ' NullInjectorError: No provider for DashboardSoftware!'); }); @@ -297,14 +297,21 @@ function factoryFn(a: any){} const injector = Injector.create([Car.PROVIDER, CyclicEngine.PROVIDER]); expect(() => injector.get(Car)) - .toThrowError( - `StaticInjectorError[${stringify(Car)} -> ${stringify(Engine)} -> ${stringify(Car)}]: Circular dependency`); + .toThrowError(`StaticInjectorError[${stringify(Car)} -> ${stringify(Engine)} -> ${ + stringify(Car)}]: Circular dependency`); }); it('should show the full path when error happens in a constructor', () => { const error = new Error('MyError'); - const injector = Injector.create( - [Car.PROVIDER, {provide: Engine, useFactory: () => { throw error; }, deps: []}]); + const injector = Injector.create([ + Car.PROVIDER, { + provide: Engine, + useFactory: () => { + throw error; + }, + deps: [] + } + ]); try { injector.get(Car); @@ -345,7 +352,6 @@ function factoryFn(a: any){} expect(injector.get('null')).toBe(null); expect(injector.get('undefined')).toBe(undefined); }); - }); @@ -495,8 +501,7 @@ function factoryFn(a: any){} it('should support overriding factory dependencies with dependency annotations', () => { const injector = Injector.create([ - Engine.PROVIDER, - {provide: 'token', useFactory: (e: any) => e, deps: [[new Inject(Engine)]]} + Engine.PROVIDER, {provide: 'token', useFactory: (e: any) => e, deps: [[new Inject(Engine)]]} ]); expect(injector.get('token')).toBeAnInstanceOf(Engine); diff --git a/packages/core/test/directive_lifecycle_integration_spec.ts b/packages/core/test/directive_lifecycle_integration_spec.ts index 84e944188a377..ab4e5e9e61d90 100644 --- a/packages/core/test/directive_lifecycle_integration_spec.ts +++ b/packages/core/test/directive_lifecycle_integration_spec.ts @@ -8,7 +8,7 @@ import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnInit} from '@angular/core'; import {Component, Directive} from '@angular/core/src/metadata'; -import {TestBed, inject} from '@angular/core/testing'; +import {inject, TestBed} from '@angular/core/testing'; import {Log} from '@angular/core/testing/src/testing_internal'; describe('directive lifecycle integration spec', () => { @@ -27,7 +27,9 @@ describe('directive lifecycle integration spec', () => { .overrideComponent(MyComp5, {set: {template: '<div [field]="123" lifecycle></div>'}}); }); - beforeEach(inject([Log], (_log: any) => { log = _log; })); + beforeEach(inject([Log], (_log: any) => { + log = _log; + })); it('should invoke lifecycle methods ngOnChanges > ngOnInit > ngDoCheck > ngAfterContentChecked', () => { @@ -51,7 +53,9 @@ describe('directive lifecycle integration spec', () => { @Directive({selector: '[lifecycle-dir]'}) class LifecycleDir implements DoCheck { constructor(private _log: Log) {} - ngDoCheck() { this._log.add('child_ngDoCheck'); } + ngDoCheck() { + this._log.add('child_ngDoCheck'); + } } @Component({ @@ -59,24 +63,38 @@ class LifecycleDir implements DoCheck { inputs: ['field'], template: `<div lifecycle-dir></div>`, }) -class LifecycleCmp implements OnChanges, - OnInit, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked { +class LifecycleCmp implements OnChanges, OnInit, DoCheck, AfterContentInit, AfterContentChecked, + AfterViewInit, AfterViewChecked { field: any /** TODO #9100 */; constructor(private _log: Log) {} - ngOnChanges(_: any /** TODO #9100 */) { this._log.add('ngOnChanges'); } + ngOnChanges(_: any /** TODO #9100 */) { + this._log.add('ngOnChanges'); + } - ngOnInit() { this._log.add('ngOnInit'); } + ngOnInit() { + this._log.add('ngOnInit'); + } - ngDoCheck() { this._log.add('ngDoCheck'); } + ngDoCheck() { + this._log.add('ngDoCheck'); + } - ngAfterContentInit() { this._log.add('ngAfterContentInit'); } + ngAfterContentInit() { + this._log.add('ngAfterContentInit'); + } - ngAfterContentChecked() { this._log.add('ngAfterContentChecked'); } + ngAfterContentChecked() { + this._log.add('ngAfterContentChecked'); + } - ngAfterViewInit() { this._log.add('ngAfterViewInit'); } + ngAfterViewInit() { + this._log.add('ngAfterViewInit'); + } - ngAfterViewChecked() { this._log.add('ngAfterViewChecked'); } + ngAfterViewChecked() { + this._log.add('ngAfterViewChecked'); + } } @Component({selector: 'my-comp'}) diff --git a/packages/core/test/dom/dom_adapter_spec.ts b/packages/core/test/dom/dom_adapter_spec.ts index fc4c540e3a791..d858eac3c2061 100644 --- a/packages/core/test/dom/dom_adapter_spec.ts +++ b/packages/core/test/dom/dom_adapter_spec.ts @@ -40,8 +40,9 @@ import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; describe('getBaseHref', () => { beforeEach(() => getDOM().resetBaseElement()); - it('should return null if base element is absent', - () => { expect(getDOM().getBaseHref(defaultDoc)).toBeNull(); }); + it('should return null if base element is absent', () => { + expect(getDOM().getBaseHref(defaultDoc)).toBeNull(); + }); it('should return the value of the base element', () => { const baseEl = getDOM().createElement('base'); @@ -62,7 +63,7 @@ import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; const headEl = defaultDoc.head; headEl.appendChild(baseEl); - const baseHref = getDOM().getBaseHref(defaultDoc) !; + const baseHref = getDOM().getBaseHref(defaultDoc)!; headEl.removeChild(baseEl); getDOM().resetBaseElement(); @@ -70,7 +71,5 @@ import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; }); }); } - - }); } diff --git a/packages/core/test/dom/shim_spec.ts b/packages/core/test/dom/shim_spec.ts index 07b2bd1738dfd..3d09e8d7d7c11 100644 --- a/packages/core/test/dom/shim_spec.ts +++ b/packages/core/test/dom/shim_spec.ts @@ -10,7 +10,6 @@ import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; { describe('Shim', () => { - it('should provide correct function.name ', () => { const functionWithoutName = identity(() => function(_: any /** TODO #9100 */) {}); function foo(_: any /** TODO #9100 */) {} @@ -18,7 +17,6 @@ import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; expect((<any>functionWithoutName).name).toBeFalsy(); expect((<any>foo).name).toEqual('foo'); }); - }); } diff --git a/packages/core/test/error_handler_spec.ts b/packages/core/test/error_handler_spec.ts index e52fd36ebad1e..d566d61233f26 100644 --- a/packages/core/test/error_handler_spec.ts +++ b/packages/core/test/error_handler_spec.ts @@ -12,66 +12,73 @@ import {ErrorHandler} from '../src/error_handler'; class MockConsole { res: any[][] = []; - error(...s: any[]): void { this.res.push(s); } + error(...s: any[]): void { + this.res.push(s); + } } (function() { - function errorToString(error: any) { - const logger = new MockConsole(); - const errorHandler = new ErrorHandler(); - (errorHandler as any)._console = logger as any; - errorHandler.handleError(error); - return logger.res.map(line => line.join('#')).join('\n'); - } +function errorToString(error: any) { + const logger = new MockConsole(); + const errorHandler = new ErrorHandler(); + (errorHandler as any)._console = logger as any; + errorHandler.handleError(error); + return logger.res.map(line => line.join('#')).join('\n'); +} - describe('ErrorHandler', () => { - it('should output exception', () => { - const e = errorToString(new Error('message!')); - expect(e).toContain('message!'); - }); +describe('ErrorHandler', () => { + it('should output exception', () => { + const e = errorToString(new Error('message!')); + expect(e).toContain('message!'); + }); - describe('context', () => { - it('should print nested context', () => { - const cause = new Error('message!'); - const context = { source: 'context!', toString() { return 'Context'; } } as any; - const original = debugError(cause, context); - const e = errorToString(wrappedError('message', original)); - expect(e).toEqual(`ERROR#Error: message caused by: Error in context! caused by: message! + describe('context', () => { + it('should print nested context', () => { + const cause = new Error('message!'); + const context = { + source: 'context!', + toString() { + return 'Context'; + } + } as any; + const original = debugError(cause, context); + const e = errorToString(wrappedError('message', original)); + expect(e).toEqual(`ERROR#Error: message caused by: Error in context! caused by: message! ORIGINAL ERROR#Error: message! ERROR CONTEXT#Context`); - }); }); + }); - describe('original exception', () => { - it('should print original exception message if available (original is Error)', () => { - const realOriginal = new Error('inner'); - const original = wrappedError('wrapped', realOriginal); - const e = errorToString(wrappedError('wrappedwrapped', original)); - expect(e).toContain('inner'); - }); + describe('original exception', () => { + it('should print original exception message if available (original is Error)', () => { + const realOriginal = new Error('inner'); + const original = wrappedError('wrapped', realOriginal); + const e = errorToString(wrappedError('wrappedwrapped', original)); + expect(e).toContain('inner'); + }); - it('should print original exception message if available (original is not Error)', () => { - const realOriginal = new Error('custom'); - const original = wrappedError('wrapped', realOriginal); - const e = errorToString(wrappedError('wrappedwrapped', original)); - expect(e).toContain('custom'); - }); + it('should print original exception message if available (original is not Error)', () => { + const realOriginal = new Error('custom'); + const original = wrappedError('wrapped', realOriginal); + const e = errorToString(wrappedError('wrappedwrapped', original)); + expect(e).toContain('custom'); }); + }); - it('should use the error logger on the error', () => { - const err = new Error('test'); - const console = new MockConsole(); - const errorHandler = new ErrorHandler(); - (errorHandler as any)._console = console as any; - const logger = jasmine.createSpy('logger'); - (err as any)[ERROR_LOGGER] = logger; + it('should use the error logger on the error', () => { + const err = new Error('test'); + const console = new MockConsole(); + const errorHandler = new ErrorHandler(); + (errorHandler as any)._console = console as any; + const logger = jasmine.createSpy('logger'); + (err as any)[ERROR_LOGGER] = logger; - errorHandler.handleError(err); + errorHandler.handleError(err); - expect(console.res).toEqual([]); - expect(logger).toHaveBeenCalledWith(console, 'ERROR', err); - }); + expect(console.res).toEqual([]); + expect(logger).toHaveBeenCalledWith(console, 'ERROR', err); }); +}); })(); function debugError(originalError: any, context: any): Error { diff --git a/packages/core/test/event_emitter_spec.ts b/packages/core/test/event_emitter_spec.ts index 0b46ecc4eca4f..2edecec58d5c9 100644 --- a/packages/core/test/event_emitter_spec.ts +++ b/packages/core/test/event_emitter_spec.ts @@ -15,7 +15,9 @@ import {EventEmitter} from '../src/event_emitter'; describe('EventEmitter', () => { let emitter: EventEmitter<any>; - beforeEach(() => { emitter = new EventEmitter(); }); + beforeEach(() => { + emitter = new EventEmitter(); + }); it('should call the next callback', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { @@ -42,20 +44,34 @@ import {EventEmitter} from '../src/event_emitter'; it('should work when no throw callback is provided', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - emitter.subscribe({next: () => {}, error: (_: any) => { async.done(); }}); + emitter.subscribe({ + next: () => {}, + error: (_: any) => { + async.done(); + } + }); emitter.error('Boom'); })); it('should call the return callback', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - emitter.subscribe( - {next: () => {}, error: (_: any) => {}, complete: () => { async.done(); }}); + emitter.subscribe({ + next: () => {}, + error: (_: any) => {}, + complete: () => { + async.done(); + } + }); emitter.complete(); })); it('should subscribe to the wrapper synchronously', () => { let called = false; - emitter.subscribe({next: (value: any) => { called = true; }}); + emitter.subscribe({ + next: (value: any) => { + called = true; + } + }); emitter.emit(99); expect(called).toBe(true); @@ -117,7 +133,6 @@ import {EventEmitter} from '../src/event_emitter'; log.push(1); e.emit(2); log.push(3); - })); it('reports whether it has subscribers', () => { @@ -161,8 +176,16 @@ import {EventEmitter} from '../src/event_emitter'; it('error thrown inside an Rx chain propagates to the error handler and disposes the chain', () => { let errorPropagated = false; - emitter.pipe(filter(() => { throw new Error(); }), ) - .subscribe(() => {}, err => errorPropagated = true, ); + emitter + .pipe( + filter(() => { + throw new Error(); + }), + ) + .subscribe( + () => {}, + err => errorPropagated = true, + ); emitter.next(1); @@ -172,7 +195,11 @@ import {EventEmitter} from '../src/event_emitter'; it('error sent by EventEmitter should dispose the Rx chain and remove subscribers', () => { let errorPropagated = false; - emitter.pipe(filter(() => true)).subscribe(() => {}, err => errorPropagated = true, ); + emitter.pipe(filter(() => true)) + .subscribe( + () => {}, + err => errorPropagated = true, + ); emitter.error(1); diff --git a/packages/core/test/fake_async_spec.ts b/packages/core/test/fake_async_spec.ts index ddf44dfa29b49..30c7bed06db3c 100644 --- a/packages/core/test/fake_async_spec.ts +++ b/packages/core/test/fake_async_spec.ts @@ -7,7 +7,7 @@ */ import {discardPeriodicTasks, fakeAsync, flush, flushMicrotasks, tick} from '@angular/core/testing'; -import {Log, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; +import {beforeEach, describe, inject, it, Log} from '@angular/core/testing/src/testing_internal'; import {EventManager} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -18,7 +18,9 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' describe('fake async', () => { it('should run synchronous code', () => { let ran = false; - fakeAsync(() => { ran = true; })(); + fakeAsync(() => { + ran = true; + })(); expect(ran).toEqual(true); }); @@ -37,26 +39,35 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should throw on nested calls', () => { expect(() => { - fakeAsync(() => { fakeAsync((): any /** TODO #9100 */ => null)(); })(); + fakeAsync(() => { + fakeAsync((): any /** TODO #9100 */ => null)(); + })(); }).toThrowError('fakeAsync() calls can not be nested'); }); it('should flush microtasks before returning', () => { let thenRan = false; - fakeAsync(() => { resolvedPromise.then(_ => { thenRan = true; }); })(); + fakeAsync(() => { + resolvedPromise.then(_ => { + thenRan = true; + }); + })(); expect(thenRan).toEqual(true); }); - it('should propagate the return value', - () => { expect(fakeAsync(() => 'foo')()).toEqual('foo'); }); + it('should propagate the return value', () => { + expect(fakeAsync(() => 'foo')()).toEqual('foo'); + }); describe('Promise', () => { it('should run asynchronous code', fakeAsync(() => { let thenRan = false; - resolvedPromise.then((_) => { thenRan = true; }); + resolvedPromise.then((_) => { + thenRan = true; + }); expect(thenRan).toEqual(false); @@ -92,22 +103,29 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should complain if the test throws an exception during async calls', () => { expect(() => { fakeAsync(() => { - resolvedPromise.then((_) => { throw new Error('async'); }); + resolvedPromise.then((_) => { + throw new Error('async'); + }); flushMicrotasks(); })(); }).toThrow(); }); it('should complain if a test throws an exception', () => { - expect(() => { fakeAsync(() => { throw new Error('sync'); })(); }).toThrowError('sync'); + expect(() => { + fakeAsync(() => { + throw new Error('sync'); + })(); + }).toThrowError('sync'); }); - }); describe('timers', () => { it('should run queued zero duration timer on zero tick', fakeAsync(() => { let ran = false; - setTimeout(() => { ran = true; }, 0); + setTimeout(() => { + ran = true; + }, 0); expect(ran).toEqual(false); @@ -118,7 +136,9 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should run queued timer after sufficient clock ticks', fakeAsync(() => { let ran = false; - setTimeout(() => { ran = true; }, 10); + setTimeout(() => { + ran = true; + }, 10); tick(6); expect(ran).toEqual(false); @@ -154,7 +174,9 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should run queued timer only once', fakeAsync(() => { let cycles = 0; - setTimeout(() => { cycles++; }, 10); + setTimeout(() => { + cycles++; + }, 10); tick(10); expect(cycles).toEqual(1); @@ -168,7 +190,9 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should not run cancelled timer', fakeAsync(() => { let ran = false; - const id = setTimeout(() => { ran = true; }, 10); + const id = setTimeout(() => { + ran = true; + }, 10); clearTimeout(id); tick(10); @@ -177,19 +201,25 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should throw an error on dangling timers', () => { expect(() => { - fakeAsync(() => { setTimeout(() => {}, 10); })(); + fakeAsync(() => { + setTimeout(() => {}, 10); + })(); }).toThrowError('1 timer(s) still in the queue.'); }); it('should throw an error on dangling periodic timers', () => { expect(() => { - fakeAsync(() => { setInterval(() => {}, 10); })(); + fakeAsync(() => { + setInterval(() => {}, 10); + })(); }).toThrowError('1 periodic timer(s) still in the queue.'); }); it('should run periodic timers', fakeAsync(() => { let cycles = 0; - const id = setInterval(() => { cycles++; }, 10); + const id = setInterval(() => { + cycles++; + }, 10); tick(10); expect(cycles).toEqual(1); @@ -204,7 +234,9 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should not run cancelled periodic timer', fakeAsync(() => { let ran = false; - const id = setInterval(() => { ran = true; }, 10); + const id = setInterval(() => { + ran = true; + }, 10); clearInterval(id); tick(10); @@ -229,7 +261,9 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should clear periodic timers', fakeAsync(() => { let cycles = 0; - const id = setInterval(() => { cycles++; }, 10); + const id = setInterval(() => { + cycles++; + }, 10); tick(10); expect(cycles).toEqual(1); @@ -289,7 +323,9 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should flush tasks', fakeAsync(() => { let ran = false; - setTimeout(() => { ran = true; }, 10); + setTimeout(() => { + ran = true; + }, 10); flush(); expect(ran).toEqual(true); @@ -298,8 +334,12 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should flush multiple tasks', fakeAsync(() => { let ran = false; let ran2 = false; - setTimeout(() => { ran = true; }, 10); - setTimeout(() => { ran2 = true; }, 30); + setTimeout(() => { + ran = true; + }, 10); + setTimeout(() => { + ran2 = true; + }, 30); let elapsed = flush(); @@ -311,8 +351,12 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' it('should move periodic tasks', fakeAsync(() => { let ran = false; let count = 0; - setInterval(() => { count++; }, 10); - setTimeout(() => { ran = true; }, 35); + setInterval(() => { + count++; + }, 10); + setTimeout(() => { + ran = true; + }, 35); let elapsed = flush(); @@ -353,7 +397,9 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' describe('only one `fakeAsync` zone per test', () => { let zoneInBeforeEach: Zone; let zoneInTest1: Zone; - beforeEach(fakeAsync(() => { zoneInBeforeEach = Zone.current; })); + beforeEach(fakeAsync(() => { + zoneInBeforeEach = Zone.current; + })); it('should use the same zone as in beforeEach', fakeAsync(() => { zoneInTest1 = Zone.current; @@ -363,9 +409,13 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec' }); describe('ProxyZone', () => { - beforeEach(() => { ProxyZoneSpec.assertPresent(); }); + beforeEach(() => { + ProxyZoneSpec.assertPresent(); + }); - afterEach(() => { ProxyZoneSpec.assertPresent(); }); + afterEach(() => { + ProxyZoneSpec.assertPresent(); + }); it('should allow fakeAsync zone to retroactively set a zoneSpec outside of fakeAsync', () => { ProxyZoneSpec.assertPresent(); diff --git a/packages/core/test/forward_ref_integration_spec.ts b/packages/core/test/forward_ref_integration_spec.ts index 856ab52ee455a..034dde7b87810 100644 --- a/packages/core/test/forward_ref_integration_spec.ts +++ b/packages/core/test/forward_ref_integration_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {Component, ContentChildren, Directive, Inject, NO_ERRORS_SCHEMA, NgModule, QueryList, asNativeElements, forwardRef} from '@angular/core'; +import {asNativeElements, Component, ContentChildren, Directive, forwardRef, Inject, NgModule, NO_ERRORS_SCHEMA, QueryList} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -20,7 +20,9 @@ class ModuleFrame { } describe('forwardRef integration', function() { - beforeEach(() => { TestBed.configureTestingModule({imports: [Module], declarations: [App]}); }); + beforeEach(() => { + TestBed.configureTestingModule({imports: [Module], declarations: [App]}); + }); it('should instantiate components which are declared using forwardRef', () => { const a = TestBed.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]}).createComponent(App); @@ -53,10 +55,12 @@ class App { }) class Door { // TODO(issue/24571): remove '!'. - @ContentChildren(forwardRef(() => Lock)) locks !: QueryList<Lock>; + @ContentChildren(forwardRef(() => Lock)) locks!: QueryList<Lock>; frame: Frame; - constructor(@Inject(forwardRef(() => Frame)) frame: Frame) { this.frame = frame; } + constructor(@Inject(forwardRef(() => Frame)) frame: Frame) { + this.frame = frame; + } } @Directive({selector: 'lock'}) diff --git a/packages/core/test/i18n/locale_data_api_spec.ts b/packages/core/test/i18n/locale_data_api_spec.ts index 3c995a61d3e46..13ecb23774c08 100644 --- a/packages/core/test/i18n/locale_data_api_spec.ts +++ b/packages/core/test/i18n/locale_data_api_spec.ts @@ -5,7 +5,7 @@ * 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 {LocaleDataIndex, findLocaleData, getLocaleCurrencyCode, registerLocaleData, unregisterAllLocaleData} from '../../src/i18n/locale_data_api'; +import {findLocaleData, getLocaleCurrencyCode, LocaleDataIndex, registerLocaleData, unregisterAllLocaleData} from '../../src/i18n/locale_data_api'; import {global} from '../../src/util/global'; { @@ -53,14 +53,18 @@ import {global} from '../../src/util/global'; .toThrowError(/Missing locale data for the locale "pt-AO"/); }); - it('should return english data if the locale is en-US', - () => { expect(findLocaleData('en-US')).toEqual(localeEn); }); + it('should return english data if the locale is en-US', () => { + expect(findLocaleData('en-US')).toEqual(localeEn); + }); - it('should return the exact LOCALE_DATA if it is available', - () => { expect(findLocaleData('fr-CA')).toEqual(localeFrCA); }); + it('should return the exact LOCALE_DATA if it is available', () => { + expect(findLocaleData('fr-CA')).toEqual(localeFrCA); + }); it('should return the parent LOCALE_DATA if it exists and exact locale is not available', - () => { expect(findLocaleData('fr-BE')).toEqual(localeFr); }); + () => { + expect(findLocaleData('fr-BE')).toEqual(localeFr); + }); it(`should find the LOCALE_DATA even if the locale id is badly formatted`, () => { expect(findLocaleData('ca-ES-VALENCIA')).toEqual(localeCaESVALENCIA); @@ -73,22 +77,30 @@ import {global} from '../../src/util/global'; expect(findLocaleData('fake-id2')).toEqual(localeFrCA); }); - it('should find the exact LOCALE_DATA if the locale is on the global object', - () => { expect(findLocaleData('de-CH')).toEqual(localeDeCH); }); + it('should find the exact LOCALE_DATA if the locale is on the global object', () => { + expect(findLocaleData('de-CH')).toEqual(localeDeCH); + }); it('should find the parent LOCALE_DATA if the exact locale is not available and the parent locale is on the global object', - () => { expect(findLocaleData('de-BE')).toEqual(localeDe); }); + () => { + expect(findLocaleData('de-BE')).toEqual(localeDe); + }); it('should find the registered LOCALE_DATA even if the same locale is on the global object', - () => { expect(findLocaleData('fr')).not.toBe(fakeGlobalFr); }); + () => { + expect(findLocaleData('fr')).not.toBe(fakeGlobalFr); + }); }); describe('getLocaleCurrencyCode()', () => { - it('should return `null` if the given locale does not provide a currency code', - () => { expect(getLocaleCurrencyCode('de')).toBe(null); }); + it('should return `null` if the given locale does not provide a currency code', () => { + expect(getLocaleCurrencyCode('de')).toBe(null); + }); it('should return the code at the `LocaleDataIndex.CurrencyCode` of the given locale`s data', - () => { expect(getLocaleCurrencyCode('fr')).toEqual('EUR'); }); + () => { + expect(getLocaleCurrencyCode('fr')).toEqual('EUR'); + }); }); }); } diff --git a/packages/core/test/linker/change_detection_integration_spec.ts b/packages/core/test/linker/change_detection_integration_spec.ts index 5d365cf9efb78..e9bd1f37fa0b7 100644 --- a/packages/core/test/linker/change_detection_integration_spec.ts +++ b/packages/core/test/linker/change_detection_integration_spec.ts @@ -9,7 +9,7 @@ import {ResourceLoader, UrlResolver} from '@angular/compiler'; import {MockResourceLoader} from '@angular/compiler/testing'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, DebugElement, Directive, DoCheck, EventEmitter, HostBinding, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, Provider, RendererFactory2, RendererType2, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core'; -import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing'; +import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -26,1665 +26,1697 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ (function() { - let renderLog: RenderLog; - let directiveLog: DirectiveLog; +let renderLog: RenderLog; +let directiveLog: DirectiveLog; - function createCompFixture<T>(template: string): ComponentFixture<TestComponent>; - function createCompFixture<T>(template: string, compType: Type<T>): ComponentFixture<T>; - function createCompFixture<T>( - template: string, compType: Type<T> = <any>TestComponent): ComponentFixture<T> { - TestBed.overrideComponent(compType, {set: new Component({template})}); +function createCompFixture<T>(template: string): ComponentFixture<TestComponent>; +function createCompFixture<T>(template: string, compType: Type<T>): ComponentFixture<T>; +function createCompFixture<T>( + template: string, compType: Type<T> = <any>TestComponent): ComponentFixture<T> { + TestBed.overrideComponent(compType, {set: new Component({template})}); - initHelpers(); + initHelpers(); - return TestBed.createComponent(compType); - } - - function initHelpers(): void { - renderLog = TestBed.inject(RenderLog); - directiveLog = TestBed.inject(DirectiveLog); - patchLoggingRenderer2(TestBed.inject(RendererFactory2), renderLog); - } + return TestBed.createComponent(compType); +} - function queryDirs(el: DebugElement, dirType: Type<any>): any { - const nodes = el.queryAllNodes(By.directive(dirType)); - return nodes.map(node => node.injector.get(dirType)); - } +function initHelpers(): void { + renderLog = TestBed.inject(RenderLog); + directiveLog = TestBed.inject(DirectiveLog); + patchLoggingRenderer2(TestBed.inject(RendererFactory2), renderLog); +} - function _bindSimpleProp<T>(bindAttr: string): ComponentFixture<TestComponent>; - function _bindSimpleProp<T>(bindAttr: string, compType: Type<T>): ComponentFixture<T>; - function _bindSimpleProp<T>( - bindAttr: string, compType: Type<T> = <any>TestComponent): ComponentFixture<T> { - const template = `<div ${bindAttr}></div>`; - return createCompFixture(template, compType); - } +function queryDirs(el: DebugElement, dirType: Type<any>): any { + const nodes = el.queryAllNodes(By.directive(dirType)); + return nodes.map(node => node.injector.get(dirType)); +} - function _bindSimpleValue(expression: any): ComponentFixture<TestComponent>; - function _bindSimpleValue<T>(expression: any, compType: Type<T>): ComponentFixture<T>; - function _bindSimpleValue<T>( - expression: any, compType: Type<T> = <any>TestComponent): ComponentFixture<T> { - return _bindSimpleProp(`[id]='${expression}'`, compType); - } +function _bindSimpleProp<T>(bindAttr: string): ComponentFixture<TestComponent>; +function _bindSimpleProp<T>(bindAttr: string, compType: Type<T>): ComponentFixture<T>; +function _bindSimpleProp<T>( + bindAttr: string, compType: Type<T> = <any>TestComponent): ComponentFixture<T> { + const template = `<div ${bindAttr}></div>`; + return createCompFixture(template, compType); +} - function _bindAndCheckSimpleValue( - expression: any, compType: Type<any> = TestComponent): string[] { - const ctx = _bindSimpleValue(expression, compType); - ctx.detectChanges(false); - return renderLog.log; - } +function _bindSimpleValue(expression: any): ComponentFixture<TestComponent>; +function _bindSimpleValue<T>(expression: any, compType: Type<T>): ComponentFixture<T>; +function _bindSimpleValue<T>( + expression: any, compType: Type<T> = <any>TestComponent): ComponentFixture<T> { + return _bindSimpleProp(`[id]='${expression}'`, compType); +} - describe(`ChangeDetection`, () => { +function _bindAndCheckSimpleValue(expression: any, compType: Type<any> = TestComponent): string[] { + const ctx = _bindSimpleValue(expression, compType); + ctx.detectChanges(false); + return renderLog.log; +} - beforeEach(() => { - TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); - TestBed.configureTestingModule({ - declarations: [ - TestData, - TestDirective, - TestComponent, - AnotherComponent, - TestLocals, - CompWithRef, - WrapCompWithRef, - EmitterDirective, - PushComp, - OnDestroyDirective, - OrderCheckDirective2, - OrderCheckDirective0, - OrderCheckDirective1, - Gh9882, - Uninitialized, - Person, - PersonHolder, - PersonHolderHolder, - CountingPipe, - CountingImpurePipe, - MultiArgPipe, - PipeWithOnDestroy, - IdentityPipe, - WrappedPipe, - ], - providers: [ - RenderLog, - DirectiveLog, - ], - }); +describe(`ChangeDetection`, () => { + beforeEach(() => { + TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); + TestBed.configureTestingModule({ + declarations: [ + TestData, + TestDirective, + TestComponent, + AnotherComponent, + TestLocals, + CompWithRef, + WrapCompWithRef, + EmitterDirective, + PushComp, + OnDestroyDirective, + OrderCheckDirective2, + OrderCheckDirective0, + OrderCheckDirective1, + Gh9882, + Uninitialized, + Person, + PersonHolder, + PersonHolderHolder, + CountingPipe, + CountingImpurePipe, + MultiArgPipe, + PipeWithOnDestroy, + IdentityPipe, + WrappedPipe, + ], + providers: [ + RenderLog, + DirectiveLog, + ], }); + }); - describe('expressions', () => { - it('should support literals', - fakeAsync(() => { expect(_bindAndCheckSimpleValue(10)).toEqual(['id=10']); })); - - it('should strip quotes from literals', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('"str"')).toEqual(['id=str']); })); - - it('should support newlines in literals', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('"a\n\nb"')).toEqual(['id=a\n\nb']); })); - - it('should support + operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('10 + 2')).toEqual(['id=12']); })); - - it('should support - operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('10 - 2')).toEqual(['id=8']); })); - - it('should support * operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('10 * 2')).toEqual(['id=20']); })); - - it('should support / operations', fakeAsync(() => { - expect(_bindAndCheckSimpleValue('10 / 2')).toEqual([`id=${5.0}`]); - })); // dart exp=5.0, js exp=5 - - it('should support % operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('11 % 2')).toEqual(['id=1']); })); - - it('should support == operations on identical', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 == 1')).toEqual(['id=true']); })); - - it('should support != operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 != 1')).toEqual(['id=false']); })); - - it('should support == operations on coerceible', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 == true')).toEqual([`id=true`]); })); - - it('should support === operations on identical', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 === 1')).toEqual(['id=true']); })); - - it('should support !== operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 !== 1')).toEqual(['id=false']); })); - - it('should support === operations on coerceible', fakeAsync(() => { - expect(_bindAndCheckSimpleValue('1 === true')).toEqual(['id=false']); - })); + describe('expressions', () => { + it('should support literals', fakeAsync(() => { + expect(_bindAndCheckSimpleValue(10)).toEqual(['id=10']); + })); - it('should support true < operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 < 2')).toEqual(['id=true']); })); + it('should strip quotes from literals', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('"str"')).toEqual(['id=str']); + })); - it('should support false < operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 < 1')).toEqual(['id=false']); })); + it('should support newlines in literals', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('"a\n\nb"')).toEqual(['id=a\n\nb']); + })); - it('should support false > operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 > 2')).toEqual(['id=false']); })); + it('should support + operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('10 + 2')).toEqual(['id=12']); + })); - it('should support true > operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 > 1')).toEqual(['id=true']); })); + it('should support - operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('10 - 2')).toEqual(['id=8']); + })); - it('should support true <= operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 <= 2')).toEqual(['id=true']); })); + it('should support * operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('10 * 2')).toEqual(['id=20']); + })); - it('should support equal <= operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 <= 2')).toEqual(['id=true']); })); + it('should support / operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('10 / 2')).toEqual([`id=${5.0}`]); + })); // dart exp=5.0, js exp=5 - it('should support false <= operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 <= 1')).toEqual(['id=false']); })); + it('should support % operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('11 % 2')).toEqual(['id=1']); + })); - it('should support true >= operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 >= 1')).toEqual(['id=true']); })); + it('should support == operations on identical', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 == 1')).toEqual(['id=true']); + })); - it('should support equal >= operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 >= 2')).toEqual(['id=true']); })); + it('should support != operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 != 1')).toEqual(['id=false']); + })); - it('should support false >= operations', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 >= 2')).toEqual(['id=false']); })); + it('should support == operations on coerceible', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 == true')).toEqual([`id=true`]); + })); - it('should support true && operations', fakeAsync(() => { - expect(_bindAndCheckSimpleValue('true && true')).toEqual(['id=true']); - })); + it('should support === operations on identical', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 === 1')).toEqual(['id=true']); + })); - it('should support false && operations', fakeAsync(() => { - expect(_bindAndCheckSimpleValue('true && false')).toEqual(['id=false']); + it('should support !== operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 !== 1')).toEqual(['id=false']); + })); + + it('should support === operations on coerceible', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 === true')).toEqual(['id=false']); + })); + + it('should support true < operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 < 2')).toEqual(['id=true']); + })); + + it('should support false < operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('2 < 1')).toEqual(['id=false']); + })); + + it('should support false > operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 > 2')).toEqual(['id=false']); + })); + + it('should support true > operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('2 > 1')).toEqual(['id=true']); + })); + + it('should support true <= operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 <= 2')).toEqual(['id=true']); + })); + + it('should support equal <= operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('2 <= 2')).toEqual(['id=true']); + })); + + it('should support false <= operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('2 <= 1')).toEqual(['id=false']); + })); + + it('should support true >= operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('2 >= 1')).toEqual(['id=true']); + })); + + it('should support equal >= operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('2 >= 2')).toEqual(['id=true']); + })); + + it('should support false >= operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 >= 2')).toEqual(['id=false']); + })); + + it('should support true && operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('true && true')).toEqual(['id=true']); + })); + + it('should support false && operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('true && false')).toEqual(['id=false']); + })); + + it('should support true || operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('true || false')).toEqual(['id=true']); + })); + + it('should support false || operations', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('false || false')).toEqual(['id=false']); + })); + + it('should support negate', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('!true')).toEqual(['id=false']); + })); + + it('should support double negate', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('!!true')).toEqual(['id=true']); + })); + + it('should support true conditionals', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 < 2 ? 1 : 2')).toEqual(['id=1']); + })); + + it('should support false conditionals', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('1 > 2 ? 1 : 2')).toEqual(['id=2']); + })); + + it('should support keyed access to a list item', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('["foo", "bar"][0]')).toEqual(['id=foo']); + })); + + it('should support keyed access to a map item', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('{"foo": "bar"}["foo"]')).toEqual(['id=bar']); + })); + + it('should report all changes on the first run including uninitialized values', + fakeAsync(() => { + expect(_bindAndCheckSimpleValue('value', Uninitialized)).toEqual(['id=null']); + })); + + it('should report all changes on the first run including null values', fakeAsync(() => { + const ctx = _bindSimpleValue('a', TestData); + ctx.componentInstance.a = null; + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=null']); + })); + + it('should support simple chained property access', fakeAsync(() => { + const ctx = _bindSimpleValue('address.city', Person); + ctx.componentInstance.name = 'Victor'; + ctx.componentInstance.address = new Address('Grenoble'); + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=Grenoble']); + })); + + describe('safe navigation operator', () => { + it('should support reading properties of nulls', fakeAsync(() => { + const ctx = _bindSimpleValue('address?.city', Person); + ctx.componentInstance.address = null!; + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=null']); })); - it('should support true || operations', fakeAsync(() => { - expect(_bindAndCheckSimpleValue('true || false')).toEqual(['id=true']); + it('should support calling methods on nulls', fakeAsync(() => { + const ctx = _bindSimpleValue('address?.toString()', Person); + ctx.componentInstance.address = null!; + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=null']); })); - it('should support false || operations', fakeAsync(() => { - expect(_bindAndCheckSimpleValue('false || false')).toEqual(['id=false']); + it('should support reading properties on non nulls', fakeAsync(() => { + const ctx = _bindSimpleValue('address?.city', Person); + ctx.componentInstance.address = new Address('MTV'); + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=MTV']); })); - it('should support negate', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('!true')).toEqual(['id=false']); })); - - it('should support double negate', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('!!true')).toEqual(['id=true']); })); - - it('should support true conditionals', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 < 2 ? 1 : 2')).toEqual(['id=1']); })); - - it('should support false conditionals', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 > 2 ? 1 : 2')).toEqual(['id=2']); })); - - it('should support keyed access to a list item', fakeAsync(() => { - expect(_bindAndCheckSimpleValue('["foo", "bar"][0]')).toEqual(['id=foo']); + it('should support calling methods on non nulls', fakeAsync(() => { + const ctx = _bindSimpleValue('address?.toString()', Person); + ctx.componentInstance.address = new Address('MTV'); + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=MTV']); })); - it('should support keyed access to a map item', fakeAsync(() => { - expect(_bindAndCheckSimpleValue('{"foo": "bar"}["foo"]')).toEqual(['id=bar']); + it('should support short-circuting safe navigation', fakeAsync(() => { + const ctx = _bindSimpleValue('value?.address.city', PersonHolder); + ctx.componentInstance.value = null!; + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=null']); })); - it('should report all changes on the first run including uninitialized values', - fakeAsync(() => { - expect(_bindAndCheckSimpleValue('value', Uninitialized)).toEqual(['id=null']); + it('should support nested short-circuting safe navigation', fakeAsync(() => { + const ctx = _bindSimpleValue('value.value?.address.city', PersonHolderHolder); + ctx.componentInstance.value = new PersonHolder(); + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=null']); })); - it('should report all changes on the first run including null values', fakeAsync(() => { - const ctx = _bindSimpleValue('a', TestData); - ctx.componentInstance.a = null; + it('should support chained short-circuting safe navigation', fakeAsync(() => { + const ctx = _bindSimpleValue('value?.value?.address.city', PersonHolderHolder); ctx.detectChanges(false); expect(renderLog.log).toEqual(['id=null']); })); - it('should support simple chained property access', fakeAsync(() => { - const ctx = _bindSimpleValue('address.city', Person); - ctx.componentInstance.name = 'Victor'; - ctx.componentInstance.address = new Address('Grenoble'); + it('should support short-circuting array index operations', fakeAsync(() => { + const ctx = _bindSimpleValue('value?.phones[0]', PersonHolder); ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=Grenoble']); + expect(renderLog.log).toEqual(['id=null']); })); - describe('safe navigation operator', () => { - it('should support reading properties of nulls', fakeAsync(() => { - const ctx = _bindSimpleValue('address?.city', Person); - ctx.componentInstance.address = null !; - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=null']); - })); - - it('should support calling methods on nulls', fakeAsync(() => { - const ctx = _bindSimpleValue('address?.toString()', Person); - ctx.componentInstance.address = null !; - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=null']); - })); - - it('should support reading properties on non nulls', fakeAsync(() => { - const ctx = _bindSimpleValue('address?.city', Person); - ctx.componentInstance.address = new Address('MTV'); - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=MTV']); - })); - - it('should support calling methods on non nulls', fakeAsync(() => { - const ctx = _bindSimpleValue('address?.toString()', Person); - ctx.componentInstance.address = new Address('MTV'); - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=MTV']); - })); - - it('should support short-circuting safe navigation', fakeAsync(() => { + it('should still throw if right-side would throw', fakeAsync(() => { + expect(() => { const ctx = _bindSimpleValue('value?.address.city', PersonHolder); - ctx.componentInstance.value = null !; - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=null']); - })); - - it('should support nested short-circuting safe navigation', fakeAsync(() => { - const ctx = _bindSimpleValue('value.value?.address.city', PersonHolderHolder); - ctx.componentInstance.value = new PersonHolder(); - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=null']); - })); - - it('should support chained short-circuting safe navigation', fakeAsync(() => { - const ctx = _bindSimpleValue('value?.value?.address.city', PersonHolderHolder); + const person = new Person(); + person.address = null!; + ctx.componentInstance.value = person; ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=null']); - })); - - it('should support short-circuting array index operations', fakeAsync(() => { - const ctx = _bindSimpleValue('value?.phones[0]', PersonHolder); - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=null']); - })); - - it('should still throw if right-side would throw', fakeAsync(() => { - expect(() => { - const ctx = _bindSimpleValue('value?.address.city', PersonHolder); - const person = new Person(); - person.address = null !; - ctx.componentInstance.value = person; - ctx.detectChanges(false); - }).toThrow(); - })); - }); + }).toThrow(); + })); + }); - it('should support method calls', fakeAsync(() => { - const ctx = _bindSimpleValue('sayHi("Jim")', Person); + it('should support method calls', fakeAsync(() => { + const ctx = _bindSimpleValue('sayHi("Jim")', Person); + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=Hi, Jim']); + })); + + it('should support function calls', fakeAsync(() => { + const ctx = _bindSimpleValue('a()(99)', TestData); + ctx.componentInstance.a = () => (a: any) => a; + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=99']); + })); + + it('should support chained method calls', fakeAsync(() => { + const ctx = _bindSimpleValue('address.toString()', Person); + ctx.componentInstance.address = new Address('MTV'); + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=MTV']); + })); + + it('should support NaN', fakeAsync(() => { + const ctx = _bindSimpleValue('age', Person); + ctx.componentInstance.age = NaN; + ctx.detectChanges(false); + + expect(renderLog.log).toEqual(['id=NaN']); + renderLog.clear(); + + ctx.detectChanges(false); + expect(renderLog.log).toEqual([]); + })); + + it('should do simple watching', fakeAsync(() => { + const ctx = _bindSimpleValue('name', Person); + ctx.componentInstance.name = 'misko'; + + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=misko']); + renderLog.clear(); + + ctx.detectChanges(false); + expect(renderLog.log).toEqual([]); + renderLog.clear(); + + ctx.componentInstance.name = 'Misko'; + ctx.detectChanges(false); + expect(renderLog.log).toEqual(['id=Misko']); + })); + + it('should support literal array made of literals', fakeAsync(() => { + const ctx = _bindSimpleValue('[1, 2]'); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([[1, 2]]); + })); + + it('should support empty literal array', fakeAsync(() => { + const ctx = _bindSimpleValue('[]'); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([[]]); + })); + + it('should support literal array made of expressions', fakeAsync(() => { + const ctx = _bindSimpleValue('[1, a]', TestData); + ctx.componentInstance.a = 2; + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([[1, 2]]); + })); + + it('should not recreate literal arrays unless their content changed', fakeAsync(() => { + const ctx = _bindSimpleValue('[1, a]', TestData); + ctx.componentInstance.a = 2; + ctx.detectChanges(false); + ctx.detectChanges(false); + ctx.componentInstance.a = 3; + ctx.detectChanges(false); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([[1, 2], [1, 3]]); + })); + + it('should support literal maps made of literals', fakeAsync(() => { + const ctx = _bindSimpleValue('{z: 1}'); + ctx.detectChanges(false); + expect(renderLog.loggedValues[0]['z']).toEqual(1); + })); + + it('should support empty literal map', fakeAsync(() => { + const ctx = _bindSimpleValue('{}'); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([{}]); + })); + + it('should support literal maps made of expressions', fakeAsync(() => { + const ctx = _bindSimpleValue('{z: a}'); + ctx.componentInstance.a = 1; + ctx.detectChanges(false); + expect(renderLog.loggedValues[0]['z']).toEqual(1); + })); + + it('should not recreate literal maps unless their content changed', fakeAsync(() => { + const ctx = _bindSimpleValue('{z: a}'); + ctx.componentInstance.a = 1; + ctx.detectChanges(false); + ctx.detectChanges(false); + ctx.componentInstance.a = 2; + ctx.detectChanges(false); + ctx.detectChanges(false); + expect(renderLog.loggedValues.length).toBe(2); + expect(renderLog.loggedValues[0]['z']).toEqual(1); + expect(renderLog.loggedValues[1]['z']).toEqual(2); + })); + + + it('should ignore empty bindings', fakeAsync(() => { + const ctx = _bindSimpleProp('[id]', TestData); + ctx.componentInstance.a = 'value'; + ctx.detectChanges(false); + + expect(renderLog.log).toEqual([]); + })); + + it('should support interpolation', fakeAsync(() => { + const ctx = _bindSimpleProp('id="B{{a}}A"', TestData); + ctx.componentInstance.a = 'value'; + ctx.detectChanges(false); + + expect(renderLog.log).toEqual(['id=BvalueA']); + })); + + it('should output empty strings for null values in interpolation', fakeAsync(() => { + const ctx = _bindSimpleProp('id="B{{a}}A"', TestData); + ctx.componentInstance.a = null; + ctx.detectChanges(false); + + expect(renderLog.log).toEqual(['id=BA']); + })); + + it('should escape values in literals that indicate interpolation', fakeAsync(() => { + expect(_bindAndCheckSimpleValue('"$"')).toEqual(['id=$']); + })); + + it('should read locals', fakeAsync(() => { + const ctx = createCompFixture( + '<ng-template testLocals let-local="someLocal">{{local}}</ng-template>'); + ctx.detectChanges(false); + + expect(renderLog.log).toEqual(['{{someLocalValue}}']); + })); + + describe('pipes', () => { + it('should use the return value of the pipe', fakeAsync(() => { + const ctx = _bindSimpleValue('name | countingPipe', Person); + ctx.componentInstance.name = 'bob'; ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=Hi, Jim']); + expect(renderLog.loggedValues).toEqual(['bob state:0']); })); - it('should support function calls', fakeAsync(() => { - const ctx = _bindSimpleValue('a()(99)', TestData); - ctx.componentInstance.a = () => (a: any) => a; + it('should support arguments in pipes', fakeAsync(() => { + const ctx = _bindSimpleValue('name | multiArgPipe:"one":address.city', Person); + ctx.componentInstance.name = 'value'; + ctx.componentInstance.address = new Address('two'); ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=99']); + expect(renderLog.loggedValues).toEqual(['value one two default']); })); - it('should support chained method calls', fakeAsync(() => { - const ctx = _bindSimpleValue('address.toString()', Person); - ctx.componentInstance.address = new Address('MTV'); + it('should associate pipes right-to-left', fakeAsync(() => { + const ctx = _bindSimpleValue('name | multiArgPipe:"a":"b" | multiArgPipe:0:1', Person); + ctx.componentInstance.name = 'value'; ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=MTV']); + expect(renderLog.loggedValues).toEqual(['value a b default 0 1 default']); })); - it('should support NaN', fakeAsync(() => { - const ctx = _bindSimpleValue('age', Person); - ctx.componentInstance.age = NaN; + it('should support calling pure pipes with different number of arguments', fakeAsync(() => { + const ctx = _bindSimpleValue('name | multiArgPipe:"a":"b" | multiArgPipe:0:1:2', Person); + ctx.componentInstance.name = 'value'; ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual(['value a b default 0 1 2']); + })); - expect(renderLog.log).toEqual(['id=NaN']); - renderLog.clear(); + it('should do nothing when no change', fakeAsync(() => { + const ctx = _bindSimpleValue('"Megatron" | identityPipe', Person); ctx.detectChanges(false); - expect(renderLog.log).toEqual([]); - })); - it('should do simple watching', fakeAsync(() => { - const ctx = _bindSimpleValue('name', Person); - ctx.componentInstance.name = 'misko'; + expect(renderLog.log).toEqual(['id=Megatron']); - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=misko']); renderLog.clear(); - ctx.detectChanges(false); - expect(renderLog.log).toEqual([]); - renderLog.clear(); - ctx.componentInstance.name = 'Misko'; - ctx.detectChanges(false); - expect(renderLog.log).toEqual(['id=Misko']); + expect(renderLog.log).toEqual([]); })); - it('should support literal array made of literals', fakeAsync(() => { - const ctx = _bindSimpleValue('[1, 2]'); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([[1, 2]]); - })); + it('should unwrap the wrapped value and force a change', fakeAsync(() => { + const ctx = _bindSimpleValue('"Megatron" | wrappedPipe', Person); - it('should support empty literal array', fakeAsync(() => { - const ctx = _bindSimpleValue('[]'); ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([[]]); - })); - it('should support literal array made of expressions', fakeAsync(() => { - const ctx = _bindSimpleValue('[1, a]', TestData); - ctx.componentInstance.a = 2; + expect(renderLog.log).toEqual(['id=Megatron']); + + renderLog.clear(); ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([[1, 2]]); + + expect(renderLog.log).toEqual(['id=Megatron']); })); - it('should not recreate literal arrays unless their content changed', fakeAsync(() => { - const ctx = _bindSimpleValue('[1, a]', TestData); - ctx.componentInstance.a = 2; - ctx.detectChanges(false); - ctx.detectChanges(false); - ctx.componentInstance.a = 3; + it('should record unwrapped values via ngOnChanges', fakeAsync(() => { + const ctx = createCompFixture( + '<div [testDirective]="\'aName\' | wrappedPipe" [a]="1" [b]="2 | wrappedPipe"></div>'); + const dir: TestDirective = queryDirs(ctx.debugElement, TestDirective)[0]; ctx.detectChanges(false); + dir.changes = {}; ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([[1, 2], [1, 3]]); - })); - it('should support literal maps made of literals', fakeAsync(() => { - const ctx = _bindSimpleValue('{z: 1}'); - ctx.detectChanges(false); - expect(renderLog.loggedValues[0]['z']).toEqual(1); - })); + // Note: the binding for `a` did not change and has no ValueWrapper, + // and should therefore stay unchanged. + expect(dir.changes).toEqual({ + 'name': new SimpleChange('aName', 'aName', false), + 'b': new SimpleChange(2, 2, false) + }); - it('should support empty literal map', fakeAsync(() => { - const ctx = _bindSimpleValue('{}'); ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([{}]); + expect(dir.changes).toEqual({ + 'name': new SimpleChange('aName', 'aName', false), + 'b': new SimpleChange(2, 2, false) + }); })); - it('should support literal maps made of expressions', fakeAsync(() => { - const ctx = _bindSimpleValue('{z: a}'); - ctx.componentInstance.a = 1; + it('should call pure pipes only if the arguments change', fakeAsync(() => { + const ctx = _bindSimpleValue('name | countingPipe', Person); + // change from undefined -> null + ctx.componentInstance.name = null!; ctx.detectChanges(false); - expect(renderLog.loggedValues[0]['z']).toEqual(1); - })); + expect(renderLog.loggedValues).toEqual(['null state:0']); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual(['null state:0']); - it('should not recreate literal maps unless their content changed', fakeAsync(() => { - const ctx = _bindSimpleValue('{z: a}'); - ctx.componentInstance.a = 1; + // change from null -> some value + ctx.componentInstance.name = 'bob'; ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual(['null state:0', 'bob state:1']); ctx.detectChanges(false); - ctx.componentInstance.a = 2; + expect(renderLog.loggedValues).toEqual(['null state:0', 'bob state:1']); + + // change from some value -> some other value + ctx.componentInstance.name = 'bart'; ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual(['null state:0', 'bob state:1', 'bart state:2']); ctx.detectChanges(false); - expect(renderLog.loggedValues.length).toBe(2); - expect(renderLog.loggedValues[0]['z']).toEqual(1); - expect(renderLog.loggedValues[1]['z']).toEqual(2); + expect(renderLog.loggedValues).toEqual(['null state:0', 'bob state:1', 'bart state:2']); })); + modifiedInIvy('Pure pipes are instantiated differently in view engine and ivy') + .it('should call pure pipes that are used multiple times only when the arguments change and share state between pipe instances', + fakeAsync(() => { + const ctx = createCompFixture( + `<div [id]="name | countingPipe"></div><div [id]="age | countingPipe"></div>` + + '<div *ngFor="let x of [1,2]" [id]="address.city | countingPipe"></div>', + Person); + ctx.componentInstance.name = 'a'; + ctx.componentInstance.age = 10; + ctx.componentInstance.address = new Address('mtv'); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([ + 'mtv state:0', 'mtv state:1', 'a state:2', '10 state:3' + ]); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([ + 'mtv state:0', 'mtv state:1', 'a state:2', '10 state:3' + ]); + ctx.componentInstance.age = 11; + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([ + 'mtv state:0', 'mtv state:1', 'a state:2', '10 state:3', '11 state:4' + ]); + })); - it('should ignore empty bindings', fakeAsync(() => { - const ctx = _bindSimpleProp('[id]', TestData); - ctx.componentInstance.a = 'value'; + // this is the ivy version of the above tests - the difference is in pure pipe instantiation + // logic and binding execution order + ivyEnabled && + it('should call pure pipes that are used multiple times only when the arguments change', + fakeAsync(() => { + const ctx = createCompFixture( + `<div [id]="name | countingPipe"></div><div [id]="age | countingPipe"></div>` + + '<div *ngFor="let x of [1,2]" [id]="address.city | countingPipe"></div>', + Person); + ctx.componentInstance.name = 'a'; + ctx.componentInstance.age = 10; + ctx.componentInstance.address = new Address('mtv'); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([ + 'a state:0', '10 state:0', 'mtv state:0', 'mtv state:0' + ]); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([ + 'a state:0', '10 state:0', 'mtv state:0', 'mtv state:0' + ]); + ctx.componentInstance.age = 11; + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([ + 'a state:0', '10 state:0', 'mtv state:0', 'mtv state:0', '11 state:1' + ]); + })); + + it('should call impure pipes on each change detection run', fakeAsync(() => { + const ctx = _bindSimpleValue('name | countingImpurePipe', Person); + ctx.componentInstance.name = 'bob'; ctx.detectChanges(false); - - expect(renderLog.log).toEqual([]); - })); - - it('should support interpolation', fakeAsync(() => { - const ctx = _bindSimpleProp('id="B{{a}}A"', TestData); - ctx.componentInstance.a = 'value'; + expect(renderLog.loggedValues).toEqual(['bob state:0']); ctx.detectChanges(false); - - expect(renderLog.log).toEqual(['id=BvalueA']); + expect(renderLog.loggedValues).toEqual(['bob state:0', 'bob state:1']); })); + }); - it('should output empty strings for null values in interpolation', fakeAsync(() => { - const ctx = _bindSimpleProp('id="B{{a}}A"', TestData); - ctx.componentInstance.a = null; - ctx.detectChanges(false); + describe('event expressions', () => { + it('should support field assignments', fakeAsync(() => { + const ctx = _bindSimpleProp('(event)="b=a=$event"'); + const childEl = ctx.debugElement.children[0]; + const evt = 'EVENT'; + childEl.triggerEventHandler('event', evt); - expect(renderLog.log).toEqual(['id=BA']); + expect(ctx.componentInstance.a).toEqual(evt); + expect(ctx.componentInstance.b).toEqual(evt); })); - it('should escape values in literals that indicate interpolation', - fakeAsync(() => { expect(_bindAndCheckSimpleValue('"$"')).toEqual(['id=$']); })); - - it('should read locals', fakeAsync(() => { - const ctx = createCompFixture( - '<ng-template testLocals let-local="someLocal">{{local}}</ng-template>'); - ctx.detectChanges(false); - - expect(renderLog.log).toEqual(['{{someLocalValue}}']); + it('should support keyed assignments', fakeAsync(() => { + const ctx = _bindSimpleProp('(event)="a[0]=$event"'); + const childEl = ctx.debugElement.children[0]; + ctx.componentInstance.a = ['OLD']; + const evt = 'EVENT'; + childEl.triggerEventHandler('event', evt); + expect(ctx.componentInstance.a).toEqual([evt]); })); - describe('pipes', () => { - it('should use the return value of the pipe', fakeAsync(() => { - const ctx = _bindSimpleValue('name | countingPipe', Person); - ctx.componentInstance.name = 'bob'; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['bob state:0']); - })); - - it('should support arguments in pipes', fakeAsync(() => { - const ctx = _bindSimpleValue('name | multiArgPipe:"one":address.city', Person); - ctx.componentInstance.name = 'value'; - ctx.componentInstance.address = new Address('two'); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['value one two default']); - })); - - it('should associate pipes right-to-left', fakeAsync(() => { - const ctx = _bindSimpleValue('name | multiArgPipe:"a":"b" | multiArgPipe:0:1', Person); - ctx.componentInstance.name = 'value'; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['value a b default 0 1 default']); - })); - - it('should support calling pure pipes with different number of arguments', fakeAsync(() => { - const ctx = - _bindSimpleValue('name | multiArgPipe:"a":"b" | multiArgPipe:0:1:2', Person); - ctx.componentInstance.name = 'value'; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['value a b default 0 1 2']); - })); - - it('should do nothing when no change', fakeAsync(() => { - const ctx = _bindSimpleValue('"Megatron" | identityPipe', Person); - - ctx.detectChanges(false); - - expect(renderLog.log).toEqual(['id=Megatron']); - - renderLog.clear(); - ctx.detectChanges(false); - - expect(renderLog.log).toEqual([]); - })); - - it('should unwrap the wrapped value and force a change', fakeAsync(() => { - const ctx = _bindSimpleValue('"Megatron" | wrappedPipe', Person); - - ctx.detectChanges(false); - - expect(renderLog.log).toEqual(['id=Megatron']); - - renderLog.clear(); - ctx.detectChanges(false); - - expect(renderLog.log).toEqual(['id=Megatron']); - })); - - it('should record unwrapped values via ngOnChanges', fakeAsync(() => { - const ctx = createCompFixture( - '<div [testDirective]="\'aName\' | wrappedPipe" [a]="1" [b]="2 | wrappedPipe"></div>'); - const dir: TestDirective = queryDirs(ctx.debugElement, TestDirective)[0]; - ctx.detectChanges(false); - dir.changes = {}; - ctx.detectChanges(false); - - // Note: the binding for `a` did not change and has no ValueWrapper, - // and should therefore stay unchanged. - expect(dir.changes).toEqual({ - 'name': new SimpleChange('aName', 'aName', false), - 'b': new SimpleChange(2, 2, false) - }); - - ctx.detectChanges(false); - expect(dir.changes).toEqual({ - 'name': new SimpleChange('aName', 'aName', false), - 'b': new SimpleChange(2, 2, false) - }); - })); - - it('should call pure pipes only if the arguments change', fakeAsync(() => { - const ctx = _bindSimpleValue('name | countingPipe', Person); - // change from undefined -> null - ctx.componentInstance.name = null !; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['null state:0']); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['null state:0']); + it('should support chains', fakeAsync(() => { + const ctx = _bindSimpleProp('(event)="a=a+1; a=a+1;"'); + const childEl = ctx.debugElement.children[0]; + ctx.componentInstance.a = 0; + childEl.triggerEventHandler('event', 'EVENT'); + expect(ctx.componentInstance.a).toEqual(2); + })); - // change from null -> some value - ctx.componentInstance.name = 'bob'; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['null state:0', 'bob state:1']); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['null state:0', 'bob state:1']); + it('should support empty literals', fakeAsync(() => { + const ctx = _bindSimpleProp('(event)="a=[{},[]]"'); + const childEl = ctx.debugElement.children[0]; + childEl.triggerEventHandler('event', 'EVENT'); - // change from some value -> some other value - ctx.componentInstance.name = 'bart'; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([ - 'null state:0', 'bob state:1', 'bart state:2' - ]); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([ - 'null state:0', 'bob state:1', 'bart state:2' - ]); - - })); - - modifiedInIvy('Pure pipes are instantiated differently in view engine and ivy') - .it('should call pure pipes that are used multiple times only when the arguments change and share state between pipe instances', - fakeAsync(() => { - const ctx = createCompFixture( - `<div [id]="name | countingPipe"></div><div [id]="age | countingPipe"></div>` + - '<div *ngFor="let x of [1,2]" [id]="address.city | countingPipe"></div>', - Person); - ctx.componentInstance.name = 'a'; - ctx.componentInstance.age = 10; - ctx.componentInstance.address = new Address('mtv'); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([ - 'mtv state:0', 'mtv state:1', 'a state:2', '10 state:3' - ]); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([ - 'mtv state:0', 'mtv state:1', 'a state:2', '10 state:3' - ]); - ctx.componentInstance.age = 11; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([ - 'mtv state:0', 'mtv state:1', 'a state:2', '10 state:3', '11 state:4' - ]); - })); - - // this is the ivy version of the above tests - the difference is in pure pipe instantiation - // logic and binding execution order - ivyEnabled && - it('should call pure pipes that are used multiple times only when the arguments change', - fakeAsync(() => { - const ctx = createCompFixture( - `<div [id]="name | countingPipe"></div><div [id]="age | countingPipe"></div>` + - '<div *ngFor="let x of [1,2]" [id]="address.city | countingPipe"></div>', - Person); - ctx.componentInstance.name = 'a'; - ctx.componentInstance.age = 10; - ctx.componentInstance.address = new Address('mtv'); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([ - 'a state:0', '10 state:0', 'mtv state:0', 'mtv state:0' - ]); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([ - 'a state:0', '10 state:0', 'mtv state:0', 'mtv state:0' - ]); - ctx.componentInstance.age = 11; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([ - 'a state:0', '10 state:0', 'mtv state:0', 'mtv state:0', '11 state:1' - ]); - })); - - it('should call impure pipes on each change detection run', fakeAsync(() => { - const ctx = _bindSimpleValue('name | countingImpurePipe', Person); - ctx.componentInstance.name = 'bob'; - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['bob state:0']); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual(['bob state:0', 'bob state:1']); - })); - }); + expect(ctx.componentInstance.a).toEqual([{}, []]); + })); - describe('event expressions', () => { - it('should support field assignments', fakeAsync(() => { - const ctx = _bindSimpleProp('(event)="b=a=$event"'); - const childEl = ctx.debugElement.children[0]; - const evt = 'EVENT'; - childEl.triggerEventHandler('event', evt); - - expect(ctx.componentInstance.a).toEqual(evt); - expect(ctx.componentInstance.b).toEqual(evt); - })); - - it('should support keyed assignments', fakeAsync(() => { - const ctx = _bindSimpleProp('(event)="a[0]=$event"'); - const childEl = ctx.debugElement.children[0]; - ctx.componentInstance.a = ['OLD']; - const evt = 'EVENT'; - childEl.triggerEventHandler('event', evt); - expect(ctx.componentInstance.a).toEqual([evt]); - })); - - it('should support chains', fakeAsync(() => { - const ctx = _bindSimpleProp('(event)="a=a+1; a=a+1;"'); - const childEl = ctx.debugElement.children[0]; - ctx.componentInstance.a = 0; - childEl.triggerEventHandler('event', 'EVENT'); - expect(ctx.componentInstance.a).toEqual(2); - })); - - it('should support empty literals', fakeAsync(() => { - const ctx = _bindSimpleProp('(event)="a=[{},[]]"'); - const childEl = ctx.debugElement.children[0]; - childEl.triggerEventHandler('event', 'EVENT'); - - expect(ctx.componentInstance.a).toEqual([{}, []]); - })); - - it('should throw when trying to assign to a local', fakeAsync(() => { - expect(() => { _bindSimpleProp('(event)="$event=1"'); }) - .toThrowError(new RegExp( - 'Cannot assign value (.*) to template variable (.*). Template variables are read-only.')); - })); - - it('should support short-circuiting', fakeAsync(() => { - const ctx = _bindSimpleProp('(event)="true ? a = a + 1 : a = a + 1"'); - const childEl = ctx.debugElement.children[0]; - ctx.componentInstance.a = 0; - childEl.triggerEventHandler('event', 'EVENT'); - expect(ctx.componentInstance.a).toEqual(1); - })); - }); + it('should throw when trying to assign to a local', fakeAsync(() => { + expect(() => { + _bindSimpleProp('(event)="$event=1"'); + }) + .toThrowError(new RegExp( + 'Cannot assign value (.*) to template variable (.*). Template variables are read-only.')); + })); + it('should support short-circuiting', fakeAsync(() => { + const ctx = _bindSimpleProp('(event)="true ? a = a + 1 : a = a + 1"'); + const childEl = ctx.debugElement.children[0]; + ctx.componentInstance.a = 0; + childEl.triggerEventHandler('event', 'EVENT'); + expect(ctx.componentInstance.a).toEqual(1); + })); }); + }); - describe('RendererFactory', () => { - it('should call the begin and end methods on the renderer factory when change detection is called', - fakeAsync(() => { - const ctx = createCompFixture('<div testDirective [a]="42"></div>'); - const rf = TestBed.inject(RendererFactory2); - spyOn(rf, 'begin'); - spyOn(rf, 'end'); - expect(rf.begin).not.toHaveBeenCalled(); - expect(rf.end).not.toHaveBeenCalled(); + describe('RendererFactory', () => { + it('should call the begin and end methods on the renderer factory when change detection is called', + fakeAsync(() => { + const ctx = createCompFixture('<div testDirective [a]="42"></div>'); + const rf = TestBed.inject(RendererFactory2); + // TODO: @JiaLiPassion, need to wait @types/jasmine to fix the + // optional method infer issue. + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486 + spyOn(rf as any, 'begin'); + spyOn(rf as any, 'end'); + expect(rf.begin).not.toHaveBeenCalled(); + expect(rf.end).not.toHaveBeenCalled(); + + ctx.detectChanges(false); + expect(rf.begin).toHaveBeenCalled(); + expect(rf.end).toHaveBeenCalled(); + })); + }); + describe('change notification', () => { + describe('updating directives', () => { + it('should happen without invoking the renderer', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective [a]="42"></div>'); ctx.detectChanges(false); - expect(rf.begin).toHaveBeenCalled(); - expect(rf.end).toHaveBeenCalled(); + expect(renderLog.log).toEqual([]); + expect(queryDirs(ctx.debugElement, TestDirective)[0].a).toEqual(42); })); }); - describe('change notification', () => { - describe('updating directives', () => { - it('should happen without invoking the renderer', fakeAsync(() => { - const ctx = createCompFixture('<div testDirective [a]="42"></div>'); - ctx.detectChanges(false); - expect(renderLog.log).toEqual([]); - expect(queryDirs(ctx.debugElement, TestDirective)[0].a).toEqual(42); - })); - }); - - describe('reading directives', () => { - it('should read directive properties', fakeAsync(() => { - const ctx = createCompFixture( - '<div testDirective [a]="42" ref-dir="testDirective" [id]="dir.a"></div>'); - ctx.detectChanges(false); - expect(renderLog.loggedValues).toEqual([42]); - })); - }); - - describe('ngOnChanges', () => { - it('should notify the directive when a group of records changes', fakeAsync(() => { - const ctx = createCompFixture( - '<div [testDirective]="\'aName\'" [a]="1" [b]="2"></div><div [testDirective]="\'bName\'" [a]="4"></div>'); - ctx.detectChanges(false); - - const dirs = <TestDirective[]>queryDirs(ctx.debugElement, TestDirective); - expect(dirs[0].changes).toEqual({ - 'a': new SimpleChange(undefined, 1, true), - 'b': new SimpleChange(undefined, 2, true), - 'name': new SimpleChange(undefined, 'aName', true) - }); - expect(dirs[1].changes).toEqual({ - 'a': new SimpleChange(undefined, 4, true), - 'name': new SimpleChange(undefined, 'bName', true) - }); - })); - }); + describe('reading directives', () => { + it('should read directive properties', fakeAsync(() => { + const ctx = createCompFixture( + '<div testDirective [a]="42" ref-dir="testDirective" [id]="dir.a"></div>'); + ctx.detectChanges(false); + expect(renderLog.loggedValues).toEqual([42]); + })); }); - describe('lifecycle', () => { - function createCompWithContentAndViewChild(): ComponentFixture<any> { - TestBed.overrideComponent(AnotherComponent, { - set: new Component({ - selector: 'other-cmp', - template: '<div testDirective="viewChild"></div>', - }) - }); - - return createCompFixture( - '<div testDirective="parent"><div *ngIf="true" testDirective="contentChild"></div><other-cmp></other-cmp></div>', - TestComponent); - } + describe('ngOnChanges', () => { + it('should notify the directive when a group of records changes', fakeAsync(() => { + const ctx = createCompFixture( + '<div [testDirective]="\'aName\'" [a]="1" [b]="2"></div><div [testDirective]="\'bName\'" [a]="4"></div>'); + ctx.detectChanges(false); - describe('ngOnInit', () => { - it('should be called after ngOnChanges', fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); - expect(directiveLog.filter(['ngOnInit', 'ngOnChanges'])).toEqual([]); + const dirs = <TestDirective[]>queryDirs(ctx.debugElement, TestDirective); + expect(dirs[0].changes).toEqual({ + 'a': new SimpleChange(undefined, 1, true), + 'b': new SimpleChange(undefined, 2, true), + 'name': new SimpleChange(undefined, 'aName', true) + }); + expect(dirs[1].changes).toEqual({ + 'a': new SimpleChange(undefined, 4, true), + 'name': new SimpleChange(undefined, 'bName', true) + }); + })); + }); + }); - ctx.detectChanges(false); + describe('lifecycle', () => { + function createCompWithContentAndViewChild(): ComponentFixture<any> { + TestBed.overrideComponent(AnotherComponent, { + set: new Component({ + selector: 'other-cmp', + template: '<div testDirective="viewChild"></div>', + }) + }); - expect(directiveLog.filter(['ngOnInit', 'ngOnChanges'])).toEqual([ - 'dir.ngOnChanges', 'dir.ngOnInit' - ]); - directiveLog.clear(); + return createCompFixture( + '<div testDirective="parent"><div *ngIf="true" testDirective="contentChild"></div><other-cmp></other-cmp></div>', + TestComponent); + } - ctx.detectChanges(false); + describe('ngOnInit', () => { + it('should be called after ngOnChanges', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); + expect(directiveLog.filter(['ngOnInit', 'ngOnChanges'])).toEqual([]); - expect(directiveLog.filter(['ngOnInit'])).toEqual([]); - })); + ctx.detectChanges(false); - it('should only be called only once', fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); + expect(directiveLog.filter(['ngOnInit', 'ngOnChanges'])).toEqual([ + 'dir.ngOnChanges', 'dir.ngOnInit' + ]); + directiveLog.clear(); - ctx.detectChanges(false); + ctx.detectChanges(false); - expect(directiveLog.filter(['ngOnInit'])).toEqual(['dir.ngOnInit']); + expect(directiveLog.filter(['ngOnInit'])).toEqual([]); + })); - // reset directives - directiveLog.clear(); + it('should only be called only once', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); - // Verify that checking should not call them. - ctx.checkNoChanges(); + ctx.detectChanges(false); - expect(directiveLog.filter(['ngOnInit'])).toEqual([]); + expect(directiveLog.filter(['ngOnInit'])).toEqual(['dir.ngOnInit']); - // re-verify that changes should not call them - ctx.detectChanges(false); + // reset directives + directiveLog.clear(); - expect(directiveLog.filter(['ngOnInit'])).toEqual([]); - })); + // Verify that checking should not call them. + ctx.checkNoChanges(); - it('should not call ngOnInit again if it throws', fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir" throwOn="ngOnInit"></div>'); + expect(directiveLog.filter(['ngOnInit'])).toEqual([]); - let errored = false; - // First pass fails, but ngOnInit should be called. - try { - ctx.detectChanges(false); - } catch (e) { - expect(e.message).toBe('Boom!'); - errored = true; - } - expect(errored).toBe(true); - - expect(directiveLog.filter(['ngOnInit'])).toEqual(['dir.ngOnInit']); - directiveLog.clear(); + // re-verify that changes should not call them + ctx.detectChanges(false); - // Second change detection also fails, but this time ngOnInit should not be called. - try { - ctx.detectChanges(false); - } catch (e) { - expect(e.message).toBe('Boom!'); - throw new Error('Second detectChanges() should not have called ngOnInit.'); - } - expect(directiveLog.filter(['ngOnInit'])).toEqual([]); - })); - }); + expect(directiveLog.filter(['ngOnInit'])).toEqual([]); + })); - describe('ngDoCheck', () => { - it('should be called after ngOnInit', fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); + it('should not call ngOnInit again if it throws', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir" throwOn="ngOnInit"></div>'); + let errored = false; + // First pass fails, but ngOnInit should be called. + try { ctx.detectChanges(false); - expect(directiveLog.filter(['ngDoCheck', 'ngOnInit'])).toEqual([ - 'dir.ngOnInit', 'dir.ngDoCheck' - ]); - })); + } catch (e) { + expect(e.message).toBe('Boom!'); + errored = true; + } + expect(errored).toBe(true); - it('should be called on every detectChanges run, except for checkNoChanges', - fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); + expect(directiveLog.filter(['ngOnInit'])).toEqual(['dir.ngOnInit']); + directiveLog.clear(); + // Second change detection also fails, but this time ngOnInit should not be called. + try { ctx.detectChanges(false); + } catch (e) { + expect(e.message).toBe('Boom!'); + throw new Error('Second detectChanges() should not have called ngOnInit.'); + } + expect(directiveLog.filter(['ngOnInit'])).toEqual([]); + })); + }); - expect(directiveLog.filter(['ngDoCheck'])).toEqual(['dir.ngDoCheck']); + describe('ngDoCheck', () => { + it('should be called after ngOnInit', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); - // reset directives - directiveLog.clear(); + ctx.detectChanges(false); + expect(directiveLog.filter(['ngDoCheck', 'ngOnInit'])).toEqual([ + 'dir.ngOnInit', 'dir.ngDoCheck' + ]); + })); - // Verify that checking should not call them. - ctx.checkNoChanges(); + it('should be called on every detectChanges run, except for checkNoChanges', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); - expect(directiveLog.filter(['ngDoCheck'])).toEqual([]); + ctx.detectChanges(false); - // re-verify that changes are still detected - ctx.detectChanges(false); + expect(directiveLog.filter(['ngDoCheck'])).toEqual(['dir.ngDoCheck']); - expect(directiveLog.filter(['ngDoCheck'])).toEqual(['dir.ngDoCheck']); - })); - }); + // reset directives + directiveLog.clear(); - describe('ngAfterContentInit', () => { - it('should be called after processing the content children but before the view children', - fakeAsync(() => { - const ctx = createCompWithContentAndViewChild(); - ctx.detectChanges(false); - - expect(directiveLog.filter(['ngDoCheck', 'ngAfterContentInit'])).toEqual([ - 'parent.ngDoCheck', 'contentChild.ngDoCheck', 'contentChild.ngAfterContentInit', - 'parent.ngAfterContentInit', 'viewChild.ngDoCheck', 'viewChild.ngAfterContentInit' - ]); - })); + // Verify that checking should not call them. + ctx.checkNoChanges(); - it('should only be called only once', fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); + expect(directiveLog.filter(['ngDoCheck'])).toEqual([]); - ctx.detectChanges(false); + // re-verify that changes are still detected + ctx.detectChanges(false); - expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([ - 'dir.ngAfterContentInit' - ]); + expect(directiveLog.filter(['ngDoCheck'])).toEqual(['dir.ngDoCheck']); + })); + }); - // reset directives - directiveLog.clear(); + describe('ngAfterContentInit', () => { + it('should be called after processing the content children but before the view children', + fakeAsync(() => { + const ctx = createCompWithContentAndViewChild(); + ctx.detectChanges(false); - // Verify that checking should not call them. - ctx.checkNoChanges(); + expect(directiveLog.filter(['ngDoCheck', 'ngAfterContentInit'])).toEqual([ + 'parent.ngDoCheck', 'contentChild.ngDoCheck', 'contentChild.ngAfterContentInit', + 'parent.ngAfterContentInit', 'viewChild.ngDoCheck', 'viewChild.ngAfterContentInit' + ]); + })); - expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([]); + it('should only be called only once', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); - // re-verify that changes should not call them - ctx.detectChanges(false); + ctx.detectChanges(false); - expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([]); - })); + expect(directiveLog.filter(['ngAfterContentInit'])).toEqual(['dir.ngAfterContentInit']); - it('should not call ngAfterContentInit again if it throws', fakeAsync(() => { - const ctx = - createCompFixture('<div testDirective="dir" throwOn="ngAfterContentInit"></div>'); + // reset directives + directiveLog.clear(); - let errored = false; - // First pass fails, but ngAfterContentInit should be called. - try { - ctx.detectChanges(false); - } catch (e) { - errored = true; - } - expect(errored).toBe(true); - - expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([ - 'dir.ngAfterContentInit' - ]); - directiveLog.clear(); - - // Second change detection also fails, but this time ngAfterContentInit should not be - // called. - try { - ctx.detectChanges(false); - } catch (e) { - throw new Error('Second detectChanges() should not have run detection.'); - } - expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([]); - })); - }); + // Verify that checking should not call them. + ctx.checkNoChanges(); - describe('ngAfterContentChecked', () => { - it('should be called after the content children but before the view children', - fakeAsync(() => { - const ctx = createCompWithContentAndViewChild(); + expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([]); - ctx.detectChanges(false); + // re-verify that changes should not call them + ctx.detectChanges(false); - expect(directiveLog.filter(['ngDoCheck', 'ngAfterContentChecked'])).toEqual([ - 'parent.ngDoCheck', 'contentChild.ngDoCheck', 'contentChild.ngAfterContentChecked', - 'parent.ngAfterContentChecked', 'viewChild.ngDoCheck', - 'viewChild.ngAfterContentChecked' - ]); - })); + expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([]); + })); - it('should be called on every detectChanges run, except for checkNoChanges', - fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); + it('should not call ngAfterContentInit again if it throws', fakeAsync(() => { + const ctx = + createCompFixture('<div testDirective="dir" throwOn="ngAfterContentInit"></div>'); + let errored = false; + // First pass fails, but ngAfterContentInit should be called. + try { ctx.detectChanges(false); + } catch (e) { + errored = true; + } + expect(errored).toBe(true); - expect(directiveLog.filter(['ngAfterContentChecked'])).toEqual([ - 'dir.ngAfterContentChecked' - ]); - - // reset directives - directiveLog.clear(); - - // Verify that checking should not call them. - ctx.checkNoChanges(); - - expect(directiveLog.filter(['ngAfterContentChecked'])).toEqual([]); + expect(directiveLog.filter(['ngAfterContentInit'])).toEqual(['dir.ngAfterContentInit']); + directiveLog.clear(); - // re-verify that changes are still detected + // Second change detection also fails, but this time ngAfterContentInit should not be + // called. + try { ctx.detectChanges(false); + } catch (e) { + throw new Error('Second detectChanges() should not have run detection.'); + } + expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([]); + })); + }); - expect(directiveLog.filter(['ngAfterContentChecked'])).toEqual([ - 'dir.ngAfterContentChecked' - ]); - })); - - it('should be called in reverse order so the child is always notified before the parent', - fakeAsync(() => { - const ctx = createCompFixture( - '<div testDirective="parent"><div testDirective="child"></div></div><div testDirective="sibling"></div>'); - - ctx.detectChanges(false); + describe('ngAfterContentChecked', () => { + it('should be called after the content children but before the view children', + fakeAsync(() => { + const ctx = createCompWithContentAndViewChild(); - expect(directiveLog.filter(['ngAfterContentChecked'])).toEqual([ - 'child.ngAfterContentChecked', 'parent.ngAfterContentChecked', - 'sibling.ngAfterContentChecked' - ]); - })); - }); + ctx.detectChanges(false); + expect(directiveLog.filter(['ngDoCheck', 'ngAfterContentChecked'])).toEqual([ + 'parent.ngDoCheck', 'contentChild.ngDoCheck', 'contentChild.ngAfterContentChecked', + 'parent.ngAfterContentChecked', 'viewChild.ngDoCheck', + 'viewChild.ngAfterContentChecked' + ]); + })); - describe('ngAfterViewInit', () => { - it('should be called after processing the view children', fakeAsync(() => { - const ctx = createCompWithContentAndViewChild(); + it('should be called on every detectChanges run, except for checkNoChanges', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); - ctx.detectChanges(false); + ctx.detectChanges(false); - expect(directiveLog.filter(['ngDoCheck', 'ngAfterViewInit'])).toEqual([ - 'parent.ngDoCheck', 'contentChild.ngDoCheck', 'contentChild.ngAfterViewInit', - 'viewChild.ngDoCheck', 'viewChild.ngAfterViewInit', 'parent.ngAfterViewInit' - ]); - })); + expect(directiveLog.filter(['ngAfterContentChecked'])).toEqual([ + 'dir.ngAfterContentChecked' + ]); - it('should only be called only once', fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); + // reset directives + directiveLog.clear(); - ctx.detectChanges(false); + // Verify that checking should not call them. + ctx.checkNoChanges(); - expect(directiveLog.filter(['ngAfterViewInit'])).toEqual(['dir.ngAfterViewInit']); + expect(directiveLog.filter(['ngAfterContentChecked'])).toEqual([]); - // reset directives - directiveLog.clear(); + // re-verify that changes are still detected + ctx.detectChanges(false); - // Verify that checking should not call them. - ctx.checkNoChanges(); + expect(directiveLog.filter(['ngAfterContentChecked'])).toEqual([ + 'dir.ngAfterContentChecked' + ]); + })); - expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); + it('should be called in reverse order so the child is always notified before the parent', + fakeAsync(() => { + const ctx = createCompFixture( + '<div testDirective="parent"><div testDirective="child"></div></div><div testDirective="sibling"></div>'); - // re-verify that changes should not call them - ctx.detectChanges(false); + ctx.detectChanges(false); - expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); - })); + expect(directiveLog.filter(['ngAfterContentChecked'])).toEqual([ + 'child.ngAfterContentChecked', 'parent.ngAfterContentChecked', + 'sibling.ngAfterContentChecked' + ]); + })); + }); - it('should not call ngAfterViewInit again if it throws', fakeAsync(() => { - const ctx = - createCompFixture('<div testDirective="dir" throwOn="ngAfterViewInit"></div>'); - let errored = false; - // First pass fails, but ngAfterViewInit should be called. - try { - ctx.detectChanges(false); - } catch (e) { - errored = true; - } - expect(errored).toBe(true); + describe('ngAfterViewInit', () => { + it('should be called after processing the view children', fakeAsync(() => { + const ctx = createCompWithContentAndViewChild(); - expect(directiveLog.filter(['ngAfterViewInit'])).toEqual(['dir.ngAfterViewInit']); - directiveLog.clear(); + ctx.detectChanges(false); - // Second change detection also fails, but this time ngAfterViewInit should not be - // called. - try { - ctx.detectChanges(false); - } catch (e) { - throw new Error('Second detectChanges() should not have run detection.'); - } - expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); - })); - }); + expect(directiveLog.filter(['ngDoCheck', 'ngAfterViewInit'])).toEqual([ + 'parent.ngDoCheck', 'contentChild.ngDoCheck', 'contentChild.ngAfterViewInit', + 'viewChild.ngDoCheck', 'viewChild.ngAfterViewInit', 'parent.ngAfterViewInit' + ]); + })); - describe('ngAfterViewChecked', () => { - it('should be called after processing the view children', fakeAsync(() => { - const ctx = createCompWithContentAndViewChild(); + it('should only be called only once', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); - ctx.detectChanges(false); + ctx.detectChanges(false); - expect(directiveLog.filter(['ngDoCheck', 'ngAfterViewChecked'])).toEqual([ - 'parent.ngDoCheck', 'contentChild.ngDoCheck', 'contentChild.ngAfterViewChecked', - 'viewChild.ngDoCheck', 'viewChild.ngAfterViewChecked', 'parent.ngAfterViewChecked' - ]); - })); + expect(directiveLog.filter(['ngAfterViewInit'])).toEqual(['dir.ngAfterViewInit']); - it('should be called on every detectChanges run, except for checkNoChanges', - fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); + // reset directives + directiveLog.clear(); - ctx.detectChanges(false); + // Verify that checking should not call them. + ctx.checkNoChanges(); - expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual([ - 'dir.ngAfterViewChecked' - ]); + expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); - // reset directives - directiveLog.clear(); + // re-verify that changes should not call them + ctx.detectChanges(false); - // Verify that checking should not call them. - ctx.checkNoChanges(); + expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); + })); - expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual([]); + it('should not call ngAfterViewInit again if it throws', fakeAsync(() => { + const ctx = + createCompFixture('<div testDirective="dir" throwOn="ngAfterViewInit"></div>'); - // re-verify that changes are still detected + let errored = false; + // First pass fails, but ngAfterViewInit should be called. + try { ctx.detectChanges(false); + } catch (e) { + errored = true; + } + expect(errored).toBe(true); - expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual([ - 'dir.ngAfterViewChecked' - ]); - })); - - it('should be called in reverse order so the child is always notified before the parent', - fakeAsync(() => { - const ctx = createCompFixture( - '<div testDirective="parent"><div testDirective="child"></div></div><div testDirective="sibling"></div>'); + expect(directiveLog.filter(['ngAfterViewInit'])).toEqual(['dir.ngAfterViewInit']); + directiveLog.clear(); + // Second change detection also fails, but this time ngAfterViewInit should not be + // called. + try { ctx.detectChanges(false); + } catch (e) { + throw new Error('Second detectChanges() should not have run detection.'); + } + expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); + })); + }); - expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual([ - 'child.ngAfterViewChecked', 'parent.ngAfterViewChecked', 'sibling.ngAfterViewChecked' - ]); - })); - }); + describe('ngAfterViewChecked', () => { + it('should be called after processing the view children', fakeAsync(() => { + const ctx = createCompWithContentAndViewChild(); - describe('ngOnDestroy', () => { - it('should be called on view destruction', fakeAsync(() => { - const ctx = createCompFixture('<div testDirective="dir"></div>'); - ctx.detectChanges(false); + ctx.detectChanges(false); - ctx.destroy(); + expect(directiveLog.filter(['ngDoCheck', 'ngAfterViewChecked'])).toEqual([ + 'parent.ngDoCheck', 'contentChild.ngDoCheck', 'contentChild.ngAfterViewChecked', + 'viewChild.ngDoCheck', 'viewChild.ngAfterViewChecked', 'parent.ngAfterViewChecked' + ]); + })); - expect(directiveLog.filter(['ngOnDestroy'])).toEqual(['dir.ngOnDestroy']); - })); + it('should be called on every detectChanges run, except for checkNoChanges', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); - it('should be called after processing the content and view children', fakeAsync(() => { - TestBed.overrideComponent(AnotherComponent, { - set: new Component( - {selector: 'other-cmp', template: '<div testDirective="viewChild"></div>'}) - }); + ctx.detectChanges(false); - const ctx = createCompFixture( - '<div testDirective="parent"><div *ngFor="let x of [0,1]" testDirective="contentChild{{x}}"></div>' + - '<other-cmp></other-cmp></div>', - TestComponent); + expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual(['dir.ngAfterViewChecked']); - ctx.detectChanges(false); - ctx.destroy(); + // reset directives + directiveLog.clear(); - expect(directiveLog.filter(['ngOnDestroy'])).toEqual([ - 'contentChild0.ngOnDestroy', 'contentChild1.ngOnDestroy', 'viewChild.ngOnDestroy', - 'parent.ngOnDestroy' - ]); - })); + // Verify that checking should not call them. + ctx.checkNoChanges(); - it('should be called in reverse order so the child is always notified before the parent', - fakeAsync(() => { - const ctx = createCompFixture( - '<div testDirective="parent"><div testDirective="child"></div></div><div testDirective="sibling"></div>'); + expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual([]); - ctx.detectChanges(false); - ctx.destroy(); + // re-verify that changes are still detected + ctx.detectChanges(false); - expect(directiveLog.filter(['ngOnDestroy'])).toEqual([ - 'child.ngOnDestroy', 'parent.ngOnDestroy', 'sibling.ngOnDestroy' - ]); - })); + expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual(['dir.ngAfterViewChecked']); + })); - it('should deliver synchronous events to parent', fakeAsync(() => { - const ctx = createCompFixture('<div (destroy)="a=$event" onDestroyDirective></div>'); + it('should be called in reverse order so the child is always notified before the parent', + fakeAsync(() => { + const ctx = createCompFixture( + '<div testDirective="parent"><div testDirective="child"></div></div><div testDirective="sibling"></div>'); - ctx.detectChanges(false); - ctx.destroy(); + ctx.detectChanges(false); - expect(ctx.componentInstance.a).toEqual('destroyed'); - })); + expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual([ + 'child.ngAfterViewChecked', 'parent.ngAfterViewChecked', 'sibling.ngAfterViewChecked' + ]); + })); + }); + describe('ngOnDestroy', () => { + it('should be called on view destruction', fakeAsync(() => { + const ctx = createCompFixture('<div testDirective="dir"></div>'); + ctx.detectChanges(false); - it('should call ngOnDestroy on pipes', fakeAsync(() => { - const ctx = createCompFixture('{{true | pipeWithOnDestroy }}'); + ctx.destroy(); - ctx.detectChanges(false); - ctx.destroy(); + expect(directiveLog.filter(['ngOnDestroy'])).toEqual(['dir.ngOnDestroy']); + })); - expect(directiveLog.filter(['ngOnDestroy'])).toEqual([ - 'pipeWithOnDestroy.ngOnDestroy' - ]); - })); + it('should be called after processing the content and view children', fakeAsync(() => { + TestBed.overrideComponent(AnotherComponent, { + set: new Component( + {selector: 'other-cmp', template: '<div testDirective="viewChild"></div>'}) + }); - it('should call ngOnDestroy on an injectable class', fakeAsync(() => { - TestBed.overrideDirective( - TestDirective, {set: {providers: [InjectableWithLifecycle]}}); + const ctx = createCompFixture( + '<div testDirective="parent"><div *ngFor="let x of [0,1]" testDirective="contentChild{{x}}"></div>' + + '<other-cmp></other-cmp></div>', + TestComponent); - const ctx = createCompFixture('<div testDirective="dir"></div>', TestComponent); + ctx.detectChanges(false); + ctx.destroy(); - ctx.debugElement.children[0].injector.get(InjectableWithLifecycle); - ctx.detectChanges(false); + expect(directiveLog.filter(['ngOnDestroy'])).toEqual([ + 'contentChild0.ngOnDestroy', 'contentChild1.ngOnDestroy', 'viewChild.ngOnDestroy', + 'parent.ngOnDestroy' + ]); + })); - ctx.destroy(); + it('should be called in reverse order so the child is always notified before the parent', + fakeAsync(() => { + const ctx = createCompFixture( + '<div testDirective="parent"><div testDirective="child"></div></div><div testDirective="sibling"></div>'); - // We don't care about the exact order in this test. - expect(directiveLog.filter(['ngOnDestroy']).sort()).toEqual([ - 'dir.ngOnDestroy', 'injectable.ngOnDestroy' - ]); - })); - }); - }); + ctx.detectChanges(false); + ctx.destroy(); - describe('enforce no new changes', () => { - it('should throw when a record gets changed after it has been checked', fakeAsync(() => { - @Directive({selector: '[changed]'}) - class ChangingDirective { - @Input() changed: any; - } + expect(directiveLog.filter(['ngOnDestroy'])).toEqual([ + 'child.ngOnDestroy', 'parent.ngOnDestroy', 'sibling.ngOnDestroy' + ]); + })); - TestBed.configureTestingModule({declarations: [ChangingDirective]}); + it('should deliver synchronous events to parent', fakeAsync(() => { + const ctx = createCompFixture('<div (destroy)="a=$event" onDestroyDirective></div>'); - const ctx = createCompFixture('<div [id]="a" [changed]="b"></div>', TestData); + ctx.detectChanges(false); + ctx.destroy(); - ctx.componentInstance.b = 1; - const errMsgRegExp = ivyEnabled ? - /Previous value: 'undefined'\. Current value: '1'/g : - /Previous value: 'changed: undefined'\. Current value: 'changed: 1'/g; - expect(() => ctx.checkNoChanges()).toThrowError(errMsgRegExp); + expect(ctx.componentInstance.a).toEqual('destroyed'); })); - it('should throw when a record gets changed after the first change detection pass', - fakeAsync(() => { - @Directive({selector: '[changed]'}) - class ChangingDirective { - @Input() changed: any; - } - - TestBed.configureTestingModule({declarations: [ChangingDirective]}); - - const ctx = createCompFixture('<div [id]="a" [changed]="b"></div>', TestData); + it('should call ngOnDestroy on pipes', fakeAsync(() => { + const ctx = createCompFixture('{{true | pipeWithOnDestroy }}'); - ctx.componentInstance.b = 1; - ctx.detectChanges(); + ctx.detectChanges(false); + ctx.destroy(); - ctx.componentInstance.b = 2; - const errMsgRegExp = ivyEnabled ? - /Previous value: '1'\. Current value: '2'/g : - /Previous value: 'changed: 1'\. Current value: 'changed: 2'/g; - expect(() => ctx.checkNoChanges()).toThrowError(errMsgRegExp); + expect(directiveLog.filter(['ngOnDestroy'])).toEqual(['pipeWithOnDestroy.ngOnDestroy']); })); - it('should warn when the view has been created in a cd hook', fakeAsync(() => { - const ctx = createCompFixture('<div *gh9882>{{ a }}</div>', TestData); - ctx.componentInstance.a = 1; - expect(() => ctx.detectChanges()) - .toThrowError( - /It seems like the view has been created after its parent and its children have been dirty checked/); + it('should call ngOnDestroy on an injectable class', fakeAsync(() => { + TestBed.overrideDirective(TestDirective, {set: {providers: [InjectableWithLifecycle]}}); - // subsequent change detection should run without issues - ctx.detectChanges(); - })); + const ctx = createCompFixture('<div testDirective="dir"></div>', TestComponent); - it('should not throw when two arrays are structurally the same', fakeAsync(() => { - const ctx = _bindSimpleValue('a', TestData); - ctx.componentInstance.a = ['value']; + ctx.debugElement.children[0].injector.get(InjectableWithLifecycle); ctx.detectChanges(false); - ctx.componentInstance.a = ['value']; - expect(() => ctx.checkNoChanges()).not.toThrow(); - })); - it('should not break the next run', fakeAsync(() => { - const ctx = _bindSimpleValue('a', TestData); - ctx.componentInstance.a = 'value'; - expect(() => ctx.checkNoChanges()).toThrow(); + ctx.destroy(); - ctx.detectChanges(); - expect(renderLog.loggedValues).toEqual(['value']); + // We don't care about the exact order in this test. + expect(directiveLog.filter(['ngOnDestroy']).sort()).toEqual([ + 'dir.ngOnDestroy', 'injectable.ngOnDestroy' + ]); })); + }); + }); - it('should not break the next run (view engine and ivy)', fakeAsync(() => { - const ctx = _bindSimpleValue('a', TestData); - - ctx.detectChanges(); - renderLog.clear(); + describe('enforce no new changes', () => { + it('should throw when a record gets changed after it has been checked', fakeAsync(() => { + @Directive({selector: '[changed]'}) + class ChangingDirective { + @Input() changed: any; + } + + TestBed.configureTestingModule({declarations: [ChangingDirective]}); + + const ctx = createCompFixture('<div [id]="a" [changed]="b"></div>', TestData); + + ctx.componentInstance.b = 1; + const errMsgRegExp = ivyEnabled ? + /Previous value: 'undefined'\. Current value: '1'/g : + /Previous value: 'changed: undefined'\. Current value: 'changed: 1'/g; + expect(() => ctx.checkNoChanges()).toThrowError(errMsgRegExp); + })); + + + it('should throw when a record gets changed after the first change detection pass', + fakeAsync(() => { + @Directive({selector: '[changed]'}) + class ChangingDirective { + @Input() changed: any; + } + + TestBed.configureTestingModule({declarations: [ChangingDirective]}); + + const ctx = createCompFixture('<div [id]="a" [changed]="b"></div>', TestData); + + ctx.componentInstance.b = 1; + ctx.detectChanges(); + + ctx.componentInstance.b = 2; + const errMsgRegExp = ivyEnabled ? + /Previous value: '1'\. Current value: '2'/g : + /Previous value: 'changed: 1'\. Current value: 'changed: 2'/g; + expect(() => ctx.checkNoChanges()).toThrowError(errMsgRegExp); + })); + + it('should warn when the view has been created in a cd hook', fakeAsync(() => { + const ctx = createCompFixture('<div *gh9882>{{ a }}</div>', TestData); + ctx.componentInstance.a = 1; + expect(() => ctx.detectChanges()) + .toThrowError( + /It seems like the view has been created after its parent and its children have been dirty checked/); + + // subsequent change detection should run without issues + ctx.detectChanges(); + })); + + it('should not throw when two arrays are structurally the same', fakeAsync(() => { + const ctx = _bindSimpleValue('a', TestData); + ctx.componentInstance.a = ['value']; + ctx.detectChanges(false); + ctx.componentInstance.a = ['value']; + expect(() => ctx.checkNoChanges()).not.toThrow(); + })); + + it('should not break the next run', fakeAsync(() => { + const ctx = _bindSimpleValue('a', TestData); + ctx.componentInstance.a = 'value'; + expect(() => ctx.checkNoChanges()).toThrow(); + + ctx.detectChanges(); + expect(renderLog.loggedValues).toEqual(['value']); + })); + + it('should not break the next run (view engine and ivy)', fakeAsync(() => { + const ctx = _bindSimpleValue('a', TestData); + + ctx.detectChanges(); + renderLog.clear(); + + ctx.componentInstance.a = 'value'; + expect(() => ctx.checkNoChanges()).toThrow(); + + ctx.detectChanges(); + expect(renderLog.loggedValues).toEqual(['value']); + })); + }); - ctx.componentInstance.a = 'value'; - expect(() => ctx.checkNoChanges()).toThrow(); + describe('mode', () => { + it('Detached', fakeAsync(() => { + const ctx = createCompFixture('<comp-with-ref></comp-with-ref>'); + const cmp: CompWithRef = queryDirs(ctx.debugElement, CompWithRef)[0]; + cmp.value = 'hello'; + cmp.changeDetectorRef.detach(); - ctx.detectChanges(); - expect(renderLog.loggedValues).toEqual(['value']); - })); - }); + ctx.detectChanges(); - describe('mode', () => { - it('Detached', fakeAsync(() => { - const ctx = createCompFixture('<comp-with-ref></comp-with-ref>'); - const cmp: CompWithRef = queryDirs(ctx.debugElement, CompWithRef)[0]; - cmp.value = 'hello'; - cmp.changeDetectorRef.detach(); + expect(renderLog.log).toEqual([]); + })); - ctx.detectChanges(); + it('Detached should disable OnPush', fakeAsync(() => { + const ctx = createCompFixture('<push-cmp [value]="value"></push-cmp>'); + ctx.componentInstance.value = 0; + ctx.detectChanges(); + renderLog.clear(); - expect(renderLog.log).toEqual([]); - })); + const cmp: CompWithRef = queryDirs(ctx.debugElement, PushComp)[0]; + cmp.changeDetectorRef.detach(); - it('Detached should disable OnPush', fakeAsync(() => { - const ctx = createCompFixture('<push-cmp [value]="value"></push-cmp>'); - ctx.componentInstance.value = 0; - ctx.detectChanges(); - renderLog.clear(); + ctx.componentInstance.value = 1; + ctx.detectChanges(); - const cmp: CompWithRef = queryDirs(ctx.debugElement, PushComp)[0]; - cmp.changeDetectorRef.detach(); + expect(renderLog.log).toEqual([]); + })); - ctx.componentInstance.value = 1; - ctx.detectChanges(); + it('Detached view can be checked locally', fakeAsync(() => { + const ctx = createCompFixture('<wrap-comp-with-ref></wrap-comp-with-ref>'); + const cmp: CompWithRef = queryDirs(ctx.debugElement, CompWithRef)[0]; + cmp.value = 'hello'; + cmp.changeDetectorRef.detach(); + expect(renderLog.log).toEqual([]); - expect(renderLog.log).toEqual([]); - })); + ctx.detectChanges(); - it('Detached view can be checked locally', fakeAsync(() => { - const ctx = createCompFixture('<wrap-comp-with-ref></wrap-comp-with-ref>'); - const cmp: CompWithRef = queryDirs(ctx.debugElement, CompWithRef)[0]; - cmp.value = 'hello'; - cmp.changeDetectorRef.detach(); - expect(renderLog.log).toEqual([]); + expect(renderLog.log).toEqual([]); - ctx.detectChanges(); + cmp.changeDetectorRef.detectChanges(); - expect(renderLog.log).toEqual([]); + expect(renderLog.log).toEqual(['{{hello}}']); + })); - cmp.changeDetectorRef.detectChanges(); - expect(renderLog.log).toEqual(['{{hello}}']); - })); + it('Reattaches', fakeAsync(() => { + const ctx = createCompFixture('<comp-with-ref></comp-with-ref>'); + const cmp: CompWithRef = queryDirs(ctx.debugElement, CompWithRef)[0]; + cmp.value = 'hello'; + cmp.changeDetectorRef.detach(); - it('Reattaches', fakeAsync(() => { - const ctx = createCompFixture('<comp-with-ref></comp-with-ref>'); - const cmp: CompWithRef = queryDirs(ctx.debugElement, CompWithRef)[0]; + ctx.detectChanges(); - cmp.value = 'hello'; - cmp.changeDetectorRef.detach(); + expect(renderLog.log).toEqual([]); - ctx.detectChanges(); + cmp.changeDetectorRef.reattach(); - expect(renderLog.log).toEqual([]); + ctx.detectChanges(); - cmp.changeDetectorRef.reattach(); + expect(renderLog.log).toEqual(['{{hello}}']); + })); - ctx.detectChanges(); + it('Reattaches in the original cd mode', fakeAsync(() => { + const ctx = createCompFixture('<push-cmp></push-cmp>'); + const cmp: PushComp = queryDirs(ctx.debugElement, PushComp)[0]; + cmp.changeDetectorRef.detach(); + cmp.changeDetectorRef.reattach(); - expect(renderLog.log).toEqual(['{{hello}}']); + // renderCount should NOT be incremented with each CD as CD mode + // should be resetted to + // on-push + ctx.detectChanges(); + expect(cmp.renderCount).toBeGreaterThan(0); + const count = cmp.renderCount; - })); + ctx.detectChanges(); + expect(cmp.renderCount).toBe(count); + })); + }); - it('Reattaches in the original cd mode', fakeAsync(() => { - const ctx = createCompFixture('<push-cmp></push-cmp>'); - const cmp: PushComp = queryDirs(ctx.debugElement, PushComp)[0]; - cmp.changeDetectorRef.detach(); - cmp.changeDetectorRef.reattach(); - - // renderCount should NOT be incremented with each CD as CD mode - // should be resetted to - // on-push - ctx.detectChanges(); - expect(cmp.renderCount).toBeGreaterThan(0); - const count = cmp.renderCount; - - ctx.detectChanges(); - expect(cmp.renderCount).toBe(count); - })); + describe('multi directive order', () => { + modifiedInIvy('order of bindings to directive inputs is different in ivy') + .it('should follow the DI order for the same element', fakeAsync(() => { + const ctx = + createCompFixture('<div orderCheck2="2" orderCheck0="0" orderCheck1="1"></div>'); - }); + ctx.detectChanges(false); + ctx.destroy(); - describe('multi directive order', () => { - modifiedInIvy('order of bindings to directive inputs is different in ivy') - .it('should follow the DI order for the same element', fakeAsync(() => { - const ctx = createCompFixture( - '<div orderCheck2="2" orderCheck0="0" orderCheck1="1"></div>'); + expect(directiveLog.filter(['set'])).toEqual(['0.set', '1.set', '2.set']); + })); + }); - ctx.detectChanges(false); - ctx.destroy(); + describe('nested view recursion', () => { + it('should recurse into nested components even if there are no bindings in the component view', + () => { + @Component({selector: 'nested', template: '{{name}}'}) + class Nested { + name = 'Tom'; + } + + TestBed.configureTestingModule({declarations: [Nested]}); + + const ctx = createCompFixture('<nested></nested>'); + ctx.detectChanges(); + expect(renderLog.loggedValues).toEqual(['Tom']); + }); + + it('should recurse into nested view containers even if there are no bindings in the component view', + () => { + @Component({template: '<ng-template #vc>{{name}}</ng-template>'}) + class Comp { + name = 'Tom'; + // TODO(issue/24571): remove '!'. + @ViewChild('vc', {read: ViewContainerRef, static: true}) vc!: ViewContainerRef; + // TODO(issue/24571): remove '!'. + @ViewChild(TemplateRef, {static: true}) template !: TemplateRef<any>; + } + + TestBed.configureTestingModule({declarations: [Comp]}); + initHelpers(); + + const ctx = TestBed.createComponent(Comp); + ctx.detectChanges(); + expect(renderLog.loggedValues).toEqual([]); + + ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template); + ctx.detectChanges(); + expect(renderLog.loggedValues).toEqual(['Tom']); + }); + + describe('projected views', () => { + let log: string[]; + + @Directive({selector: '[i]'}) + class DummyDirective { + @Input() i: any; + } - expect(directiveLog.filter(['set'])).toEqual(['0.set', '1.set', '2.set']); - })); - }); + @Component({ + selector: 'main-cmp', + template: + `<span [i]="log('start')"></span><outer-cmp><ng-template><span [i]="log('tpl')"></span></ng-template></outer-cmp>` + }) + class MainComp { + constructor(public cdRef: ChangeDetectorRef) {} + log(id: string) { + log.push(`main-${id}`); + } + } - describe('nested view recursion', () => { - it('should recurse into nested components even if there are no bindings in the component view', - () => { - @Component({selector: 'nested', template: '{{name}}'}) - class Nested { - name = 'Tom'; - } + @Component({ + selector: 'outer-cmp', + template: + `<span [i]="log('start')"></span><inner-cmp [outerTpl]="tpl"><ng-template><span [i]="log('tpl')"></span></ng-template></inner-cmp>` + }) + class OuterComp { + // TODO(issue/24571): remove '!'. + @ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>; + + constructor(public cdRef: ChangeDetectorRef) {} + log(id: string) { + log.push(`outer-${id}`); + } + } - TestBed.configureTestingModule({declarations: [Nested]}); + @Component({ + selector: 'inner-cmp', + template: + `<span [i]="log('start')"></span>><ng-container [ngTemplateOutlet]="outerTpl"></ng-container><ng-container [ngTemplateOutlet]="tpl"></ng-container>` + }) + class InnerComp { + // TODO(issue/24571): remove '!'. + @ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>; + + // TODO(issue/24571): remove '!'. + @Input() outerTpl!: TemplateRef<any>; + + constructor(public cdRef: ChangeDetectorRef) {} + log(id: string) { + log.push(`inner-${id}`); + } + } - const ctx = createCompFixture('<nested></nested>'); - ctx.detectChanges(); - expect(renderLog.loggedValues).toEqual(['Tom']); - }); + let ctx: ComponentFixture<MainComp>; + let mainComp: MainComp; + let outerComp: OuterComp; + let innerComp: InnerComp; + + beforeEach(() => { + log = []; + ctx = TestBed + .configureTestingModule( + {declarations: [MainComp, OuterComp, InnerComp, DummyDirective]}) + .createComponent(MainComp); + mainComp = ctx.componentInstance; + outerComp = ctx.debugElement.query(By.directive(OuterComp)).injector.get(OuterComp); + innerComp = ctx.debugElement.query(By.directive(InnerComp)).injector.get(InnerComp); + }); - it('should recurse into nested view containers even if there are no bindings in the component view', - () => { - @Component({template: '<ng-template #vc>{{name}}</ng-template>'}) - class Comp { - name = 'Tom'; - // TODO(issue/24571): remove '!'. - @ViewChild('vc', {read: ViewContainerRef, static: true}) vc !: ViewContainerRef; - // TODO(issue/24571): remove '!'. - @ViewChild(TemplateRef, {static: true}) template !: TemplateRef<any>; - } + it('should dirty check projected views in regular order', () => { + ctx.detectChanges(false); + expect(log).toEqual(['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']); - TestBed.configureTestingModule({declarations: [Comp]}); - initHelpers(); + log = []; + ctx.detectChanges(false); + expect(log).toEqual(['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']); + }); - const ctx = TestBed.createComponent(Comp); - ctx.detectChanges(); - expect(renderLog.loggedValues).toEqual([]); + it('should not dirty check projected views if neither the declaration nor the insertion place is dirty checked', + () => { + ctx.detectChanges(false); + log = []; + mainComp.cdRef.detach(); + ctx.detectChanges(false); - ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template); - ctx.detectChanges(); - expect(renderLog.loggedValues).toEqual(['Tom']); + expect(log).toEqual([]); }); - describe('projected views', () => { - let log: string[]; - - @Directive({selector: '[i]'}) - class DummyDirective { - @Input() - i: any; - } + it('should dirty check projected views if the insertion place is dirty checked', () => { + ctx.detectChanges(false); + log = []; - @Component({ - selector: 'main-cmp', - template: - `<span [i]="log('start')"></span><outer-cmp><ng-template><span [i]="log('tpl')"></span></ng-template></outer-cmp>` - }) - class MainComp { - constructor(public cdRef: ChangeDetectorRef) {} - log(id: string) { log.push(`main-${id}`); } - } + innerComp.cdRef.detectChanges(); + expect(log).toEqual(['inner-start', 'main-tpl', 'outer-tpl']); + }); - @Component({ - selector: 'outer-cmp', - template: - `<span [i]="log('start')"></span><inner-cmp [outerTpl]="tpl"><ng-template><span [i]="log('tpl')"></span></ng-template></inner-cmp>` - }) - class OuterComp { - // TODO(issue/24571): remove '!'. - @ContentChild(TemplateRef, {static: true}) - tpl !: TemplateRef<any>; + modifiedInIvy('Views should not be dirty checked if inserted into CD-detached view tree') + .it('should dirty check projected views if the declaration place is dirty checked', + () => { + ctx.detectChanges(false); + log = []; + innerComp.cdRef.detach(); + mainComp.cdRef.detectChanges(); - constructor(public cdRef: ChangeDetectorRef) {} - log(id: string) { log.push(`outer-${id}`); } - } + expect(log).toEqual(['main-start', 'outer-start', 'main-tpl', 'outer-tpl']); - @Component({ - selector: 'inner-cmp', - template: - `<span [i]="log('start')"></span>><ng-container [ngTemplateOutlet]="outerTpl"></ng-container><ng-container [ngTemplateOutlet]="tpl"></ng-container>` - }) - class InnerComp { - // TODO(issue/24571): remove '!'. - @ContentChild(TemplateRef, {static: true}) - tpl !: TemplateRef<any>; + log = []; + outerComp.cdRef.detectChanges(); - // TODO(issue/24571): remove '!'. - @Input() - outerTpl !: TemplateRef<any>; + expect(log).toEqual(['outer-start', 'outer-tpl']); - constructor(public cdRef: ChangeDetectorRef) {} - log(id: string) { log.push(`inner-${id}`); } - } + log = []; + outerComp.cdRef.detach(); + mainComp.cdRef.detectChanges(); - let ctx: ComponentFixture<MainComp>; - let mainComp: MainComp; - let outerComp: OuterComp; - let innerComp: InnerComp; - - beforeEach(() => { - log = []; - ctx = TestBed - .configureTestingModule( - {declarations: [MainComp, OuterComp, InnerComp, DummyDirective]}) - .createComponent(MainComp); - mainComp = ctx.componentInstance; - outerComp = ctx.debugElement.query(By.directive(OuterComp)).injector.get(OuterComp); - innerComp = ctx.debugElement.query(By.directive(InnerComp)).injector.get(InnerComp); - }); - - it('should dirty check projected views in regular order', () => { - ctx.detectChanges(false); - expect(log).toEqual( - ['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']); - - log = []; - ctx.detectChanges(false); - expect(log).toEqual( - ['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']); - }); - - it('should not dirty check projected views if neither the declaration nor the insertion place is dirty checked', - () => { - ctx.detectChanges(false); - log = []; - mainComp.cdRef.detach(); - ctx.detectChanges(false); + expect(log).toEqual(['main-start', 'main-tpl']); + }); - expect(log).toEqual([]); - }); + onlyInIvy('Views should not be dirty checked if inserted into CD-detached view tree') + .it('should not dirty check views that are inserted into a detached tree, even if the declaration place is dirty checked', + () => { + ctx.detectChanges(false); + log = []; + innerComp.cdRef.detach(); + mainComp.cdRef.detectChanges(); - it('should dirty check projected views if the insertion place is dirty checked', () => { - ctx.detectChanges(false); - log = []; + expect(log).toEqual(['main-start', 'outer-start']); - innerComp.cdRef.detectChanges(); - expect(log).toEqual(['inner-start', 'main-tpl', 'outer-tpl']); - }); + log = []; + outerComp.cdRef.detectChanges(); - modifiedInIvy('Views should not be dirty checked if inserted into CD-detached view tree') - .it('should dirty check projected views if the declaration place is dirty checked', - () => { - ctx.detectChanges(false); - log = []; - innerComp.cdRef.detach(); - mainComp.cdRef.detectChanges(); + expect(log).toEqual(['outer-start']); - expect(log).toEqual(['main-start', 'outer-start', 'main-tpl', 'outer-tpl']); + log = []; + outerComp.cdRef.detach(); + mainComp.cdRef.detectChanges(); - log = []; - outerComp.cdRef.detectChanges(); + expect(log).toEqual(['main-start']); + }); + }); + }); - expect(log).toEqual(['outer-start', 'outer-tpl']); + describe('class binding', () => { + it('should coordinate class attribute and class host binding', () => { + @Component({template: `<div class="{{initClasses}}" someDir></div>`}) + class Comp { + initClasses = 'init'; + } - log = []; - outerComp.cdRef.detach(); - mainComp.cdRef.detectChanges(); + @Directive({selector: '[someDir]'}) + class SomeDir { + @HostBinding('class.foo') fooClass = true; + } - expect(log).toEqual(['main-start', 'main-tpl']); - }); + const ctx = + TestBed.configureTestingModule({declarations: [Comp, SomeDir]}).createComponent(Comp); - onlyInIvy('Views should not be dirty checked if inserted into CD-detached view tree') - .it('should not dirty check views that are inserted into a detached tree, even if the declaration place is dirty checked', - () => { - ctx.detectChanges(false); - log = []; - innerComp.cdRef.detach(); - mainComp.cdRef.detectChanges(); + ctx.detectChanges(); - expect(log).toEqual(['main-start', 'outer-start']); + const divEl = ctx.debugElement.children[0]; + expect(divEl.nativeElement).toHaveCssClass('init'); + expect(divEl.nativeElement).toHaveCssClass('foo'); + }); + }); - log = []; - outerComp.cdRef.detectChanges(); + describe('lifecycle asserts', () => { + let logged: string[]; - expect(log).toEqual(['outer-start']); + function log(value: string) { + logged.push(value); + } + function clearLog() { + logged = []; + } - log = []; - outerComp.cdRef.detach(); - mainComp.cdRef.detectChanges(); + function expectOnceAndOnlyOnce(log: string) { + expect(logged.indexOf(log) >= 0) + .toBeTruthy(`'${log}' not logged. Log was ${JSON.stringify(logged)}`); + expect(logged.lastIndexOf(log) === logged.indexOf(log)) + .toBeTruthy(`'${log}' logged more than once. Log was ${JSON.stringify(logged)}`); + } - expect(log).toEqual(['main-start']); - }); - }); + beforeEach(() => { + clearLog(); }); - describe('class binding', () => { - it('should coordinate class attribute and class host binding', () => { - @Component({template: `<div class="{{initClasses}}" someDir></div>`}) - class Comp { - initClasses = 'init'; - } - - @Directive({selector: '[someDir]'}) - class SomeDir { - @HostBinding('class.foo') - fooClass = true; - } - - const ctx = - TestBed.configureTestingModule({declarations: [Comp, SomeDir]}).createComponent(Comp); - - ctx.detectChanges(); + enum LifetimeMethods { + None = 0, + ngOnInit = 1 << 0, + ngOnChanges = 1 << 1, + ngAfterViewInit = 1 << 2, + ngAfterContentInit = 1 << 3, + ngDoCheck = 1 << 4, + InitMethods = ngOnInit | ngAfterViewInit | ngAfterContentInit, + InitMethodsAndChanges = InitMethods | ngOnChanges, + All = InitMethodsAndChanges | ngDoCheck, + } - const divEl = ctx.debugElement.children[0]; - expect(divEl.nativeElement).toHaveCssClass('init'); - expect(divEl.nativeElement).toHaveCssClass('foo'); - }); - }); + function forEachMethod(methods: LifetimeMethods, cb: (method: LifetimeMethods) => void) { + if (methods & LifetimeMethods.ngOnInit) cb(LifetimeMethods.ngOnInit); + if (methods & LifetimeMethods.ngOnChanges) cb(LifetimeMethods.ngOnChanges); + if (methods & LifetimeMethods.ngAfterContentInit) cb(LifetimeMethods.ngAfterContentInit); + if (methods & LifetimeMethods.ngAfterViewInit) cb(LifetimeMethods.ngAfterViewInit); + if (methods & LifetimeMethods.ngDoCheck) cb(LifetimeMethods.ngDoCheck); + } - describe('lifecycle asserts', () => { - let logged: string[]; + interface Options { + childRecursion: LifetimeMethods; + childThrows: LifetimeMethods; + } - function log(value: string) { logged.push(value); } - function clearLog() { logged = []; } + describe('calling init', () => { + function initialize(options: Options) { + @Component({selector: 'my-child', template: ''}) + class MyChild { + private thrown = LifetimeMethods.None; - function expectOnceAndOnlyOnce(log: string) { - expect(logged.indexOf(log) >= 0) - .toBeTruthy(`'${log}' not logged. Log was ${JSON.stringify(logged)}`); - expect(logged.lastIndexOf(log) === logged.indexOf(log)) - .toBeTruthy(`'${log}' logged more than once. Log was ${JSON.stringify(logged)}`); - } + // TODO(issue/24571): remove '!'. + @Input() inp!: boolean; + @Output() outp = new EventEmitter<any>(); - beforeEach(() => { clearLog(); }); - - enum LifetimeMethods { - None = 0, - ngOnInit = 1 << 0, - ngOnChanges = 1 << 1, - ngAfterViewInit = 1 << 2, - ngAfterContentInit = 1 << 3, - ngDoCheck = 1 << 4, - InitMethods = ngOnInit | ngAfterViewInit | ngAfterContentInit, - InitMethodsAndChanges = InitMethods | ngOnChanges, - All = InitMethodsAndChanges | ngDoCheck, - } + constructor() {} - function forEachMethod(methods: LifetimeMethods, cb: (method: LifetimeMethods) => void) { - if (methods & LifetimeMethods.ngOnInit) cb(LifetimeMethods.ngOnInit); - if (methods & LifetimeMethods.ngOnChanges) cb(LifetimeMethods.ngOnChanges); - if (methods & LifetimeMethods.ngAfterContentInit) cb(LifetimeMethods.ngAfterContentInit); - if (methods & LifetimeMethods.ngAfterViewInit) cb(LifetimeMethods.ngAfterViewInit); - if (methods & LifetimeMethods.ngDoCheck) cb(LifetimeMethods.ngDoCheck); - } + ngDoCheck() { + this.check(LifetimeMethods.ngDoCheck); + } + ngOnInit() { + this.check(LifetimeMethods.ngOnInit); + } + ngOnChanges() { + this.check(LifetimeMethods.ngOnChanges); + } + ngAfterViewInit() { + this.check(LifetimeMethods.ngAfterViewInit); + } + ngAfterContentInit() { + this.check(LifetimeMethods.ngAfterContentInit); + } - interface Options { - childRecursion: LifetimeMethods; - childThrows: LifetimeMethods; - } + private check(method: LifetimeMethods) { + log(`MyChild::${LifetimeMethods[method]}()`); - describe('calling init', () => { - function initialize(options: Options) { - @Component({selector: 'my-child', template: ''}) - class MyChild { - private thrown = LifetimeMethods.None; - - // TODO(issue/24571): remove '!'. - @Input() inp !: boolean; - @Output() outp = new EventEmitter<any>(); - - constructor() {} - - ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); } - ngOnInit() { this.check(LifetimeMethods.ngOnInit); } - ngOnChanges() { this.check(LifetimeMethods.ngOnChanges); } - ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); } - ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); } - - private check(method: LifetimeMethods) { - log(`MyChild::${LifetimeMethods[method]}()`); - - if ((options.childRecursion & method) !== 0) { - if (logged.length < 20) { - this.outp.emit(null); - } else { - fail(`Unexpected MyChild::${LifetimeMethods[method]} recursion`); - } + if ((options.childRecursion & method) !== 0) { + if (logged.length < 20) { + this.outp.emit(null); + } else { + fail(`Unexpected MyChild::${LifetimeMethods[method]} recursion`); } - if ((options.childThrows & method) !== 0) { - if ((this.thrown & method) === 0) { - this.thrown |= method; - log(`<THROW from MyChild::${LifetimeMethods[method]}>()`); - throw new Error(`Throw from MyChild::${LifetimeMethods[method]}`); - } + } + if ((options.childThrows & method) !== 0) { + if ((this.thrown & method) === 0) { + this.thrown |= method; + log(`<THROW from MyChild::${LifetimeMethods[method]}>()`); + throw new Error(`Throw from MyChild::${LifetimeMethods[method]}`); } } } + } - @Component({ - selector: 'my-component', - template: `<my-child [inp]='true' (outp)='onOutp()'></my-child>` - }) - class MyComponent { - constructor(private changeDetectionRef: ChangeDetectorRef) {} - ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); } - ngOnInit() { this.check(LifetimeMethods.ngOnInit); } - ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); } - ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); } - onOutp() { - log('<RECURSION START>'); - this.changeDetectionRef.detectChanges(); - log('<RECURSION DONE>'); - } + @Component({ + selector: 'my-component', + template: `<my-child [inp]='true' (outp)='onOutp()'></my-child>` + }) + class MyComponent { + constructor(private changeDetectionRef: ChangeDetectorRef) {} + ngDoCheck() { + this.check(LifetimeMethods.ngDoCheck); + } + ngOnInit() { + this.check(LifetimeMethods.ngOnInit); + } + ngAfterViewInit() { + this.check(LifetimeMethods.ngAfterViewInit); + } + ngAfterContentInit() { + this.check(LifetimeMethods.ngAfterContentInit); + } + onOutp() { + log('<RECURSION START>'); + this.changeDetectionRef.detectChanges(); + log('<RECURSION DONE>'); + } - private check(method: LifetimeMethods) { - log(`MyComponent::${LifetimeMethods[method]}()`); - } + private check(method: LifetimeMethods) { + log(`MyComponent::${LifetimeMethods[method]}()`); } + } - TestBed.configureTestingModule({declarations: [MyChild, MyComponent]}); + TestBed.configureTestingModule({declarations: [MyChild, MyComponent]}); - return createCompFixture(`<my-component></my-component>`); - } + return createCompFixture(`<my-component></my-component>`); + } - function ensureOneInit(options: Options) { - const ctx = initialize(options); + function ensureOneInit(options: Options) { + const ctx = initialize(options); - const throws = options.childThrows != LifetimeMethods.None; - if (throws) { - log(`<CYCLE 0 START>`); - expect(() => { - // Expect child to throw. - ctx.detectChanges(); - }).toThrow(); - log(`<CYCLE 0 END>`); - log(`<CYCLE 1 START>`); - } - ctx.detectChanges(); - if (throws) log(`<CYCLE 1 DONE>`); - expectOnceAndOnlyOnce('MyComponent::ngOnInit()'); - expectOnceAndOnlyOnce('MyChild::ngOnInit()'); - expectOnceAndOnlyOnce('MyComponent::ngAfterViewInit()'); - expectOnceAndOnlyOnce('MyComponent::ngAfterContentInit()'); - expectOnceAndOnlyOnce('MyChild::ngAfterViewInit()'); - expectOnceAndOnlyOnce('MyChild::ngAfterContentInit()'); + const throws = options.childThrows != LifetimeMethods.None; + if (throws) { + log(`<CYCLE 0 START>`); + expect(() => { + // Expect child to throw. + ctx.detectChanges(); + }).toThrow(); + log(`<CYCLE 0 END>`); + log(`<CYCLE 1 START>`); } + ctx.detectChanges(); + if (throws) log(`<CYCLE 1 DONE>`); + expectOnceAndOnlyOnce('MyComponent::ngOnInit()'); + expectOnceAndOnlyOnce('MyChild::ngOnInit()'); + expectOnceAndOnlyOnce('MyComponent::ngAfterViewInit()'); + expectOnceAndOnlyOnce('MyComponent::ngAfterContentInit()'); + expectOnceAndOnlyOnce('MyChild::ngAfterViewInit()'); + expectOnceAndOnlyOnce('MyChild::ngAfterContentInit()'); + } - forEachMethod(LifetimeMethods.InitMethodsAndChanges, method => { - it(`should ensure that init hooks are called once an only once with recursion in ${LifetimeMethods[method]} `, - () => { - // Ensure all the init methods are called once. - ensureOneInit({childRecursion: method, childThrows: LifetimeMethods.None}); - }); - }); - forEachMethod(LifetimeMethods.All, method => { - it(`should ensure that init hooks are called once an only once with a throw in ${LifetimeMethods[method]} `, - () => { - // Ensure all the init methods are called once. - // the first cycle throws but the next cycle should complete the inits. - ensureOneInit({childRecursion: LifetimeMethods.None, childThrows: method}); - }); - }); + forEachMethod(LifetimeMethods.InitMethodsAndChanges, method => { + it(`should ensure that init hooks are called once an only once with recursion in ${ + LifetimeMethods[method]} `, + () => { + // Ensure all the init methods are called once. + ensureOneInit({childRecursion: method, childThrows: LifetimeMethods.None}); + }); + }); + forEachMethod(LifetimeMethods.All, method => { + it(`should ensure that init hooks are called once an only once with a throw in ${ + LifetimeMethods[method]} `, + () => { + // Ensure all the init methods are called once. + // the first cycle throws but the next cycle should complete the inits. + ensureOneInit({childRecursion: LifetimeMethods.None, childThrows: method}); + }); }); }); }); +}); })(); @Injectable() @@ -1748,7 +1780,9 @@ class DirectiveLog { this.entries.push(new DirectiveLogEntry(directiveName, method)); } - clear() { this.entries = []; } + clear() { + this.entries = []; + } filter(methods: string[]): string[] { return this.entries.filter((entry) => methods.indexOf(entry.method) !== -1) @@ -1760,32 +1794,44 @@ class DirectiveLog { @Pipe({name: 'countingPipe'}) class CountingPipe implements PipeTransform { state: number = 0; - transform(value: any) { return `${value} state:${this.state++}`; } + transform(value: any) { + return `${value} state:${this.state++}`; + } } @Pipe({name: 'countingImpurePipe', pure: false}) class CountingImpurePipe implements PipeTransform { state: number = 0; - transform(value: any) { return `${value} state:${this.state++}`; } + transform(value: any) { + return `${value} state:${this.state++}`; + } } @Pipe({name: 'pipeWithOnDestroy'}) class PipeWithOnDestroy implements PipeTransform, OnDestroy { constructor(private directiveLog: DirectiveLog) {} - ngOnDestroy() { this.directiveLog.add('pipeWithOnDestroy', 'ngOnDestroy'); } + ngOnDestroy() { + this.directiveLog.add('pipeWithOnDestroy', 'ngOnDestroy'); + } - transform(value: any): any { return null; } + transform(value: any): any { + return null; + } } @Pipe({name: 'identityPipe'}) class IdentityPipe implements PipeTransform { - transform(value: any) { return value; } + transform(value: any) { + return value; + } } @Pipe({name: 'wrappedPipe'}) class WrappedPipe implements PipeTransform { - transform(value: any) { return WrappedValue.wrap(value); } + transform(value: any) { + return WrappedValue.wrap(value); + } } @Pipe({name: 'multiArgPipe'}) @@ -1854,30 +1900,36 @@ class Gh9882 implements AfterContentInit { constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) { } - ngAfterContentInit(): any { this._viewContainer.createEmbeddedView(this._templateRef); } + ngAfterContentInit(): any { + this._viewContainer.createEmbeddedView(this._templateRef); + } } @Directive({selector: '[testDirective]', exportAs: 'testDirective'}) class TestDirective implements OnInit, DoCheck, OnChanges, AfterContentInit, AfterContentChecked, - AfterViewInit, AfterViewChecked, OnDestroy { + AfterViewInit, AfterViewChecked, OnDestroy { @Input() a: any; @Input() b: any; // TODO(issue/24571): remove '!'. - changes !: SimpleChanges; + changes!: SimpleChanges; event: any; eventEmitter: EventEmitter<string> = new EventEmitter<string>(); // TODO(issue/24571): remove '!'. - @Input('testDirective') name !: string; + @Input('testDirective') name!: string; // TODO(issue/24571): remove '!'. - @Input() throwOn !: string; + @Input() throwOn!: string; constructor(public log: DirectiveLog) {} - onEvent(event: any) { this.event = event; } + onEvent(event: any) { + this.event = event; + } - ngDoCheck() { this.log.add(this.name, 'ngDoCheck'); } + ngDoCheck() { + this.log.add(this.name, 'ngDoCheck'); + } ngOnInit() { this.log.add(this.name, 'ngOnInit'); @@ -1935,20 +1987,24 @@ class InjectableWithLifecycle { name = 'injectable'; constructor(public log: DirectiveLog) {} - ngOnDestroy() { this.log.add(this.name, 'ngOnDestroy'); } + ngOnDestroy() { + this.log.add(this.name, 'ngOnDestroy'); + } } @Directive({selector: '[onDestroyDirective]'}) class OnDestroyDirective implements OnDestroy { @Output('destroy') emitter = new EventEmitter<string>(false); - ngOnDestroy() { this.emitter.emit('destroyed'); } + ngOnDestroy() { + this.emitter.emit('destroyed'); + } } @Directive({selector: '[orderCheck0]'}) class OrderCheckDirective0 { // TODO(issue/24571): remove '!'. - private _name !: string; + private _name!: string; @Input('orderCheck0') set name(value: string) { @@ -1962,7 +2018,7 @@ class OrderCheckDirective0 { @Directive({selector: '[orderCheck1]'}) class OrderCheckDirective1 { // TODO(issue/24571): remove '!'. - private _name !: string; + private _name!: string; @Input('orderCheck1') set name(value: string) { @@ -1976,7 +2032,7 @@ class OrderCheckDirective1 { @Directive({selector: '[orderCheck2]'}) class OrderCheckDirective2 { // TODO(issue/24571): remove '!'. - private _name !: string; + private _name!: string; @Input('orderCheck2') set name(value: string) { @@ -2001,21 +2057,25 @@ class TestLocals { @Component({selector: 'root', template: 'empty'}) class Person { // TODO(issue/24571): remove '!'. - age !: number; + age!: number; // TODO(issue/24571): remove '!'. - name !: string; + name!: string; address: Address|null = null; // TODO(issue/24571): remove '!'. - phones !: number[]; + phones!: number[]; init(name: string, address: Address|null = null) { this.name = name; this.address = address; } - sayHi(m: any): string { return `Hi, ${m}`; } + sayHi(m: any): string { + return `Hi, ${m}`; + } - passThrough(val: any): any { return val; } + passThrough(val: any): any { + return val; + } toString(): string { const address = this.address == null ? '' : ' address=' + this.address.toString(); @@ -2040,11 +2100,17 @@ class Address { return this._zipcode; } - set city(v) { this._city = v; } + set city(v) { + this._city = v; + } - set zipcode(v) { this._zipcode = v; } + set zipcode(v) { + this._zipcode = v; + } - toString(): string { return this.city || '-'; } + toString(): string { + return this.city || '-'; + } } @Component({selector: 'root', template: 'empty'}) @@ -2061,14 +2127,16 @@ class TestData { @Component({selector: 'root', template: 'empty'}) class TestDataWithGetter { // TODO(issue/24571): remove '!'. - public fn !: Function; + public fn!: Function; - get a() { return this.fn(); } + get a() { + return this.fn(); + } } class Holder<T> { // TODO(issue/24571): remove '!'. - value !: T; + value!: T; } @Component({selector: 'root', template: 'empty'}) diff --git a/packages/core/test/linker/entry_components_integration_spec.ts b/packages/core/test/linker/entry_components_integration_spec.ts index 7c0adff4aa1a5..e924bd05ab348 100644 --- a/packages/core/test/linker/entry_components_integration_spec.ts +++ b/packages/core/test/linker/entry_components_integration_spec.ts @@ -14,17 +14,25 @@ import {obsoleteInIvy} from '@angular/private/testing'; if (ivyEnabled) { - describe('ivy', () => { declareTests(); }); + describe('ivy', () => { + declareTests(); + }); } else { - describe('jit', () => { declareTests({useJit: true}); }); - describe('no jit', () => { declareTests({useJit: false}); }); + describe('jit', () => { + declareTests({useJit: true}); + }); + describe('no jit', () => { + declareTests({useJit: false}); + }); } class DummyConsole implements Console { public warnings: string[] = []; log(message: string) {} - warn(message: string) { this.warnings.push(message); } + warn(message: string) { + this.warnings.push(message); + } } function declareTests(config?: {useJit: boolean}) { @@ -40,7 +48,7 @@ function declareTests(config?: {useJit: boolean}) { const compFixture = TestBed.createComponent(MainComp); const mainComp: MainComp = compFixture.componentInstance; expect(compFixture.componentRef.injector.get(ComponentFactoryResolver)).toBe(mainComp.cfr); - const cf = mainComp.cfr.resolveComponentFactory(ChildComp) !; + const cf = mainComp.cfr.resolveComponentFactory(ChildComp)!; expect(cf.componentType).toBe(ChildComp); }); @@ -52,8 +60,8 @@ function declareTests(config?: {useJit: boolean}) { const mainComp: CompWithAnalyzeEntryComponentsProvider = compFixture.componentInstance; const cfr: ComponentFactoryResolver = compFixture.componentRef.injector.get(ComponentFactoryResolver); - expect(cfr.resolveComponentFactory(ChildComp) !.componentType).toBe(ChildComp); - expect(cfr.resolveComponentFactory(NestedChildComp) !.componentType).toBe(NestedChildComp); + expect(cfr.resolveComponentFactory(ChildComp)!.componentType).toBe(ChildComp); + expect(cfr.resolveComponentFactory(NestedChildComp)!.componentType).toBe(NestedChildComp); }); it('should be able to get a component form a parent component (view hierarchy)', () => { @@ -63,10 +71,10 @@ function declareTests(config?: {useJit: boolean}) { const childCompEl = compFixture.debugElement.children[0]; const childComp: ChildComp = childCompEl.componentInstance; // declared on ChildComp directly - expect(childComp.cfr.resolveComponentFactory(NestedChildComp) !.componentType) + expect(childComp.cfr.resolveComponentFactory(NestedChildComp)!.componentType) .toBe(NestedChildComp); // inherited from MainComp - expect(childComp.cfr.resolveComponentFactory(ChildComp) !.componentType).toBe(ChildComp); + expect(childComp.cfr.resolveComponentFactory(ChildComp)!.componentType).toBe(ChildComp); }); obsoleteInIvy('In Ivy, the ComponentFactoryResolver can resolve any component factory') @@ -79,12 +87,11 @@ function declareTests(config?: {useJit: boolean}) { const compFixture = TestBed.createComponent(MainComp); const nestedChildCompEl = compFixture.debugElement.children[0].children[0]; const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance; - expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp) !.componentType) + expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp)!.componentType) .toBe(ChildComp); expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp)) .toThrow(noComponentFactoryError(NestedChildComp)); }); - }); } diff --git a/packages/core/test/linker/inheritance_integration_spec.ts b/packages/core/test/linker/inheritance_integration_spec.ts index 24f7129079c61..6da392f4968e8 100644 --- a/packages/core/test/linker/inheritance_integration_spec.ts +++ b/packages/core/test/linker/inheritance_integration_spec.ts @@ -17,8 +17,7 @@ class DirectiveA { @Directive({selector: '[directiveB]'}) class DirectiveB { - @HostBinding('title') - title = 'DirectiveB Title'; + @HostBinding('title') title = 'DirectiveB Title'; } @Component({selector: 'component-a', template: 'ComponentA Template'}) @@ -34,8 +33,7 @@ class ComponentWithNoAnnotation extends ComponentA {} @Directive({selector: '[directiveExtendsComponent]'}) class DirectiveExtendsComponent extends ComponentA { - @HostBinding('title') - title = 'DirectiveExtendsComponent Title'; + @HostBinding('title') title = 'DirectiveExtendsComponent Title'; } class DirectiveWithNoAnnotation extends DirectiveB {} diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index 35228190217b8..679795b62970d 100644 --- a/packages/core/test/linker/integration_spec.ts +++ b/packages/core/test/linker/integration_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule, DOCUMENT, ɵgetDOM as getDOM} from '@angular/common'; -import {Compiler, ComponentFactory, ComponentRef, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, SkipSelf, ViewRef, ɵivyEnabled as ivyEnabled} from '@angular/core'; +import {Compiler, ComponentFactory, ComponentRef, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NgModule, NgModuleRef, NO_ERRORS_SCHEMA, OnDestroy, SkipSelf, ViewRef, ɵivyEnabled as ivyEnabled} from '@angular/core'; import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection'; import {getDebugContext} from '@angular/core/src/errors'; import {ComponentFactoryResolver} from '@angular/core/src/linker/component_factory_resolver'; @@ -17,7 +17,7 @@ import {TemplateRef} from '@angular/core/src/linker/template_ref'; import {ViewContainerRef} from '@angular/core/src/linker/view_container_ref'; import {EmbeddedViewRef} from '@angular/core/src/linker/view_ref'; import {Attribute, Component, ContentChildren, Directive, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata'; -import {TestBed, async, fakeAsync, getTestBed, tick} from '@angular/core/testing'; +import {async, fakeAsync, getTestBed, TestBed, tick} from '@angular/core/testing'; import {createMouseEvent, dispatchEvent, el, isCommentNode} from '@angular/platform-browser/testing/src/browser_util'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing'; @@ -27,16 +27,23 @@ import {stringify} from '../../src/util/stringify'; const ANCHOR_ELEMENT = new InjectionToken('AnchorElement'); if (ivyEnabled) { - describe('ivy', () => { declareTests(); }); + describe('ivy', () => { + declareTests(); + }); } else { - describe('jit', () => { declareTests({useJit: true}); }); - describe('no jit', () => { declareTests({useJit: false}); }); + describe('jit', () => { + declareTests({useJit: true}); + }); + describe('no jit', () => { + declareTests({useJit: false}); + }); } function declareTests(config?: {useJit: boolean}) { describe('integration tests', function() { - - beforeEach(() => { TestBed.configureCompiler({...config}); }); + beforeEach(() => { + TestBed.configureCompiler({...config}); + }); describe('react to record changes', function() { it('should consume text node changes', () => { @@ -55,7 +62,7 @@ function declareTests(config?: {useJit: boolean}) { const template = '<div>{{null}}{{ctxProp}}</div>'; TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - fixture.componentInstance.ctxProp = null !; + fixture.componentInstance.ctxProp = null!; fixture.detectChanges(); expect(fixture.nativeElement).toHaveText(''); @@ -133,7 +140,7 @@ function declareTests(config?: {useJit: boolean}) { fixture.detectChanges(); expect(fixture.debugElement.children[0].nativeElement.getAttribute('foo')).toEqual('bar'); - fixture.componentInstance.ctxProp = null !; + fixture.componentInstance.ctxProp = null!; fixture.detectChanges(); expect(fixture.debugElement.children[0].nativeElement.hasAttribute('foo')).toBeFalsy(); }); @@ -148,7 +155,7 @@ function declareTests(config?: {useJit: boolean}) { fixture.detectChanges(); expect(fixture.debugElement.children[0].nativeElement.style['height']).toEqual('10px'); - fixture.componentInstance.ctxProp = null !; + fixture.componentInstance.ctxProp = null!; fixture.detectChanges(); expect(fixture.debugElement.children[0].nativeElement.style['height']).toEqual(''); }); @@ -265,7 +272,7 @@ function declareTests(config?: {useJit: boolean}) { fixture.componentInstance.ctxProp = 'a'; fixture.detectChanges(); - const dir = fixture.debugElement.children[0].references !['dir']; + const dir = fixture.debugElement.children[0].references!['dir']; expect(dir.dirProp).toEqual('aa'); }); }); @@ -344,7 +351,7 @@ function declareTests(config?: {useJit: boolean}) { it('should display correct error message for uninitialized @Output', () => { @Component({selector: 'my-uninitialized-output', template: '<p>It works!</p>'}) class UninitializedOutputComp { - @Output() customEvent !: EventEmitter<any>; + @Output() customEvent!: EventEmitter<any>; } const template = @@ -406,7 +413,7 @@ function declareTests(config?: {useJit: boolean}) { ngIfEl.childNodes .find( debugElement => debugElement.nativeNode.nodeType === - Node.COMMENT_NODE) !.injector.get(SomeViewport); + Node.COMMENT_NODE)!.injector.get(SomeViewport); expect(someViewport.container.length).toBe(2); expect(ngIfEl.children.length).toBe(2); @@ -455,7 +462,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - expect(fixture.debugElement.children[0].children[0].references !['alice']) + expect(fixture.debugElement.children[0].children[0].references!['alice']) .toBeAnInstanceOf(ChildComp); }); @@ -465,7 +472,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - expect(fixture.debugElement.children[0].children[0].references !['localdir']) + expect(fixture.debugElement.children[0].children[0].references!['localdir']) .toBeAnInstanceOf(ExportDir); }); @@ -477,9 +484,9 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - expect(fixture.debugElement.children[0].references !['x']) + expect(fixture.debugElement.children[0].references!['x']) .toBeAnInstanceOf(DirectiveWithMultipleExportAsNames); - expect(fixture.debugElement.children[0].references !['y']) + expect(fixture.debugElement.children[0].references!['y']) .toBeAnInstanceOf(DirectiveWithMultipleExportAsNames); }); @@ -505,8 +512,8 @@ function declareTests(config?: {useJit: boolean}) { const pEl = fixture.debugElement.children[0]; - const alice = pEl.children[0].references !['alice']; - const bob = pEl.children[1].references !['bob']; + const alice = pEl.children[0].references!['alice']; + const bob = pEl.children[1].references!['bob']; expect(alice).toBeAnInstanceOf(ChildComp); expect(bob).toBeAnInstanceOf(ChildComp); expect(alice).not.toBe(bob); @@ -518,8 +525,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - expect(fixture.debugElement.children[0].references !['alice']) - .toBeAnInstanceOf(ChildComp); + expect(fixture.debugElement.children[0].references!['alice']).toBeAnInstanceOf(ChildComp); }); it('should assign the element instance to a user-defined variable', () => { @@ -528,7 +534,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - const value = fixture.debugElement.children[0].children[0].references !['alice']; + const value = fixture.debugElement.children[0].children[0].references!['alice']; expect(value).not.toBe(null); expect(value.tagName.toLowerCase()).toEqual('div'); }); @@ -540,7 +546,7 @@ function declareTests(config?: {useJit: boolean}) { MyComp, {set: {template: '<ng-template ref-alice></ng-template>'}}) .createComponent(MyComp); - const value = fixture.debugElement.childNodes[0].references !['alice']; + const value = fixture.debugElement.childNodes[0].references!['alice']; expect(value.createEmbeddedView).toBeTruthy(); }); @@ -550,7 +556,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - expect(fixture.debugElement.children[0].children[0].references !['superAlice']) + expect(fixture.debugElement.children[0].children[0].references!['superAlice']) .toBeAnInstanceOf(ChildComp); }); }); @@ -573,14 +579,13 @@ function declareTests(config?: {useJit: boolean}) { }); describe('OnPush components', () => { - it('should use ChangeDetectorRef to manually request a check', () => { TestBed.configureTestingModule({declarations: [MyComp, [[PushCmpWithRef]]]}); const template = '<push-cmp-with-ref #cmp></push-cmp-with-ref>'; TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - const cmp = fixture.debugElement.children[0].references !['cmp']; + const cmp = fixture.debugElement.children[0].references!['cmp']; fixture.detectChanges(); expect(cmp.numberOfChecks).toEqual(1); @@ -601,7 +606,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - const cmp = fixture.debugElement.children[0].references !['cmp']; + const cmp = fixture.debugElement.children[0].references!['cmp']; fixture.componentInstance.ctxProp = 'one'; fixture.detectChanges(); @@ -675,7 +680,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - const cmp = fixture.debugElement.children[0].references !['cmp']; + const cmp = fixture.debugElement.children[0].references!['cmp']; fixture.componentInstance.ctxProp = 'one'; fixture.detectChanges(); @@ -695,7 +700,7 @@ function declareTests(config?: {useJit: boolean}) { tick(); - const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references !['cmp']; + const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references!['cmp']; fixture.detectChanges(); expect(cmp.numberOfChecks).toEqual(1); @@ -726,7 +731,7 @@ function declareTests(config?: {useJit: boolean}) { const fixture = TestBed.createComponent(MyComp); const childComponent = - fixture.debugElement.children[0].children[0].children[0].references !['child']; + fixture.debugElement.children[0].children[0].children[0].references!['child']; expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective); }); @@ -748,7 +753,7 @@ function declareTests(config?: {useJit: boolean}) { const tc = fixture.debugElement.children[0].children[0].children[0]; - const childComponent = tc.references !['child']; + const childComponent = tc.references!['child']; expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective); }); @@ -795,7 +800,7 @@ function declareTests(config?: {useJit: boolean}) { }) .createComponent(MyComp); const tc = fixture.debugElement.childNodes.find( - debugElement => debugElement.nativeNode.nodeType === Node.COMMENT_NODE) !; + debugElement => debugElement.nativeNode.nodeType === Node.COMMENT_NODE)!; const emitter = tc.injector.get(DirectiveEmittingEvent); const myComp = fixture.debugElement.injector.get(MyComp); @@ -827,8 +832,11 @@ function declareTests(config?: {useJit: boolean}) { expect(dir.control).toEqual('one'); - dir.controlChange.subscribe( - {next: () => { expect(fixture.componentInstance.ctxProp).toEqual('two'); }}); + dir.controlChange.subscribe({ + next: () => { + expect(fixture.componentInstance.ctxProp).toEqual('two'); + } + }); dir.triggerChange('two'); })); @@ -954,9 +962,11 @@ function declareTests(config?: {useJit: boolean}) { class DirectiveWithHostListener { id = 'one'; // TODO(issue/24571): remove '!'. - receivedArgs !: any[]; + receivedArgs!: any[]; - doIt(...args: any[]) { this.receivedArgs = args; } + doIt(...args: any[]) { + this.receivedArgs = args; + } } const fixture = @@ -1239,7 +1249,6 @@ function declareTests(config?: {useJit: boolean}) { }).toThrowError('Cannot move a destroyed View in a ViewContainer!'); })); }); - }); it('should support static attributes', () => { @@ -1292,7 +1301,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - const comp = fixture.debugElement.children[0].children[0].references !['consuming']; + const comp = fixture.debugElement.children[0].children[0].references!['consuming']; expect(comp.injectable).toBeAnInstanceOf(InjectableService); }); @@ -1308,7 +1317,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(DirectiveProvidingInjectableInView, {set: {template}}); const fixture = TestBed.createComponent(DirectiveProvidingInjectableInView); - const comp = fixture.debugElement.children[0].references !['consuming']; + const comp = fixture.debugElement.children[0].references!['consuming']; expect(comp.injectable).toBeAnInstanceOf(InjectableService); }); @@ -1336,7 +1345,7 @@ function declareTests(config?: {useJit: boolean}) { }); const fixture = TestBed.createComponent(MyComp); - const comp = fixture.debugElement.children[0].children[0].references !['dir']; + const comp = fixture.debugElement.children[0].children[0].references!['dir']; expect(comp.directive.injectable).toBeAnInstanceOf(InjectableService); }); @@ -1386,7 +1395,7 @@ function declareTests(config?: {useJit: boolean}) { TestBed.overrideComponent(MyComp, {set: {template}}); const fixture = TestBed.createComponent(MyComp); - const providing = fixture.debugElement.children[0].references !['providing']; + const providing = fixture.debugElement.children[0].references!['providing']; expect(providing.created).toBe(false); fixture.componentInstance.ctxBoolProp = true; @@ -1469,7 +1478,7 @@ function declareTests(config?: {useJit: boolean}) { }); it('should use a default element name for components without selectors', () => { - let noSelectorComponentFactory: ComponentFactory<SomeComponent> = undefined !; + let noSelectorComponentFactory: ComponentFactory<SomeComponent> = undefined!; @Component({template: '----'}) class NoSelectorComponent { @@ -1480,7 +1489,7 @@ function declareTests(config?: {useJit: boolean}) { constructor(componentFactoryResolver: ComponentFactoryResolver) { // grab its own component factory noSelectorComponentFactory = - componentFactoryResolver.resolveComponentFactory(NoSelectorComponent) !; + componentFactoryResolver.resolveComponentFactory(NoSelectorComponent)!; } } @@ -1502,8 +1511,9 @@ function declareTests(config?: {useJit: boolean}) { TestBed.configureTestingModule({declarations: [MyComp, SomeDirectiveMissingAnnotation]}); expect(() => TestBed.createComponent(MyComp)) - .toThrowError( - `Unexpected value '${stringify(SomeDirectiveMissingAnnotation)}' declared by the module 'DynamicTestModule'. Please add a @Pipe/@Directive/@Component annotation.`); + .toThrowError(`Unexpected value '${ + stringify( + SomeDirectiveMissingAnnotation)}' declared by the module 'DynamicTestModule'. Please add a @Pipe/@Directive/@Component annotation.`); }); it('should report a meaningful error when a component is missing view annotation', () => { @@ -1822,7 +1832,7 @@ function declareTests(config?: {useJit: boolean}) { expect(fixture.nativeElement.innerHTML).toContain('ng-reflect-dir-prop="hello"'); - fixture.componentInstance.ctxProp = undefined !; + fixture.componentInstance.ctxProp = undefined!; fixture.detectChanges(); expect(fixture.nativeElement.innerHTML).not.toContain('ng-reflect-'); @@ -1839,7 +1849,7 @@ function declareTests(config?: {useJit: boolean}) { expect(fixture.nativeElement.innerHTML).toContain('ng-reflect-dir-prop="hello"'); - fixture.componentInstance.ctxProp = null !; + fixture.componentInstance.ctxProp = null!; fixture.detectChanges(); expect(fixture.nativeElement.innerHTML).not.toContain('ng-reflect-'); @@ -1871,7 +1881,7 @@ function declareTests(config?: {useJit: boolean}) { expect(html).toContain('bindings={'); expect(html).toContain('"ng-reflect-ng-if": "true"'); - fixture.componentInstance.ctxBoolProp = undefined !; + fixture.componentInstance.ctxBoolProp = undefined!; fixture.detectChanges(); html = fixture.nativeElement.innerHTML; @@ -1893,14 +1903,13 @@ function declareTests(config?: {useJit: boolean}) { expect(html).toContain('bindings={'); expect(html).toContain('"ng-reflect-ng-if": "true"'); - fixture.componentInstance.ctxBoolProp = null !; + fixture.componentInstance.ctxBoolProp = null!; fixture.detectChanges(); html = fixture.nativeElement.innerHTML; expect(html).toContain('bindings={'); expect(html).toContain('"ng-reflect-ng-if": null'); }); - }); describe('property decorators', () => { @@ -2079,7 +2088,6 @@ function declareTests(config?: {useJit: boolean}) { }); describe('attributes', () => { - it('should support attributes with namespace', () => { TestBed.configureTestingModule({declarations: [MyComp, SomeCmp]}); const template = '<svg:use xlink:href="#id" />'; @@ -2142,7 +2150,9 @@ class ComponentWithCustomInterpolationB { @Injectable() class MyService { greeting: string; - constructor() { this.greeting = 'hello'; } + constructor() { + this.greeting = 'hello'; + } } @Component({selector: 'simple-imp-cmp', template: ''}) @@ -2165,14 +2175,16 @@ class DynamicViewport { this.injector = Injector.create([{provide: MyService, useValue: myService}], vc.injector); this.componentFactory = - componentFactoryResolver.resolveComponentFactory(ChildCompUsingService) !; + componentFactoryResolver.resolveComponentFactory(ChildCompUsingService)!; } create(): ComponentRef<ChildCompUsingService> { return this.vc.createComponent(this.componentFactory, this.vc.length, this.injector); } - insert(viewRef: ViewRef, index?: number): ViewRef { return this.vc.insert(viewRef, index); } + insert(viewRef: ViewRef, index?: number): ViewRef { + return this.vc.insert(viewRef, index); + } move(viewRef: ViewRef, currentIndex: number): ViewRef { return this.vc.move(viewRef, currentIndex); @@ -2182,25 +2194,29 @@ class DynamicViewport { @Directive({selector: '[my-dir]', inputs: ['dirProp: elprop'], exportAs: 'mydir'}) class MyDir { dirProp: string; - constructor() { this.dirProp = ''; } + constructor() { + this.dirProp = ''; + } } @Directive({selector: '[my-dir2]', inputs: ['dirProp2: elprop'], exportAs: 'mydir2'}) class MyDir2 { dirProp2: string; - constructor() { this.dirProp2 = ''; } + constructor() { + this.dirProp2 = ''; + } } @Directive({selector: '[title]', inputs: ['title']}) class DirectiveWithTitle { // TODO(issue/24571): remove '!'. - title !: string; + title!: string; } @Directive({selector: '[title]', inputs: ['title'], host: {'[title]': 'title'}}) class DirectiveWithTitleAndHostProperty { // TODO(issue/24571): remove '!'. - title !: string; + title!: string; } @Component({selector: 'event-cmp', template: '<div (click)="noop()"></div>'}) @@ -2220,7 +2236,9 @@ class PushCmp { numberOfChecks: number; prop: any; - constructor() { this.numberOfChecks = 0; } + constructor() { + this.numberOfChecks = 0; + } noop() {} @@ -2251,7 +2269,9 @@ class PushCmpWithRef { return 'fixed'; } - propagate() { this.ref.markForCheck(); } + propagate() { + this.ref.markForCheck(); + } } @Component({ @@ -2272,11 +2292,13 @@ class PushCmpWithHostEvent { class PushCmpWithAsyncPipe { numberOfChecks: number = 0; // TODO(issue/24571): remove '!'. - resolve !: (result: any) => void; + resolve!: (result: any) => void; promise: Promise<any>; constructor() { - this.promise = new Promise((resolve) => { this.resolve = resolve; }); + this.promise = new Promise((resolve) => { + this.resolve = resolve; + }); } get field() { @@ -2291,7 +2313,11 @@ class MyComp { ctxNumProp: number; ctxBoolProp: boolean; ctxArrProp: number[]; - toStringThrow = {toString: function() { throw 'boom'; }}; + toStringThrow = { + toString: function() { + throw 'boom'; + } + }; constructor() { this.ctxProp = 'initial value'; @@ -2300,7 +2326,9 @@ class MyComp { this.ctxArrProp = [0, 1, 2]; } - throwError() { throw 'boom'; } + throwError() { + throw 'boom'; + } } @Component({ @@ -2326,7 +2354,9 @@ class ChildCompNoTemplate { @Component({selector: 'child-cmp-svc', template: '{{ctxProp}}'}) class ChildCompUsingService { ctxProp: string; - constructor(service: MyService) { this.ctxProp = service.greeting; } + constructor(service: MyService) { + this.ctxProp = service.greeting; + } } @Directive({selector: 'some-directive'}) @@ -2341,7 +2371,9 @@ class SomeDirectiveMissingAnnotation {} }) class CompWithHost { myHost: SomeDirective; - constructor(@Host() someComp: SomeDirective) { this.myHost = someComp; } + constructor(@Host() someComp: SomeDirective) { + this.myHost = someComp; + } } @Component({selector: '[child-cmp2]', viewProviders: [MyService]}) @@ -2384,7 +2416,9 @@ class NoContext { @Pipe({name: 'double'}) class DoublePipe implements PipeTransform, OnDestroy { ngOnDestroy() {} - transform(value: any) { return `${value}${value}`; } + transform(value: any) { + return `${value}${value}`; + } } @Directive({selector: '[emitter]', outputs: ['event']}) @@ -2397,7 +2431,9 @@ class DirectiveEmittingEvent { this.event = new EventEmitter(); } - fireEvent(msg: string) { this.event.emit(msg); } + fireEvent(msg: string) { + this.event.emit(msg); + } } @Directive({selector: '[update-host-attributes]', host: {'role': 'button'}}) @@ -2408,16 +2444,22 @@ class DirectiveUpdatingHostAttributes { class DirectiveUpdatingHostProperties { id: string; - constructor() { this.id = 'one'; } + constructor() { + this.id = 'one'; + } } @Directive({selector: '[listener]', host: {'(event)': 'onEvent($event)'}}) class DirectiveListeningEvent { msg: string; - constructor() { this.msg = ''; } + constructor() { + this.msg = ''; + } - onEvent(msg: string) { this.msg = msg; } + onEvent(msg: string) { + this.msg = msg; + } } @Directive({ @@ -2431,17 +2473,27 @@ class DirectiveListeningEvent { }) class DirectiveListeningDomEvent { eventTypes: string[] = []; - onEvent(eventType: string) { this.eventTypes.push(eventType); } - onWindowEvent(eventType: string) { this.eventTypes.push('window_' + eventType); } - onDocumentEvent(eventType: string) { this.eventTypes.push('document_' + eventType); } - onBodyEvent(eventType: string) { this.eventTypes.push('body_' + eventType); } + onEvent(eventType: string) { + this.eventTypes.push(eventType); + } + onWindowEvent(eventType: string) { + this.eventTypes.push('window_' + eventType); + } + onDocumentEvent(eventType: string) { + this.eventTypes.push('document_' + eventType); + } + onBodyEvent(eventType: string) { + this.eventTypes.push('body_' + eventType); + } } let globalCounter = 0; @Directive({selector: '[listenerother]', host: {'(window:domEvent)': 'onEvent($event.type)'}}) class DirectiveListeningDomEventOther { eventType: string; - constructor() { this.eventType = ''; } + constructor() { + this.eventType = ''; + } onEvent(eventType: string) { globalCounter++; this.eventType = 'other_' + eventType; @@ -2450,18 +2502,22 @@ class DirectiveListeningDomEventOther { @Directive({selector: '[listenerprevent]', host: {'(click)': 'onEvent($event)'}}) class DirectiveListeningDomEventPrevent { - onEvent(event: any) { return false; } + onEvent(event: any) { + return false; + } } @Directive({selector: '[listenernoprevent]', host: {'(click)': 'onEvent($event)'}}) class DirectiveListeningDomEventNoPrevent { - onEvent(event: any) { return true; } + onEvent(event: any) { + return true; + } } @Directive({selector: '[id]', inputs: ['id']}) class IdDir { // TODO(issue/24571): remove '!'. - id !: string; + id!: string; } @Directive({selector: '[customEvent]'}) @@ -2497,7 +2553,9 @@ class PrivateImpl extends PublicApi { @Directive({selector: '[needs-public-api]'}) class NeedsPublicApi { - constructor(@Host() api: PublicApi) { expect(api instanceof PrivateImpl).toBe(true); } + constructor(@Host() api: PublicApi) { + expect(api instanceof PrivateImpl).toBe(true); + } } class ToolbarContext { @@ -2507,7 +2565,9 @@ class ToolbarContext { @Directive({selector: '[toolbarpart]'}) class ToolbarPart { templateRef: TemplateRef<ToolbarContext>; - constructor(templateRef: TemplateRef<ToolbarContext>) { this.templateRef = templateRef; } + constructor(templateRef: TemplateRef<ToolbarContext>) { + this.templateRef = templateRef; + } } @Directive({selector: '[toolbarVc]', inputs: ['toolbarVc']}) @@ -2525,7 +2585,7 @@ class ToolbarViewContainer { }) class ToolbarComponent { // TODO(issue/24571): remove '!'. - @ContentChildren(ToolbarPart) query !: QueryList<ToolbarPart>; + @ContentChildren(ToolbarPart) query!: QueryList<ToolbarPart>; ctxProp: string = 'hello world'; constructor() {} @@ -2536,7 +2596,9 @@ class DirectiveWithTwoWayBinding { controlChange = new EventEmitter(); control: any = null; - triggerChange(value: any) { this.controlChange.emit(value); } + triggerChange(value: any) { + this.controlChange.emit(value); + } } @Injectable() @@ -2585,7 +2647,9 @@ class DirectiveProvidingInjectableInHostAndView { class DirectiveConsumingInjectable { injectable: any; - constructor(@Host() @Inject(InjectableService) injectable: any) { this.injectable = injectable; } + constructor(@Host() @Inject(InjectableService) injectable: any) { + this.injectable = injectable; + } } @@ -2620,12 +2684,14 @@ class EventBus { @Directive({ selector: 'grand-parent-providing-event-bus', - providers: [{provide: EventBus, useValue: new EventBus(null !, 'grandparent')}] + providers: [{provide: EventBus, useValue: new EventBus(null!, 'grandparent')}] }) class GrandParentProvidingEventBus { bus: EventBus; - constructor(bus: EventBus) { this.bus = bus; } + constructor(bus: EventBus) { + this.bus = bus; + } } function createParentBus(peb: EventBus) { @@ -2651,7 +2717,9 @@ class ParentProvidingEventBus { class ChildConsumingEventBus { bus: EventBus; - constructor(@SkipSelf() bus: EventBus) { this.bus = bus; } + constructor(@SkipSelf() bus: EventBus) { + this.bus = bus; + } } @Directive({selector: '[someImpvp]', inputs: ['someImpvp']}) @@ -2695,17 +2763,23 @@ class ComponentWithoutView { @Directive({selector: '[no-duplicate]'}) class DuplicateDir { - constructor(elRef: ElementRef) { elRef.nativeElement.textContent += 'noduplicate'; } + constructor(elRef: ElementRef) { + elRef.nativeElement.textContent += 'noduplicate'; + } } @Directive({selector: '[no-duplicate]'}) class OtherDuplicateDir { - constructor(elRef: ElementRef) { elRef.nativeElement.textContent += 'othernoduplicate'; } + constructor(elRef: ElementRef) { + elRef.nativeElement.textContent += 'othernoduplicate'; + } } @Directive({selector: 'directive-throwing-error'}) class DirectiveThrowingAnError { - constructor() { throw new Error('BOOM'); } + constructor() { + throw new Error('BOOM'); + } } @Component({ @@ -2721,15 +2795,19 @@ class DirectiveWithPropDecorators { target: any; // TODO(issue/24571): remove '!'. - @Input('elProp') dirProp !: string; + @Input('elProp') dirProp!: string; @Output('elEvent') event = new EventEmitter(); // TODO(issue/24571): remove '!'. - @HostBinding('attr.my-attr') myAttr !: string; + @HostBinding('attr.my-attr') myAttr!: string; @HostListener('click', ['$event.target']) - onClick(target: any) { this.target = target; } + onClick(target: any) { + this.target = target; + } - fireEvent(msg: any) { this.event.emit(msg); } + fireEvent(msg: any) { + this.event.emit(msg); + } } @Component({selector: 'some-cmp'}) diff --git a/packages/core/test/linker/jit_summaries_integration_spec.ts b/packages/core/test/linker/jit_summaries_integration_spec.ts index 20dfd201b0198..3c7b28aad9674 100644 --- a/packages/core/test/linker/jit_summaries_integration_spec.ts +++ b/packages/core/test/linker/jit_summaries_integration_spec.ts @@ -10,7 +10,7 @@ import {ResourceLoader} from '@angular/compiler'; import {CompileMetadataResolver} from '@angular/compiler/src/metadata_resolver'; import {MockResourceLoader} from '@angular/compiler/testing/src/resource_loader_mock'; import {Component, Directive, Injectable, NgModule, OnDestroy, Pipe} from '@angular/core'; -import {TestBed, async, getTestBed} from '@angular/core/testing'; +import {async, getTestBed, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {obsoleteInIvy} from '@angular/private/testing'; @@ -32,7 +32,7 @@ import {obsoleteInIvy} from '@angular/private/testing'; } function expectInstanceCreated(type: any) { - const instance = instances.get(type) !; + const instance = instances.get(type)!; expect(instance).toBeDefined(); expect(instance.dep instanceof SomeDep).toBe(true); } @@ -46,7 +46,9 @@ import {obsoleteInIvy} from '@angular/private/testing'; class SomeDirective extends Base {} class SomePipe extends Base { - transform(value: any) { return value; } + transform(value: any) { + return value; + } } class SomeService extends Base {} @@ -138,7 +140,9 @@ import {obsoleteInIvy} from '@angular/private/testing'; createSummaries().then(s => summaries = s); })); - afterEach(() => { resetTestEnvironmentWithSummaries(); }); + afterEach(() => { + resetTestEnvironmentWithSummaries(); + }); it('should use directive metadata from summaries', () => { resetTestEnvironmentWithSummaries(summaries); diff --git a/packages/core/test/linker/ng_container_integration_spec.ts b/packages/core/test/linker/ng_container_integration_spec.ts index 6f7fca54a268c..d0f737d6e9fa6 100644 --- a/packages/core/test/linker/ng_container_integration_spec.ts +++ b/packages/core/test/linker/ng_container_integration_spec.ts @@ -15,15 +15,20 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; import {modifiedInIvy} from '@angular/private/testing'; if (ivyEnabled) { - describe('ivy', () => { declareTests(); }); + describe('ivy', () => { + declareTests(); + }); } else { - describe('jit', () => { declareTests({useJit: true}); }); - describe('no jit', () => { declareTests({useJit: false}); }); + describe('jit', () => { + declareTests({useJit: true}); + }); + describe('no jit', () => { + declareTests({useJit: false}); + }); } function declareTests(config?: {useJit: boolean}) { describe('<ng-container>', function() { - beforeEach(() => { TestBed.configureCompiler({...config}); TestBed.configureTestingModule({ @@ -140,7 +145,7 @@ function declareTests(config?: {useJit: boolean}) { const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); - const q = fixture.debugElement.children[0].references !['q']; + const q = fixture.debugElement.children[0].references!['q']; fixture.detectChanges(); expect(q.textDirChildren.length).toEqual(1); @@ -153,7 +158,7 @@ function declareTests(config?: {useJit: boolean}) { const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); - const q = fixture.debugElement.children[0].references !['q']; + const q = fixture.debugElement.children[0].references!['q']; fixture.detectChanges(); expect(q.textDirChildren.length).toEqual(1); @@ -170,21 +175,25 @@ class TextDirective { @Component({selector: 'needs-content-children', template: ''}) class NeedsContentChildren implements AfterContentInit { // TODO(issue/24571): remove '!'. - @ContentChildren(TextDirective) textDirChildren !: QueryList<TextDirective>; + @ContentChildren(TextDirective) textDirChildren!: QueryList<TextDirective>; // TODO(issue/24571): remove '!'. - numberOfChildrenAfterContentInit !: number; + numberOfChildrenAfterContentInit!: number; - ngAfterContentInit() { this.numberOfChildrenAfterContentInit = this.textDirChildren.length; } + ngAfterContentInit() { + this.numberOfChildrenAfterContentInit = this.textDirChildren.length; + } } @Component({selector: 'needs-view-children', template: '<div text></div>'}) class NeedsViewChildren implements AfterViewInit { // TODO(issue/24571): remove '!'. - @ViewChildren(TextDirective) textDirChildren !: QueryList<TextDirective>; + @ViewChildren(TextDirective) textDirChildren!: QueryList<TextDirective>; // TODO(issue/24571): remove '!'. - numberOfChildrenAfterViewInit !: number; + numberOfChildrenAfterViewInit!: number; - ngAfterViewInit() { this.numberOfChildrenAfterViewInit = this.textDirChildren.length; } + ngAfterViewInit() { + this.numberOfChildrenAfterViewInit = this.textDirChildren.length; + } } @Component({selector: 'simple', template: 'SIMPLE(<ng-content></ng-content>)'}) diff --git a/packages/core/test/linker/ng_module_integration_spec.ts b/packages/core/test/linker/ng_module_integration_spec.ts index 152f42e68c1df..e6f2bf694b4aa 100644 --- a/packages/core/test/linker/ng_module_integration_spec.ts +++ b/packages/core/test/linker/ng_module_integration_spec.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, forwardRef, getModuleFactory, ɵivyEnabled as ivyEnabled, ɵɵdefineNgModule as defineNgModule} from '@angular/core'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectorRef, Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, forwardRef, getModuleFactory, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, ɵivyEnabled as ivyEnabled, ɵɵdefineNgModule as defineNgModule} from '@angular/core'; import {Console} from '@angular/core/src/console'; -import {ɵɵInjectableDef, ɵɵdefineInjectable} from '@angular/core/src/di/interface/defs'; +import {ɵɵdefineInjectable, ɵɵInjectableDef} from '@angular/core/src/di/interface/defs'; import {getNgModuleDef} from '@angular/core/src/render3/definition'; import {NgModuleData} from '@angular/core/src/view/types'; import {tokenKey} from '@angular/core/src/view/util'; -import {ComponentFixture, TestBed, inject} from '@angular/core/testing'; +import {ComponentFixture, inject, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing'; @@ -23,7 +23,9 @@ import {stringify} from '../../src/util/stringify'; class Engine {} class BrokenEngine { - constructor() { throw new Error('Broken Engine'); } + constructor() { + throw new Error('Broken Engine'); + } } class DashboardSoftware {} @@ -53,7 +55,9 @@ class CarWithDashboard { @Injectable() class SportsCar extends Car { - constructor(engine: Engine) { super(engine); } + constructor(engine: Engine) { + super(engine); + } } @Injectable() @@ -79,13 +83,14 @@ class SomeComp { @Directive({selector: '[someDir]'}) class SomeDirective { // TODO(issue/24571): remove '!'. - @HostBinding('title') @Input() - someDir !: string; + @HostBinding('title') @Input() someDir!: string; } @Pipe({name: 'somePipe'}) class SomePipe { - transform(value: string): any { return `transformed ${value}`; } + transform(value: string): any { + return `transformed ${value}`; + } } @Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`}) @@ -94,10 +99,16 @@ class CompUsingModuleDirectiveAndPipe { { if (ivyEnabled) { - describe('ivy', () => { declareTests(); }); + describe('ivy', () => { + declareTests(); + }); } else { - describe('jit', () => { declareTests({useJit: true}); }); - describe('no jit', () => { declareTests({useJit: false}); }); + describe('jit', () => { + declareTests({useJit: true}); + }); + describe('no jit', () => { + declareTests({useJit: false}); + }); } } @@ -106,7 +117,9 @@ function declareTests(config?: {useJit: boolean}) { let compiler: Compiler; let injector: Injector; - beforeEach(() => { TestBed.configureCompiler(config || {}); }); + beforeEach(() => { + TestBed.configureCompiler(config || {}); + }); beforeEach(inject([Compiler, Injector], (_compiler: Compiler, _injector: Injector) => { compiler = _compiler; @@ -117,8 +130,7 @@ function declareTests(config?: {useJit: boolean}) { return compiler.compileModuleSync(moduleType); } - function createModule<T>( - moduleType: Type<T>, parentInjector?: Injector | null): NgModuleRef<T> { + function createModule<T>(moduleType: Type<T>, parentInjector?: Injector|null): NgModuleRef<T> { // Read the `ngModuleDef` to cause it to be compiled and any errors thrown. getNgModuleDef(moduleType); return createModuleFactory(moduleType).create(parentInjector || null); @@ -136,11 +148,11 @@ function declareTests(config?: {useJit: boolean}) { const ngModule = createModule(moduleType, injector); - const cf = ngModule.componentFactoryResolver.resolveComponentFactory(compType) !; + const cf = ngModule.componentFactoryResolver.resolveComponentFactory(compType)!; const comp = cf.create(Injector.NULL); - return new ComponentFixture(comp, null !, false); + return new ComponentFixture(comp, null!, false); } describe('errors', () => { @@ -150,8 +162,8 @@ function declareTests(config?: {useJit: boolean}) { } expect(() => createModule(SomeModule)) - .toThrowError( - `Can't export directive ${stringify(SomeDirective)} from ${stringify(SomeModule)} as it was neither declared nor imported!`); + .toThrowError(`Can't export directive ${stringify(SomeDirective)} from ${ + stringify(SomeModule)} as it was neither declared nor imported!`); }); it('should error when exporting a pipe that was neither declared nor imported', () => { @@ -160,8 +172,8 @@ function declareTests(config?: {useJit: boolean}) { } expect(() => createModule(SomeModule)) - .toThrowError( - `Can't export pipe ${stringify(SomePipe)} from ${stringify(SomeModule)} as it was neither declared nor imported!`); + .toThrowError(`Can't export pipe ${stringify(SomePipe)} from ${ + stringify(SomeModule)} as it was neither declared nor imported!`); }); it('should error if a directive is declared in more than 1 module', () => { @@ -177,9 +189,14 @@ function declareTests(config?: {useJit: boolean}) { expect(() => createModule(Module2)) .toThrowError( - `Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` + - `Please consider moving ${stringify(SomeDirective)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` + - `You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`); + `Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${ + stringify(Module1)} and ${stringify(Module2)}! ` + + `Please consider moving ${ + stringify(SomeDirective)} to a higher module that imports ${ + stringify(Module1)} and ${stringify(Module2)}. ` + + `You can also create a new NgModule that exports and includes ${ + stringify(SomeDirective)} then import that NgModule in ${ + stringify(Module1)} and ${stringify(Module2)}.`); }); it('should error if a directive is declared in more than 1 module also if the module declaring it is imported', @@ -194,9 +211,14 @@ function declareTests(config?: {useJit: boolean}) { expect(() => createModule(Module2)) .toThrowError( - `Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` + - `Please consider moving ${stringify(SomeDirective)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` + - `You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`); + `Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${ + stringify(Module1)} and ${stringify(Module2)}! ` + + `Please consider moving ${ + stringify(SomeDirective)} to a higher module that imports ${ + stringify(Module1)} and ${stringify(Module2)}. ` + + `You can also create a new NgModule that exports and includes ${ + stringify(SomeDirective)} then import that NgModule in ${ + stringify(Module1)} and ${stringify(Module2)}.`); }); it('should error if a pipe is declared in more than 1 module', () => { @@ -212,9 +234,13 @@ function declareTests(config?: {useJit: boolean}) { expect(() => createModule(Module2)) .toThrowError( - `Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` + - `Please consider moving ${stringify(SomePipe)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` + - `You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`); + `Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${ + stringify(Module1)} and ${stringify(Module2)}! ` + + `Please consider moving ${stringify(SomePipe)} to a higher module that imports ${ + stringify(Module1)} and ${stringify(Module2)}. ` + + `You can also create a new NgModule that exports and includes ${ + stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${ + stringify(Module2)}.`); }); it('should error if a pipe is declared in more than 1 module also if the module declaring it is imported', @@ -229,11 +255,14 @@ function declareTests(config?: {useJit: boolean}) { expect(() => createModule(Module2)) .toThrowError( - `Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` + - `Please consider moving ${stringify(SomePipe)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` + - `You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`); + `Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${ + stringify(Module1)} and ${stringify(Module2)}! ` + + `Please consider moving ${stringify(SomePipe)} to a higher module that imports ${ + stringify(Module1)} and ${stringify(Module2)}. ` + + `You can also create a new NgModule that exports and includes ${ + stringify(SomePipe)} then import that NgModule in ${ + stringify(Module1)} and ${stringify(Module2)}.`); }); - }); describe('schemas', () => { @@ -361,7 +390,7 @@ function declareTests(config?: {useJit: boolean}) { } const ngModule = createModule(SomeModule); - expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType) .toBe(SomeComp); expect(ngModule.injector.get(ComponentFactoryResolver) .resolveComponentFactory(SomeComp) @@ -411,7 +440,7 @@ function declareTests(config?: {useJit: boolean}) { } const ngModule = createModule(SomeModule); - expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType) .toBe(SomeComp); expect(ngModule.injector.get(ComponentFactoryResolver) .resolveComponentFactory(SomeComp) @@ -429,7 +458,7 @@ function declareTests(config?: {useJit: boolean}) { } const ngModule = createModule(SomeModule); - expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType) .toBe(SomeComp); expect(ngModule.injector.get(ComponentFactoryResolver) .resolveComponentFactory(SomeComp) @@ -447,14 +476,13 @@ function declareTests(config?: {useJit: boolean}) { } const ngModule = createModule(SomeModule); - expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType) .toBe(SomeComp); expect(ngModule.injector.get(ComponentFactoryResolver) .resolveComponentFactory(SomeComp) .componentType) .toBe(SomeComp); }); - }); describe('bootstrap components', () => { @@ -464,7 +492,7 @@ function declareTests(config?: {useJit: boolean}) { } const ngModule = createModule(SomeModule); - expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType) .toBe(SomeComp); }); @@ -477,7 +505,6 @@ function declareTests(config?: {useJit: boolean}) { expect(ngModule._bootstrapComponents.length).toBe(1); expect(ngModule._bootstrapComponents[0]).toBe(SomeComp); }); - }); describe('directives and pipes', () => { @@ -542,7 +569,6 @@ function declareTests(config?: {useJit: boolean}) { }); describe('import/export', () => { - it('should support exported directives and pipes', () => { @NgModule({declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]}) class SomeImportedModule { @@ -677,7 +703,7 @@ function declareTests(config?: {useJit: boolean}) { let moduleType: any = null; - function createInjector(providers: Provider[], parent?: Injector | null): Injector { + function createInjector(providers: Provider[], parent?: Injector|null): Injector { @NgModule({providers: providers}) class SomeModule { } @@ -687,8 +713,9 @@ function declareTests(config?: {useJit: boolean}) { return createModule(SomeModule, parent).injector; } - it('should provide the module', - () => { expect(createInjector([]).get(moduleType)).toBeAnInstanceOf(moduleType); }); + it('should provide the module', () => { + expect(createInjector([]).get(moduleType)).toBeAnInstanceOf(moduleType); + }); it('should instantiate a class without dependencies', () => { const injector = createInjector([Engine]); @@ -741,7 +768,9 @@ function declareTests(config?: {useJit: boolean}) { }); it('should provide to a factory', () => { - function sportsCarFactory(e: Engine) { return new SportsCar(e); } + function sportsCarFactory(e: Engine) { + return new SportsCar(e); + } const injector = createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]); @@ -759,8 +788,7 @@ function declareTests(config?: {useJit: boolean}) { it('should provide to an alias', () => { const injector = createInjector([ - Engine, {provide: SportsCar, useClass: SportsCar}, - {provide: Car, useExisting: SportsCar} + Engine, {provide: SportsCar, useClass: SportsCar}, {provide: Car, useExisting: SportsCar} ]); const car = injector.get(Car); @@ -794,7 +822,7 @@ function declareTests(config?: {useJit: boolean}) { const injector = createInjector([{provide: 'car', useExisting: SportsCar}]); let errorMsg = `NullInjectorError: No provider for ${stringify(SportsCar)}!`; if (ivyEnabled) { - errorMsg = `R3InjectorError(SomeModule)[car -> SportsCar]: \n ` + errorMsg; + errorMsg = `R3InjectorError(SomeModule)[car -> ${stringify(SportsCar)}]: \n ` + errorMsg; } expect(() => injector.get('car')).toThrowError(errorMsg); }); @@ -879,7 +907,6 @@ function declareTests(config?: {useJit: boolean}) { }); describe('injecting lazy providers into an eager provider via Injector.get', () => { - it('should inject providers that were declared before it', () => { @NgModule({ providers: [ @@ -920,7 +947,6 @@ function declareTests(config?: {useJit: boolean}) { }); describe('injecting eager providers into an eager provider via Injector.get', () => { - it('should inject providers that were declared before it', () => { @NgModule({ providers: [ @@ -1039,7 +1065,6 @@ function declareTests(config?: {useJit: boolean}) { expect(engineFromParent).not.toBe(engineFromChild); expect(engineFromChild).toBeAnInstanceOf(TurboEngine); }); - }); describe('depedency resolution', () => { @@ -1075,7 +1100,9 @@ function declareTests(config?: {useJit: boolean}) { @NgModule() class ImportedModule { - constructor() { created = true; } + constructor() { + created = true; + } } @NgModule({imports: [ImportedModule]}) @@ -1105,7 +1132,9 @@ function declareTests(config?: {useJit: boolean}) { let destroyed = false; class SomeInjectable { - ngOnDestroy() { destroyed = true; } + ngOnDestroy() { + destroyed = true; + } } @NgModule({providers: [SomeInjectable]}) @@ -1125,8 +1154,12 @@ function declareTests(config?: {useJit: boolean}) { let destroyed = false; class SomeInjectable { - constructor() { created = true; } - ngOnDestroy() { destroyed = true; } + constructor() { + created = true; + } + ngOnDestroy() { + destroyed = true; + } } @NgModule({providers: [SomeInjectable]}) @@ -1174,9 +1207,8 @@ function declareTests(config?: {useJit: boolean}) { } @NgModule({ - imports: [ - {ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]} - ] + imports: + [{ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]}] }) class SomeModule { } @@ -1210,9 +1242,8 @@ function declareTests(config?: {useJit: boolean}) { @NgModule({ providers: [{provide: 'token1', useValue: 'direct'}], - imports: [ - {ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]} - ] + imports: + [{ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]}] }) class SomeModule { } diff --git a/packages/core/test/linker/projection_integration_spec.ts b/packages/core/test/linker/projection_integration_spec.ts index 96590a54369d9..814739e9b6377 100644 --- a/packages/core/test/linker/projection_integration_spec.ts +++ b/packages/core/test/linker/projection_integration_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule, ɵgetDOM as getDOM} from '@angular/common'; -import {Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, Injector, Input, NO_ERRORS_SCHEMA, NgModule, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation} from '@angular/core'; +import {Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, Injector, Input, NgModule, NO_ERRORS_SCHEMA, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -50,9 +50,7 @@ describe('projection', () => { it('should support projecting text interpolation to a non bound element with other bound elements after it', () => { TestBed.overrideComponent(Simple, { - set: { - template: 'SIMPLE(<div><ng-content></ng-content></div><div [tabIndex]="0">EL</div>)' - } + set: {template: 'SIMPLE(<div><ng-content></ng-content></div><div [tabIndex]="0">EL</div>)'} }); TestBed.overrideComponent(MainComp, {set: {template: '<simple>{{text}}</simple>'}}); const main = TestBed.createComponent(MainComp); @@ -244,8 +242,7 @@ describe('projection', () => { it('should support nesting with content being direct child of a nested component', () => { TestBed.configureTestingModule({ - declarations: - [InnerComponent, InnerInnerComponent, OuterComponent, ManualViewportDirective] + declarations: [InnerComponent, InnerInnerComponent, OuterComponent, ManualViewportDirective] }); TestBed.overrideComponent(MainComp, { set: { @@ -304,7 +301,7 @@ describe('projection', () => { `<ng-content></ng-content>(<ng-template [ngIf]="showing"><ng-content select="div"></ng-content></ng-template>)` }) class Child { - @Input() showing !: boolean; + @Input() showing!: boolean; } @Component({ @@ -361,11 +358,13 @@ describe('projection', () => { }); it('should support moving non projected light dom around', () => { - let sourceDirective: ManualViewportDirective = undefined !; + let sourceDirective: ManualViewportDirective = undefined!; @Directive({selector: '[manual]'}) class ManualViewportDirective { - constructor(public templateRef: TemplateRef<Object>) { sourceDirective = this; } + constructor(public templateRef: TemplateRef<Object>) { + sourceDirective = this; + } } TestBed.configureTestingModule( @@ -594,7 +593,6 @@ describe('projection', () => { it('should project nodes into nested templates when the main template doesn\'t have <ng-content>', () => { - @Component({ selector: 'content-in-template', template: @@ -622,7 +620,6 @@ describe('projection', () => { }); it('should project nodes into nested templates and the main template', () => { - @Component({ selector: 'content-in-main-and-template', template: @@ -720,7 +717,6 @@ describe('projection', () => { }); describe('projectable nodes', () => { - @Component({selector: 'test', template: ''}) class TestComponent { constructor(public cfr: ComponentFactoryResolver) {} @@ -739,14 +735,20 @@ describe('projection', () => { class InsertTplRef implements OnInit { constructor(private _vcRef: ViewContainerRef, private _tplRef: TemplateRef<{}>) {} - ngOnInit() { this._vcRef.createEmbeddedView(this._tplRef); } + ngOnInit() { + this._vcRef.createEmbeddedView(this._tplRef); + } } @Directive({selector: '[delayedInsert]', exportAs: 'delayedInsert'}) class DelayedInsertTplRef { constructor(public vc: ViewContainerRef, public templateRef: TemplateRef<Object>) {} - show() { this.vc.createEmbeddedView(this.templateRef); } - hide() { this.vc.clear(); } + show() { + this.vc.createEmbeddedView(this.templateRef); + } + hide() { + this.vc.clear(); + } } @NgModule({ @@ -893,15 +895,23 @@ class SingleContentTagComponent { @Directive({selector: '[manual]'}) class ManualViewportDirective { constructor(public vc: ViewContainerRef, public templateRef: TemplateRef<Object>) {} - show() { this.vc.createEmbeddedView(this.templateRef); } - hide() { this.vc.clear(); } + show() { + this.vc.createEmbeddedView(this.templateRef); + } + hide() { + this.vc.clear(); + } } @Directive({selector: '[project]'}) class ProjectDirective { constructor(public vc: ViewContainerRef) {} - show(templateRef: TemplateRef<Object>) { this.vc.createEmbeddedView(templateRef); } - hide() { this.vc.clear(); } + show(templateRef: TemplateRef<Object>) { + this.vc.createEmbeddedView(templateRef); + } + hide() { + this.vc.clear(); + } } @Component({ @@ -1034,5 +1044,5 @@ class CmpA2 { } function supportsNativeShadowDOM(): boolean { - return typeof(<any>document.body).createShadowRoot === 'function'; + return typeof (<any>document.body).createShadowRoot === 'function'; } diff --git a/packages/core/test/linker/query_integration_spec.ts b/packages/core/test/linker/query_integration_spec.ts index 4339e63e0683c..bbcfad6ed0240 100644 --- a/packages/core/test/linker/query_integration_spec.ts +++ b/packages/core/test/linker/query_integration_spec.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, asNativeElements} from '@angular/core'; +import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, asNativeElements, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core'; import {ElementRef} from '@angular/core/src/core'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, modifiedInIvy, onlyInIvy} from '@angular/private/testing'; import {Subject} from 'rxjs'; @@ -16,7 +16,6 @@ import {Subject} from 'rxjs'; import {stringify} from '../../src/util/stringify'; describe('Query API', () => { - beforeEach(() => TestBed.configureTestingModule({ declarations: [ MyComp0, @@ -76,7 +75,7 @@ describe('Query API', () => { const template = '<needs-content-children #q><div text="foo"></div></needs-content-children>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; view.detectChanges(); expect(q.textDirChildren.length).toEqual(1); expect(q.numberOfChildrenAfterContentInit).toEqual(1); @@ -88,7 +87,7 @@ describe('Query API', () => { const view = createTestCmp(MyComp0, template); view.componentInstance.shouldShow = true; view.detectChanges(); - const q: NeedsContentChild = view.debugElement.children[0].references !['q']; + const q: NeedsContentChild = view.debugElement.children[0].references!['q']; expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); view.componentInstance.shouldShow = false; @@ -114,7 +113,7 @@ describe('Query API', () => { `; const view = createTestCmp(MyComp0, template); view.detectChanges(); - const q: NeedsContentChild = view.debugElement.children[1].references !['q']; + const q: NeedsContentChild = view.debugElement.children[1].references!['q']; expect(q.child.text).toEqual('foo'); const directive: DirectiveNeedsContentChild = view.debugElement.children[0].injector.get(DirectiveNeedsContentChild); @@ -125,7 +124,7 @@ describe('Query API', () => { const template = '<needs-view-child #q></needs-view-child>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewChild = view.debugElement.children[0].references !['q']; + const q: NeedsViewChild = view.debugElement.children[0].references!['q']; expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); q.shouldShow = false; @@ -140,7 +139,7 @@ describe('Query API', () => { const template = '<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>'; const view = createTestCmp(MyComp0, template); - const q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references !['q']; + const q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references!['q']; expect(q.contentChild.text).toBeFalsy(); expect(q.viewChild.text).toBeFalsy(); @@ -161,7 +160,7 @@ describe('Query API', () => { const view = TestBed.createComponent(MyComp0); view.detectChanges(); - const q: NeedsViewChild = view.debugElement.children[0].references !['q']; + const q: NeedsViewChild = view.debugElement.children[0].references!['q']; expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); q.shouldShow = false; @@ -252,8 +251,8 @@ describe('Query API', () => { const template = '<has-null-query-condition></has-null-query-condition>'; TestBed.overrideComponent(MyCompBroken0, {set: {template}}); expect(() => TestBed.createComponent(MyCompBroken0)) - .toThrowError( - `Can't construct a query for the property "errorTrigger" of "${stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`); + .toThrowError(`Can't construct a query for the property "errorTrigger" of "${ + stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`); }); }); @@ -422,7 +421,7 @@ describe('Query API', () => { '</needs-query>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; q.query.changes.subscribe({ next: () => { @@ -444,11 +443,13 @@ describe('Query API', () => { let isQueryListCompleted = false; - const q: NeedsQuery = view.debugElement.children[0].references !['q']; + const q: NeedsQuery = view.debugElement.children[0].references!['q']; const changes = <Subject<any>>q.query.changes; expect(q.query.length).toEqual(1); expect(changes.closed).toBeFalsy(); - changes.subscribe(() => {}, () => {}, () => { isQueryListCompleted = true; }); + changes.subscribe(() => {}, () => {}, () => { + isQueryListCompleted = true; + }); view.componentInstance.shouldShow = false; view.detectChanges(); @@ -457,7 +458,7 @@ describe('Query API', () => { view.componentInstance.shouldShow = true; view.detectChanges(); - const q2: NeedsQuery = view.debugElement.children[0].references !['q']; + const q2: NeedsQuery = view.debugElement.children[0].references!['q']; expect(q2.query.length).toEqual(1); expect(changes.closed).toBeTruthy(); @@ -472,7 +473,7 @@ describe('Query API', () => { '<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' + '</needs-query-by-ref-binding>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; view.componentInstance.list = ['1d', '2d']; view.detectChanges(); @@ -486,7 +487,7 @@ describe('Query API', () => { '<div text="two" #textLabel2="textDir"></div>' + '</needs-query-by-ref-bindings>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; expect(q.query.first.text).toEqual('one'); expect(q.query.last.text).toEqual('two'); @@ -497,7 +498,7 @@ describe('Query API', () => { '<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' + '</needs-query-by-ref-binding>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; view.componentInstance.list = ['1d', '2d']; view.detectChanges(); @@ -513,7 +514,7 @@ describe('Query API', () => { '</div>' + '</needs-query-by-ref-binding>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; view.componentInstance.list = ['1d', '2d']; view.detectChanges(); @@ -534,14 +535,14 @@ describe('Query API', () => { const template = '<needs-view-query-by-ref-binding #q></needs-view-query-by-ref-binding>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewQueryByLabel = view.debugElement.children[0].references !['q']; + const q: NeedsViewQueryByLabel = view.debugElement.children[0].references!['q']; expect(q.query.first.nativeElement).toHaveText('text'); }); it('should contain all child directives in the view dom', () => { const template = '<needs-view-children #q></needs-view-children>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; expect(q.textDirChildren.length).toEqual(1); expect(q.numberOfChildrenAfterViewInit).toEqual(1); }); @@ -551,21 +552,21 @@ describe('Query API', () => { it('should contain all the elements in the view with that have the given directive', () => { const template = '<needs-view-query #q><div text="ignoreme"></div></needs-view-query>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewQuery = view.debugElement.children[0].references !['q']; + const q: NeedsViewQuery = view.debugElement.children[0].references!['q']; expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); }); it('should not include directive present on the host element', () => { const template = '<needs-view-query #q text="self"></needs-view-query>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewQuery = view.debugElement.children[0].references !['q']; + const q: NeedsViewQuery = view.debugElement.children[0].references!['q']; expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); }); it('should reflect changes in the component', () => { const template = '<needs-view-query-if #q></needs-view-query-if>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewQueryIf = view.debugElement.children[0].references !['q']; + const q: NeedsViewQueryIf = view.debugElement.children[0].references!['q']; expect(q.query.length).toBe(0); q.show = true; @@ -577,7 +578,7 @@ describe('Query API', () => { it('should not be affected by other changes in the component', () => { const template = '<needs-view-query-nested-if #q></needs-view-query-nested-if>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewQueryNestedIf = view.debugElement.children[0].references !['q']; + const q: NeedsViewQueryNestedIf = view.debugElement.children[0].references!['q']; expect(q.query.length).toEqual(1); expect(q.query.first.text).toEqual('1'); @@ -592,7 +593,7 @@ describe('Query API', () => { () => { const template = '<needs-view-query-order #q></needs-view-query-order>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewQueryOrder = view.debugElement.children[0].references !['q']; + const q: NeedsViewQueryOrder = view.debugElement.children[0].references!['q']; expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); @@ -605,7 +606,7 @@ describe('Query API', () => { () => { const template = '<needs-view-query-order-with-p #q></needs-view-query-order-with-p>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references !['q']; + const q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references!['q']; expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); q.list = ['-3', '2']; @@ -616,7 +617,7 @@ describe('Query API', () => { it('should handle long ngFor cycles', () => { const template = '<needs-view-query-order #q></needs-view-query-order>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q: NeedsViewQueryOrder = view.debugElement.children[0].references !['q']; + const q: NeedsViewQueryOrder = view.debugElement.children[0].references!['q']; // no significance to 50, just a reasonably large cycle. for (let i = 0; i < 50; i++) { @@ -630,7 +631,7 @@ describe('Query API', () => { it('should support more than three queries', () => { const template = '<needs-four-queries #q><div text="1"></div></needs-four-queries>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; expect(q.query1).toBeDefined(); expect(q.query2).toBeDefined(); expect(q.query3).toBeDefined(); @@ -643,7 +644,7 @@ describe('Query API', () => { const template = '<manual-projecting #q><ng-template><div text="1"></div></ng-template></manual-projecting>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; expect(q.query.length).toBe(0); q.create(); @@ -665,7 +666,7 @@ describe('Query API', () => { </ng-template> </manual-projecting>`; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q'] as ManualProjecting; + const q = view.debugElement.children[0].references!['q'] as ManualProjecting; expect(q.query.length).toBe(0); const view1 = q.vc.createEmbeddedView(q.template, {'x': '1'}); @@ -694,7 +695,7 @@ describe('Query API', () => { </ng-template> </manual-projecting>`; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q'] as ManualProjecting; + const q = view.debugElement.children[0].references!['q'] as ManualProjecting; expect(q.query.length).toBe(0); const view1 = q.vc.createEmbeddedView(q.template, {'x': '1'}); @@ -729,7 +730,7 @@ describe('Query API', () => { </div> `; const view = createTestCmp(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; view.componentInstance.shouldShow = true; view.detectChanges(); @@ -751,13 +752,11 @@ describe('Query API', () => { class AutoProjecting { // TODO(issue/24571): // remove '!'. - @ContentChild(TemplateRef) - content !: TemplateRef<any>; + @ContentChild(TemplateRef) content!: TemplateRef<any>; // TODO(issue/24571): // remove '!'. - @ContentChildren(TextDirective) - query !: QueryList<TextDirective>; + @ContentChildren(TextDirective) query!: QueryList<TextDirective>; } TestBed.configureTestingModule({declarations: [AutoProjecting]}); @@ -765,7 +764,7 @@ describe('Query API', () => { '<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; // This should be 1, but due to // https://github.com/angular/angular/issues/15117 // this is 0. @@ -783,13 +782,11 @@ describe('Query API', () => { class AutoProjecting { // TODO(issue/24571): // remove '!'. - @ContentChild(TemplateRef) - content !: TemplateRef<any>; + @ContentChild(TemplateRef) content!: TemplateRef<any>; // TODO(issue/24571): // remove '!'. - @ContentChildren(TextDirective) - query !: QueryList<TextDirective>; + @ContentChildren(TextDirective) query!: QueryList<TextDirective>; } TestBed.configureTestingModule({declarations: [AutoProjecting]}); @@ -797,7 +794,7 @@ describe('Query API', () => { '<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>'; const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; + const q = view.debugElement.children[0].references!['q']; expect(q.query.length).toBe(1); }); } @@ -808,35 +805,39 @@ describe('Query API', () => { @Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'}) class TextDirective { // TODO(issue/24571): remove '!'. - text !: string; + text!: string; constructor() {} } @Component({selector: 'needs-content-children', template: ''}) class NeedsContentChildren implements AfterContentInit { // TODO(issue/24571): remove '!'. - @ContentChildren(TextDirective) textDirChildren !: QueryList<TextDirective>; + @ContentChildren(TextDirective) textDirChildren!: QueryList<TextDirective>; // TODO(issue/24571): remove '!'. - numberOfChildrenAfterContentInit !: number; + numberOfChildrenAfterContentInit!: number; - ngAfterContentInit() { this.numberOfChildrenAfterContentInit = this.textDirChildren.length; } + ngAfterContentInit() { + this.numberOfChildrenAfterContentInit = this.textDirChildren.length; + } } @Component({selector: 'needs-view-children', template: '<div text></div>'}) class NeedsViewChildren implements AfterViewInit { // TODO(issue/24571): remove '!'. - @ViewChildren(TextDirective) textDirChildren !: QueryList<TextDirective>; + @ViewChildren(TextDirective) textDirChildren!: QueryList<TextDirective>; // TODO(issue/24571): remove '!'. - numberOfChildrenAfterViewInit !: number; + numberOfChildrenAfterViewInit!: number; - ngAfterViewInit() { this.numberOfChildrenAfterViewInit = this.textDirChildren.length; } + ngAfterViewInit() { + this.numberOfChildrenAfterViewInit = this.textDirChildren.length; + } } @Component({selector: 'needs-content-child', template: ''}) class NeedsContentChild implements AfterContentInit, AfterContentChecked { /** @internal */ // TODO(issue/24571): remove '!'. - _child !: TextDirective; + _child!: TextDirective; @ContentChild(TextDirective) set child(value) { @@ -844,18 +845,24 @@ class NeedsContentChild implements AfterContentInit, AfterContentChecked { this.logs.push(['setter', value ? value.text : null]); } - get child() { return this._child; } + get child() { + return this._child; + } logs: any[] /** TODO #9100 */ = []; - ngAfterContentInit() { this.logs.push(['init', this.child ? this.child.text : null]); } + ngAfterContentInit() { + this.logs.push(['init', this.child ? this.child.text : null]); + } - ngAfterContentChecked() { this.logs.push(['check', this.child ? this.child.text : null]); } + ngAfterContentChecked() { + this.logs.push(['check', this.child ? this.child.text : null]); + } } @Directive({selector: '[directive-needs-content-child]'}) class DirectiveNeedsContentChild { // TODO(issue/24571): remove '!'. - @ContentChild(TextDirective) child !: TextDirective; + @ContentChild(TextDirective) child!: TextDirective; } @Component({selector: 'needs-view-child', template: `<div *ngIf="shouldShow" text="foo"></div>`}) @@ -864,7 +871,7 @@ class NeedsViewChild implements AfterViewInit, AfterViewChecked { shouldShow2: boolean = false; /** @internal */ // TODO(issue/24571): remove '!'. - _child !: TextDirective; + _child!: TextDirective; @ViewChild(TextDirective) set child(value) { @@ -872,12 +879,18 @@ class NeedsViewChild implements AfterViewInit, AfterViewChecked { this.logs.push(['setter', value ? value.text : null]); } - get child() { return this._child; } + get child() { + return this._child; + } logs: any[] /** TODO #9100 */ = []; - ngAfterViewInit() { this.logs.push(['init', this.child ? this.child.text : null]); } + ngAfterViewInit() { + this.logs.push(['init', this.child ? this.child.text : null]); + } - ngAfterViewChecked() { this.logs.push(['check', this.child ? this.child.text : null]); } + ngAfterViewChecked() { + this.logs.push(['check', this.child ? this.child.text : null]); + } } function createTestCmp<T>(type: Type<T>, template: string): ComponentFixture<T> { @@ -895,9 +908,9 @@ function createTestCmpAndDetectChanges<T>(type: Type<T>, template: string): Comp @Component({selector: 'needs-static-content-view-child', template: `<div text="viewFoo"></div>`}) class NeedsStaticContentAndViewChild { // TODO(issue/24571): remove '!'. - @ContentChild(TextDirective, {static: true}) contentChild !: TextDirective; + @ContentChild(TextDirective, {static: true}) contentChild!: TextDirective; // TODO(issue/24571): remove '!'. - @ViewChild(TextDirective, {static: true}) viewChild !: TextDirective; + @ViewChild(TextDirective, {static: true}) viewChild!: TextDirective; } @Directive({selector: '[dir]'}) @@ -910,19 +923,19 @@ class InertDirective { }) class NeedsQuery { // TODO(issue/24571): remove '!'. - @ContentChildren(TextDirective) query !: QueryList<TextDirective>; + @ContentChildren(TextDirective) query!: QueryList<TextDirective>; } @Component({selector: 'needs-four-queries', template: ''}) class NeedsFourQueries { // TODO(issue/24571): remove '!'. - @ContentChild(TextDirective) query1 !: TextDirective; + @ContentChild(TextDirective) query1!: TextDirective; // TODO(issue/24571): remove '!'. - @ContentChild(TextDirective) query2 !: TextDirective; + @ContentChild(TextDirective) query2!: TextDirective; // TODO(issue/24571): remove '!'. - @ContentChild(TextDirective) query3 !: TextDirective; + @ContentChild(TextDirective) query3!: TextDirective; // TODO(issue/24571): remove '!'. - @ContentChild(TextDirective) query4 !: TextDirective; + @ContentChild(TextDirective) query4!: TextDirective; } @Component({ @@ -931,25 +944,25 @@ class NeedsFourQueries { }) class NeedsQueryDesc { // TODO(issue/24571): remove '!'. - @ContentChildren(TextDirective, {descendants: true}) query !: QueryList<TextDirective>; + @ContentChildren(TextDirective, {descendants: true}) query!: QueryList<TextDirective>; } @Component({selector: 'needs-query-by-ref-binding', template: '<ng-content>'}) class NeedsQueryByLabel { // TODO(issue/24571): remove '!'. - @ContentChildren('textLabel', {descendants: true}) query !: QueryList<any>; + @ContentChildren('textLabel', {descendants: true}) query!: QueryList<any>; } @Component({selector: 'needs-view-query-by-ref-binding', template: '<div #textLabel>text</div>'}) class NeedsViewQueryByLabel { // TODO(issue/24571): remove '!'. - @ViewChildren('textLabel') query !: QueryList<any>; + @ViewChildren('textLabel') query!: QueryList<any>; } @Component({selector: 'needs-query-by-ref-bindings', template: '<ng-content>'}) class NeedsQueryByTwoLabels { // TODO(issue/24571): remove '!'. - @ContentChildren('textLabel1,textLabel2', {descendants: true}) query !: QueryList<any>; + @ContentChildren('textLabel1,textLabel2', {descendants: true}) query!: QueryList<any>; } @Component({ @@ -958,7 +971,7 @@ class NeedsQueryByTwoLabels { }) class NeedsQueryAndProject { // TODO(issue/24571): remove '!'. - @ContentChildren(TextDirective) query !: QueryList<TextDirective>; + @ContentChildren(TextDirective) query!: QueryList<TextDirective>; } @Component({ @@ -967,14 +980,14 @@ class NeedsQueryAndProject { }) class NeedsViewQuery { // TODO(issue/24571): remove '!'. - @ViewChildren(TextDirective) query !: QueryList<TextDirective>; + @ViewChildren(TextDirective) query!: QueryList<TextDirective>; } @Component({selector: 'needs-view-query-if', template: '<div *ngIf="show" text="1"></div>'}) class NeedsViewQueryIf { show: boolean = false; // TODO(issue/24571): remove '!'. - @ViewChildren(TextDirective) query !: QueryList<TextDirective>; + @ViewChildren(TextDirective) query!: QueryList<TextDirective>; } @Component({ @@ -984,7 +997,7 @@ class NeedsViewQueryIf { class NeedsViewQueryNestedIf { show: boolean = true; // TODO(issue/24571): remove '!'. - @ViewChildren(TextDirective) query !: QueryList<TextDirective>; + @ViewChildren(TextDirective) query!: QueryList<TextDirective>; } @Component({ @@ -995,7 +1008,7 @@ class NeedsViewQueryNestedIf { }) class NeedsViewQueryOrder { // TODO(issue/24571): remove '!'. - @ViewChildren(TextDirective) query !: QueryList<TextDirective>; + @ViewChildren(TextDirective) query!: QueryList<TextDirective>; list: string[] = ['2', '3']; } @@ -1007,16 +1020,16 @@ class NeedsViewQueryOrder { }) class NeedsViewQueryOrderWithParent { // TODO(issue/24571): remove '!'. - @ViewChildren(TextDirective) query !: QueryList<TextDirective>; + @ViewChildren(TextDirective) query!: QueryList<TextDirective>; list: string[] = ['2', '3']; } @Component({selector: 'needs-tpl', template: '<ng-template><div>shadow</div></ng-template>'}) class NeedsTpl { // TODO(issue/24571): remove '!'. - @ViewChildren(TemplateRef) viewQuery !: QueryList<TemplateRef<Object>>; + @ViewChildren(TemplateRef) viewQuery!: QueryList<TemplateRef<Object>>; // TODO(issue/24571): remove '!'. - @ContentChildren(TemplateRef) query !: QueryList<TemplateRef<Object>>; + @ContentChildren(TemplateRef) query!: QueryList<TemplateRef<Object>>; constructor(public vc: ViewContainerRef) {} } @@ -1024,33 +1037,31 @@ class NeedsTpl { {selector: 'needs-named-tpl', template: '<ng-template #tpl><div>shadow</div></ng-template>'}) class NeedsNamedTpl { // TODO(issue/24571): remove '!'. - @ViewChild('tpl', {static: true}) viewTpl !: TemplateRef<Object>; + @ViewChild('tpl', {static: true}) viewTpl!: TemplateRef<Object>; // TODO(issue/24571): remove '!'. - @ContentChild('tpl', {static: true}) contentTpl !: TemplateRef<Object>; + @ContentChild('tpl', {static: true}) contentTpl!: TemplateRef<Object>; constructor(public vc: ViewContainerRef) {} } @Component({selector: 'needs-content-children-read', template: ''}) class NeedsContentChildrenWithRead { // TODO(issue/24571): remove '!'. - @ContentChildren('q', {read: TextDirective}) textDirChildren !: QueryList<TextDirective>; + @ContentChildren('q', {read: TextDirective}) textDirChildren!: QueryList<TextDirective>; // TODO(issue/24571): remove '!'. - @ContentChildren('nonExisting', {read: TextDirective}) nonExistingVar !: QueryList<TextDirective>; + @ContentChildren('nonExisting', {read: TextDirective}) nonExistingVar!: QueryList<TextDirective>; } @Component({selector: 'needs-content-child-read', template: ''}) class NeedsContentChildWithRead { // TODO(issue/24571): remove '!'. - @ContentChild('q', {read: TextDirective}) textDirChild !: TextDirective; + @ContentChild('q', {read: TextDirective}) textDirChild!: TextDirective; // TODO(issue/24571): remove '!'. - @ContentChild('nonExisting', {read: TextDirective}) - nonExistingVar !: TextDirective; + @ContentChild('nonExisting', {read: TextDirective}) nonExistingVar!: TextDirective; } @Component({selector: 'needs-content-children-shallow', template: ''}) class NeedsContentChildrenShallow { - @ContentChildren('q', {descendants: false}) - children !: QueryList<ElementRef>; + @ContentChildren('q', {descendants: false}) children!: QueryList<ElementRef>; } @Component({ @@ -1059,7 +1070,7 @@ class NeedsContentChildrenShallow { }) class NeedsContentChildTemplateRef { // TODO(issue/24571): remove '!'. - @ContentChild(TemplateRef, {static: true}) templateRef !: TemplateRef<any>; + @ContentChild(TemplateRef, {static: true}) templateRef!: TemplateRef<any>; } @Component({ @@ -1077,9 +1088,9 @@ class NeedsContentChildTemplateRefApp { }) class NeedsViewChildrenWithRead { // TODO(issue/24571): remove '!'. - @ViewChildren('q,w', {read: TextDirective}) textDirChildren !: QueryList<TextDirective>; + @ViewChildren('q,w', {read: TextDirective}) textDirChildren!: QueryList<TextDirective>; // TODO(issue/24571): remove '!'. - @ViewChildren('nonExisting', {read: TextDirective}) nonExistingVar !: QueryList<TextDirective>; + @ViewChildren('nonExisting', {read: TextDirective}) nonExistingVar!: QueryList<TextDirective>; } @Component({ @@ -1088,27 +1099,28 @@ class NeedsViewChildrenWithRead { }) class NeedsViewChildWithRead { // TODO(issue/24571): remove '!'. - @ViewChild('q', {read: TextDirective}) textDirChild !: TextDirective; + @ViewChild('q', {read: TextDirective}) textDirChild!: TextDirective; // TODO(issue/24571): remove '!'. - @ViewChild('nonExisting', {read: TextDirective}) nonExistingVar !: TextDirective; + @ViewChild('nonExisting', {read: TextDirective}) nonExistingVar!: TextDirective; } @Component({selector: 'needs-viewcontainer-read', template: '<div #q></div>'}) class NeedsViewContainerWithRead { // TODO(issue/24571): remove '!'. - @ViewChild('q', {read: ViewContainerRef}) vc !: ViewContainerRef; + @ViewChild('q', {read: ViewContainerRef}) vc!: ViewContainerRef; // TODO(issue/24571): remove '!'. - @ViewChild('nonExisting', {read: ViewContainerRef}) - nonExistingVar !: ViewContainerRef; + @ViewChild('nonExisting', {read: ViewContainerRef}) nonExistingVar!: ViewContainerRef; // TODO(issue/24571): remove '!'. @ContentChild(TemplateRef, {static: true}) template !: TemplateRef<Object>; - createView() { this.vc.createEmbeddedView(this.template); } + createView() { + this.vc.createEmbeddedView(this.template); + } } @Component({selector: 'has-null-query-condition', template: '<div></div>'}) class HasNullQueryCondition { - @ContentChildren(null !) errorTrigger: any; + @ContentChildren(null!) errorTrigger: any; } @Component({selector: 'my-comp', template: ''}) @@ -1127,14 +1139,16 @@ class ManualProjecting { @ContentChild(TemplateRef, {static: true}) template !: TemplateRef<any>; // TODO(issue/24571): remove '!'. - @ViewChild('vc', {read: ViewContainerRef}) - vc !: ViewContainerRef; + @ViewChild('vc', {read: ViewContainerRef}) vc!: ViewContainerRef; // TODO(issue/24571): remove '!'. - @ContentChildren(TextDirective) - query !: QueryList<TextDirective>; + @ContentChildren(TextDirective) query!: QueryList<TextDirective>; - create() { this.vc.createEmbeddedView(this.template); } + create() { + this.vc.createEmbeddedView(this.template); + } - destroy() { this.vc.clear(); } + destroy() { + this.vc.clear(); + } } diff --git a/packages/core/test/linker/query_list_spec.ts b/packages/core/test/linker/query_list_spec.ts index b53827183956d..fa9bf5f9e965d 100644 --- a/packages/core/test/linker/query_list_spec.ts +++ b/packages/core/test/linker/query_list_spec.ts @@ -21,10 +21,11 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin log = ''; }); - function logAppend(item: any /** TODO #9100 */) { log += (log.length == 0 ? '' : ', ') + item; } + function logAppend(item: any /** TODO #9100 */) { + log += (log.length == 0 ? '' : ', ') + item; + } describe('dirty and reset', () => { - it('should initially be dirty and empty', () => { expect(queryList.dirty).toBeTruthy(); expect(queryList.length).toBe(0); @@ -36,7 +37,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin expect(queryList.dirty).toBeFalsy(); expect(queryList.length).toBe(2); }); - }); it('should support resetting and iterating over the new objects', () => { @@ -146,7 +146,7 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin // For loops use the iteration protocol. for (const value of queryListAsIterable) { - expect(value).toBe(data.shift() !); + expect(value).toBe(data.shift()!); } expect(data.length).toBe(0); }); @@ -155,7 +155,11 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin describe('simple observable interface', () => { it('should fire callbacks on change', fakeAsync(() => { let fires = 0; - queryList.changes.subscribe({next: (_) => { fires += 1; }}); + queryList.changes.subscribe({ + next: (_) => { + fires += 1; + } + }); queryList.notifyOnChanges(); tick(); @@ -170,7 +174,11 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin it('should provides query list as an argument', fakeAsync(() => { let recorded: any /** TODO #9100 */; - queryList.changes.subscribe({next: (v: any) => { recorded = v; }}); + queryList.changes.subscribe({ + next: (v: any) => { + recorded = v; + } + }); queryList.reset(['one']); queryList.notifyOnChanges(); diff --git a/packages/core/test/linker/regression_integration_spec.ts b/packages/core/test/linker/regression_integration_spec.ts index 4666970a22424..15239a6277558 100644 --- a/packages/core/test/linker/regression_integration_spec.ts +++ b/packages/core/test/linker/regression_integration_spec.ts @@ -7,18 +7,24 @@ */ import {DOCUMENT, ɵgetDOM as getDOM} from '@angular/common'; -import {ANALYZE_FOR_ENTRY_COMPONENTS, ApplicationRef, Component, ComponentRef, ContentChild, Directive, ErrorHandler, EventEmitter, HostListener, InjectionToken, Injector, Input, NgModule, NgModuleRef, NgZone, Output, Pipe, PipeTransform, Provider, QueryList, Renderer2, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, destroyPlatform, ɵivyEnabled as ivyEnabled} from '@angular/core'; -import {TestBed, fakeAsync, inject, tick} from '@angular/core/testing'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, ApplicationRef, Component, ComponentRef, ContentChild, destroyPlatform, Directive, ErrorHandler, EventEmitter, HostListener, InjectionToken, Injector, Input, NgModule, NgModuleRef, NgZone, Output, Pipe, PipeTransform, Provider, QueryList, Renderer2, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, ɵivyEnabled as ivyEnabled} from '@angular/core'; +import {fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; import {BrowserModule, By} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {modifiedInIvy, onlyInIvy} from '@angular/private/testing'; if (ivyEnabled) { - describe('ivy', () => { declareTests(); }); + describe('ivy', () => { + declareTests(); + }); } else { - describe('jit', () => { declareTests({useJit: true}); }); - describe('no jit', () => { declareTests({useJit: false}); }); + describe('jit', () => { + declareTests({useJit: true}); + }); + describe('no jit', () => { + declareTests({useJit: false}); + }); } declareTestsUsingBootstrap(); @@ -26,11 +32,14 @@ declareTestsUsingBootstrap(); function declareTests(config?: {useJit: boolean}) { // Place to put reproductions for regressions describe('regressions', () => { - - beforeEach(() => { TestBed.configureTestingModule({declarations: [MyComp1, PlatformPipe]}); }); + beforeEach(() => { + TestBed.configureTestingModule({declarations: [MyComp1, PlatformPipe]}); + }); describe('platform pipes', () => { - beforeEach(() => { TestBed.configureCompiler({...config}); }); + beforeEach(() => { + TestBed.configureCompiler({...config}); + }); it('should overwrite them by custom pipes', () => { TestBed.configureTestingModule({declarations: [CustomPipe]}); @@ -84,14 +93,20 @@ function declareTests(config?: {useJit: boolean}) { class MyDir { setterCalls: {[key: string]: any} = {}; // TODO(issue/24571): remove '!'. - changes !: SimpleChanges; + changes!: SimpleChanges; @Input() - set a(v: number) { this.setterCalls['a'] = v; } + set a(v: number) { + this.setterCalls['a'] = v; + } @Input() - set b(v: number) { this.setterCalls['b'] = v; } + set b(v: number) { + this.setterCalls['b'] = v; + } - ngOnChanges(changes: SimpleChanges) { this.changes = changes; } + ngOnChanges(changes: SimpleChanges) { + this.changes = changes; + } } TestBed.configureTestingModule({declarations: [MyDir, MyComp]}); @@ -128,7 +143,7 @@ function declareTests(config?: {useJit: boolean}) { @Component({selector: 'some-comp', template: '<p (click)="nullValue?.click()"></p>'}) class SomeComponent { // TODO(issue/24571): remove '!'. - nullValue !: SomeReferencedClass; + nullValue!: SomeReferencedClass; } class SomeReferencedClass { @@ -195,20 +210,17 @@ function declareTests(config?: {useJit: boolean}) { }); describe('ANALYZE_FOR_ENTRY_COMPONENTS providers', () => { - it('should support class instances', () => { class SomeObject { someMethod() {} } - expect( - () => createInjector([ - {provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: new SomeObject(), multi: true} - ])) + expect(() => createInjector([ + {provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: new SomeObject(), multi: true} + ])) .not.toThrow(); }); }); - }); it('should allow logging a previous elements class binding via interpolation', () => { @@ -298,8 +310,7 @@ function declareTests(config?: {useJit: boolean}) { @Component({template: '<div #vc></div><div *ngIf="show" #vc></div>'}) class MyComp { // TODO(issue/24571): remove '!'. - @ViewChildren('vc', {read: ViewContainerRef}) - viewContainers !: QueryList<ViewContainerRef>; + @ViewChildren('vc', {read: ViewContainerRef}) viewContainers!: QueryList<ViewContainerRef>; show = true; } @@ -361,7 +372,7 @@ function declareTests(config?: {useJit: boolean}) { @Directive({selector: 'test'}) class Test { // TODO(issue/24571): remove '!'. - @Input() @ContentChild(TemplateRef, {static: true}) tpl !: TemplateRef<any>; + @Input() @ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>; } @Component({ @@ -391,7 +402,7 @@ function declareTests(config?: {useJit: boolean}) { .it('should throw if @ContentChild and @Input are on the same property', () => { @Directive({selector: 'test'}) class Test { - @Input() @ContentChild(TemplateRef, {static: true}) tpl !: TemplateRef<any>; + @Input() @ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>; } @Component({selector: 'my-app', template: `<test></test>`}) @@ -412,8 +423,8 @@ function declareTests(config?: {useJit: boolean}) { class MyModule { } - const modRef = TestBed.configureTestingModule({imports: [MyModule]}) - .get(NgModuleRef) as NgModuleRef<MyModule>; + const modRef = TestBed.configureTestingModule({imports: [MyModule]}).get(NgModuleRef) as + NgModuleRef<MyModule>; const compRef = modRef.componentFactoryResolver.resolveComponentFactory(App).create(Injector.NULL); @@ -429,7 +440,9 @@ function declareTestsUsingBootstrap() { class MockConsole { errors: any[][] = []; - error(...s: any[]): void { this.errors.push(s); } + error(...s: any[]): void { + this.errors.push(s); + } } let logger: MockConsole; @@ -445,7 +458,9 @@ function declareTestsUsingBootstrap() { (errorHandler as any)._console = logger as any; })); - afterEach(() => { destroyPlatform(); }); + afterEach(() => { + destroyPlatform(); + }); if (getDOM().supportsDOMEvents()) { // This test needs a real DOM.... @@ -459,7 +474,9 @@ function declareTestsUsingBootstrap() { class ErrorComp { value = 0; thrownValue = 0; - next() { this.value++; } + next() { + this.value++; + } nextAndThrow() { this.value++; this.throwIfNeeded(); @@ -475,11 +492,12 @@ function declareTestsUsingBootstrap() { @Directive({selector: '[dirClick]'}) class EventDir { - @Output() - dirClick = new EventEmitter(); + @Output() dirClick = new EventEmitter(); @HostListener('click', ['$event']) - onClick(event: any) { this.dirClick.next(event); } + onClick(event: any) { + this.dirClick.next(event); + } } @NgModule({ @@ -552,12 +570,16 @@ class MyComp1 { @Pipe({name: 'somePipe', pure: true}) class PlatformPipe implements PipeTransform { - transform(value: any): any { return 'somePlatformPipe'; } + transform(value: any): any { + return 'somePlatformPipe'; + } } @Pipe({name: 'somePipe', pure: true}) class CustomPipe implements PipeTransform { - transform(value: any): any { return 'someCustomPipe'; } + transform(value: any): any { + return 'someCustomPipe'; + } } @Component({selector: 'cmp-content', template: `<ng-content></ng-content>`}) @@ -571,7 +593,9 @@ class MyCountingComp { return {value: 'counting method value'}; } - static reset() { MyCountingComp.calls = 0; } + static reset() { + MyCountingComp.calls = 0; + } static calls = 0; } @@ -581,7 +605,9 @@ class CountingPipe implements PipeTransform { CountingPipe.calls++; return {value: 'counting pipe value'}; } - static reset() { CountingPipe.calls = 0; } + static reset() { + CountingPipe.calls = 0; + } static calls = 0; } diff --git a/packages/core/test/linker/security_integration_spec.ts b/packages/core/test/linker/security_integration_spec.ts index fb077acac786e..ce8b5c5216dd4 100644 --- a/packages/core/test/linker/security_integration_spec.ts +++ b/packages/core/test/linker/security_integration_spec.ts @@ -8,16 +8,22 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {Component, Directive, HostBinding, Input, NO_ERRORS_SCHEMA, ɵivyEnabled as ivyEnabled} from '@angular/core'; -import {ComponentFixture, TestBed, getTestBed} from '@angular/core/testing'; +import {ComponentFixture, getTestBed, TestBed} from '@angular/core/testing'; import {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service'; import {modifiedInIvy, onlyInIvy} from '@angular/private/testing'; { if (ivyEnabled) { - describe('ivy', () => { declareTests(); }); + describe('ivy', () => { + declareTests(); + }); } else { - describe('jit', () => { declareTests({useJit: true}); }); - describe('no jit', () => { declareTests({useJit: false}); }); + describe('jit', () => { + declareTests({useJit: true}); + }); + describe('no jit', () => { + declareTests({useJit: false}); + }); } } @@ -34,7 +40,6 @@ class OnPrefixDir { function declareTests(config?: {useJit: boolean}) { describe('security integration tests', function() { - beforeEach(() => { TestBed.configureCompiler({...config}).configureTestingModule({ declarations: [ @@ -49,7 +54,9 @@ function declareTests(config?: {useJit: boolean}) { originalLog = getDOM().log; getDOM().log = (msg) => { /* disable logging */ }; }); - afterEach(() => { getDOM().log = originalLog; }); + afterEach(() => { + getDOM().log = originalLog; + }); describe('events', () => { modifiedInIvy('on-prefixed attributes validation happens at runtime in Ivy') @@ -113,7 +120,7 @@ function declareTests(config?: {useJit: boolean}) { }); // should not throw for inputs starting with "on" - let cmp: ComponentFixture<SecuredComponent> = undefined !; + let cmp: ComponentFixture<SecuredComponent> = undefined!; expect(() => cmp = TestBed.createComponent(SecuredComponent)).not.toThrow(); // must bind to the directive not to the property of the div @@ -124,7 +131,6 @@ function declareTests(config?: {useJit: boolean}) { expect(getDOM().getProperty(div.nativeElement, 'onclick')).not.toBe(value); expect(div.nativeElement.hasAttribute('onclick')).toEqual(false); }); - }); describe('safe HTML values', function() { @@ -206,8 +212,7 @@ function declareTests(config?: {useJit: boolean}) { @Directive({selector: '[dirHref]'}) class HrefDirective { // TODO(issue/24571): remove '!'. - @HostBinding('href') @Input() - dirHref !: string; + @HostBinding('href') @Input() dirHref!: string; } const template = `<a [dirHref]="ctxProp">Link Title</a>`; @@ -222,8 +227,7 @@ function declareTests(config?: {useJit: boolean}) { @Directive({selector: '[dirHref]'}) class HrefDirective { // TODO(issue/24571): remove '!'. - @HostBinding('attr.href') @Input() - dirHref !: string; + @HostBinding('attr.href') @Input() dirHref!: string; } const template = `<a [dirHref]="ctxProp">Link Title</a>`; diff --git a/packages/core/test/linker/source_map_integration_node_only_spec.ts b/packages/core/test/linker/source_map_integration_node_only_spec.ts index 581fc63dc4c6d..221a7c630925b 100644 --- a/packages/core/test/linker/source_map_integration_node_only_spec.ts +++ b/packages/core/test/linker/source_map_integration_node_only_spec.ts @@ -16,7 +16,7 @@ import {Attribute, Component, Directive, ErrorHandler, ɵglobal} from '@angular/ import {CompilerFacade, ExportedCompilerFacade} from '@angular/core/src/compiler/compiler_facade'; import {getErrorLogger} from '@angular/core/src/errors'; import {resolveComponentResources} from '@angular/core/src/metadata/resource_loading'; -import {TestBed, fakeAsync, tick} from '@angular/core/testing'; +import {fakeAsync, TestBed, tick} from '@angular/core/testing'; import {modifiedInIvy, onlyInIvy} from '@angular/private/testing'; describe('jit source mapping', () => { @@ -44,7 +44,9 @@ describe('jit source mapping', () => { .describe('(View Engine)', () => { describe('inline templates', () => { const ngUrl = 'ng:///DynamicTestModule/MyComp.html'; - function templateDecorator(template: string) { return {template}; } + function templateDecorator(template: string) { + return {template}; + } declareTests({ngUrl, templateDecorator}); }); @@ -66,7 +68,9 @@ describe('jit source mapping', () => { class MyComp { } - expect(() => { compileAndCreateComponent(MyComp); }) + expect(() => { + compileAndCreateComponent(MyComp); + }) .toThrowError( new RegExp(`Template parse errors[\\s\\S]*${escapeRegExp(ngUrl)}@1:2`)); })); @@ -76,7 +80,9 @@ describe('jit source mapping', () => { class MyComp { } - expect(() => { compileAndCreateComponent(MyComp); }) + expect(() => { + compileAndCreateComponent(MyComp); + }) .toThrowError( new RegExp(`Template parse errors[\\s\\S]*${escapeRegExp(ngUrl)}@1:7`)); })); @@ -105,7 +111,9 @@ describe('jit source mapping', () => { @Directive({selector: '[someDir]'}) class SomeDir { - constructor() { throw new Error('Test'); } + constructor() { + throw new Error('Test'); + } } TestBed.configureTestingModule({declarations: [SomeDir]}); @@ -163,7 +171,9 @@ describe('jit source mapping', () => { @Component({...templateDecorator(template)}) class MyComp { - createError() { throw new Error('Test'); } + createError() { + throw new Error('Test'); + } } const comp = compileAndCreateComponent(MyComp); @@ -195,7 +205,9 @@ describe('jit source mapping', () => { @Component({...templateDecorator(template)}) class MyComp { - createError() { throw new Error('Test'); } + createError() { + throw new Error('Test'); + } } const comp = compileAndCreateComponent(MyComp); @@ -219,19 +231,19 @@ describe('jit source mapping', () => { column: 4, source: ngUrl, }); - })); } }); onlyInIvy('Generated filenames and stack traces have changed in ivy').describe('(Ivy)', () => { - beforeEach(() => overrideCompilerFacade()); afterEach(() => restoreCompilerFacade()); describe('inline templates', () => { const ngUrl = 'ng:///MyComp/template.html'; - function templateDecorator(template: string) { return {template}; } + function templateDecorator(template: string) { + return {template}; + } declareTests({ngUrl, templateDecorator}); }); @@ -268,7 +280,9 @@ describe('jit source mapping', () => { class MyComp { } - expect(() => { resolveCompileAndCreateComponent(MyComp, template); }) + expect(() => { + resolveCompileAndCreateComponent(MyComp, template); + }) .toThrowError( new RegExp(`Template parse errors[\\s\\S]*${escapeRegExp(ngUrl)}@1:7`)); })); @@ -297,7 +311,9 @@ describe('jit source mapping', () => { @Directive({selector: '[someDir]'}) class SomeDir { - constructor() { throw new Error('Test'); } + constructor() { + throw new Error('Test'); + } } TestBed.configureTestingModule({declarations: [SomeDir]}); @@ -351,7 +367,9 @@ describe('jit source mapping', () => { @Component({...templateDecorator(template)}) class MyComp { - createError() { throw new Error('Test'); } + createError() { + throw new Error('Test'); + } } const comp = resolveCompileAndCreateComponent(MyComp, template); @@ -375,7 +393,9 @@ describe('jit source mapping', () => { @Component({...templateDecorator(template)}) class MyComp { - createError() { throw new Error('Test'); } + createError() { + throw new Error('Test'); + } } const comp = resolveCompileAndCreateComponent(MyComp, template); @@ -414,7 +434,9 @@ describe('jit source mapping', () => { return TestBed.createComponent(comType); } - function createResolver(contents: string) { return (_url: string) => Promise.resolve(contents); } + function createResolver(contents: string) { + return (_url: string) => Promise.resolve(contents); + } function resolveCompileAndCreateComponent(comType: any, template: string) { resolveComponentResources(createResolver(template)); @@ -438,7 +460,9 @@ describe('jit source mapping', () => { interface TestConfig { ngUrl: string; - templateDecorator: (template: string) => { [key: string]: any }; + templateDecorator: (template: string) => { + [key: string]: any + }; } interface SourcePos { @@ -448,8 +472,8 @@ describe('jit source mapping', () => { } /** - * A helper class that captures the sources that have been JIT compiled. - */ + * A helper class that captures the sources that have been JIT compiled. + */ class MockJitEvaluator extends JitEvaluator { sources: string[] = []; @@ -466,7 +490,7 @@ describe('jit source mapping', () => { */ getSourceMap(genFile: string): SourceMap { return this.sources.map(source => extractSourceMap(source)) - .find(map => !!(map && map.file === genFile)) !; + .find(map => !!(map && map.file === genFile))!; } getSourcePositionForStack(stack: string, genFile: string): SourcePos { @@ -475,9 +499,9 @@ describe('jit source mapping', () => { .map(line => urlRegexp.exec(line)) .filter(match => !!match) .map(match => ({ - file: match ![1], - line: parseInt(match ![2], 10), - column: parseInt(match ![3], 10) + file: match![1], + line: parseInt(match![2], 10), + column: parseInt(match![3], 10) })) .shift(); if (!pos) { @@ -489,8 +513,8 @@ describe('jit source mapping', () => { } function getErrorLoggerStack(e: Error): string { - let logStack: string = undefined !; - getErrorLogger(e)(<any>{error: () => logStack = new Error().stack !}, e.message); + let logStack: string = undefined!; + getErrorLogger(e)(<any>{error: () => logStack = new Error().stack!}, e.message); return logStack; } }); diff --git a/packages/core/test/linker/system_ng_module_factory_loader_spec.ts b/packages/core/test/linker/system_ng_module_factory_loader_spec.ts index b4baf714f93dd..357b738b73577 100644 --- a/packages/core/test/linker/system_ng_module_factory_loader_spec.ts +++ b/packages/core/test/linker/system_ng_module_factory_loader_spec.ts @@ -32,12 +32,15 @@ describe('SystemJsNgModuleLoader', () => { 'prefixed/test/suffixed': {'NamedNgFactory': 'test module factory'} }); }); - afterEach(() => { global['System'] = oldSystem; }); + afterEach(() => { + global['System'] = oldSystem; + }); it('loads a default factory by appending the factory suffix', async(() => { const loader = new SystemJsNgModuleLoader(new Compiler()); - loader.load('test').then( - contents => { expect(contents).toBe('test module factory' as any); }); + loader.load('test').then(contents => { + expect(contents).toBe('test module factory' as any); + }); })); it('loads a named factory by appending the factory suffix', async(() => { const loader = new SystemJsNgModuleLoader(new Compiler()); @@ -63,12 +66,15 @@ describe('SystemJsNgModuleLoader', () => { 'test': {'default': 'test module', 'NamedModule': 'test NamedModule'}, }); }); - afterEach(() => { global['System'] = oldSystem; }); + afterEach(() => { + global['System'] = oldSystem; + }); it('loads a default module', async(() => { const loader = new SystemJsNgModuleLoader(new Compiler()); - loader.load('test').then( - contents => { expect(contents.moduleType).toBe('test module' as any); }); + loader.load('test').then(contents => { + expect(contents.moduleType).toBe('test module' as any); + }); })); it('loads a named module', async(() => { const loader = new SystemJsNgModuleLoader(new Compiler()); diff --git a/packages/core/test/linker/view_injector_integration_spec.ts b/packages/core/test/linker/view_injector_integration_spec.ts index f6604d552b2f9..008f9f5c37f1f 100644 --- a/packages/core/test/linker/view_injector_integration_spec.ts +++ b/packages/core/test/linker/view_injector_integration_spec.ts @@ -7,7 +7,7 @@ */ import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, DebugElement, Directive, ElementRef, EmbeddedViewRef, Host, Inject, InjectionToken, Injector, Input, NgModule, Optional, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewContainerRef} from '@angular/core'; -import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing'; +import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing'; @@ -32,61 +32,81 @@ class CycleDirective { @Directive({selector: '[needsDirectiveFromSelf]'}) class NeedsDirectiveFromSelf { dependency: SimpleDirective; - constructor(@Self() dependency: SimpleDirective) { this.dependency = dependency; } + constructor(@Self() dependency: SimpleDirective) { + this.dependency = dependency; + } } @Directive({selector: '[optionallyNeedsDirective]'}) class OptionallyNeedsDirective { dependency: SimpleDirective; - constructor(@Self() @Optional() dependency: SimpleDirective) { this.dependency = dependency; } + constructor(@Self() @Optional() dependency: SimpleDirective) { + this.dependency = dependency; + } } @Directive({selector: '[needsComponentFromHost]'}) class NeedsComponentFromHost { dependency: SimpleComponent; - constructor(@Host() dependency: SimpleComponent) { this.dependency = dependency; } + constructor(@Host() dependency: SimpleComponent) { + this.dependency = dependency; + } } @Directive({selector: '[needsDirectiveFromHost]'}) class NeedsDirectiveFromHost { dependency: SimpleDirective; - constructor(@Host() dependency: SimpleDirective) { this.dependency = dependency; } + constructor(@Host() dependency: SimpleDirective) { + this.dependency = dependency; + } } @Directive({selector: '[needsDirective]'}) class NeedsDirective { dependency: SimpleDirective; - constructor(dependency: SimpleDirective) { this.dependency = dependency; } + constructor(dependency: SimpleDirective) { + this.dependency = dependency; + } } @Directive({selector: '[needsService]'}) class NeedsService { service: any; - constructor(@Inject('service') service: any) { this.service = service; } + constructor(@Inject('service') service: any) { + this.service = service; + } } @Directive({selector: '[needsAppService]'}) class NeedsAppService { service: any; - constructor(@Inject('appService') service: any) { this.service = service; } + constructor(@Inject('appService') service: any) { + this.service = service; + } } @Component({selector: '[needsHostAppService]', template: ''}) class NeedsHostAppService { service: any; - constructor(@Host() @Inject('appService') service: any) { this.service = service; } + constructor(@Host() @Inject('appService') service: any) { + this.service = service; + } } @Component({selector: '[needsServiceComponent]', template: ''}) class NeedsServiceComponent { service: any; - constructor(@Inject('service') service: any) { this.service = service; } + constructor(@Inject('service') service: any) { + this.service = service; + } } @Directive({selector: '[needsServiceFromHost]'}) class NeedsServiceFromHost { service: any; - constructor(@Host() @Inject('service') service: any) { this.service = service; } + constructor(@Host() @Inject('service') service: any) { + this.service = service; + } } @Directive({selector: '[needsAttribute]'}) @@ -146,36 +166,50 @@ class PushComponentNeedsChangeDetectorRef { @Pipe({name: 'purePipe', pure: true}) class PurePipe implements PipeTransform { constructor() {} - transform(value: any): any { return this; } + transform(value: any): any { + return this; + } } @Pipe({name: 'impurePipe', pure: false}) class ImpurePipe implements PipeTransform { constructor() {} - transform(value: any): any { return this; } + transform(value: any): any { + return this; + } } @Pipe({name: 'pipeNeedsChangeDetectorRef'}) class PipeNeedsChangeDetectorRef { constructor(public changeDetectorRef: ChangeDetectorRef) {} - transform(value: any): any { return this; } + transform(value: any): any { + return this; + } } @Pipe({name: 'pipeNeedsService'}) export class PipeNeedsService implements PipeTransform { service: any; - constructor(@Inject('service') service: any) { this.service = service; } - transform(value: any): any { return this; } + constructor(@Inject('service') service: any) { + this.service = service; + } + transform(value: any): any { + return this; + } } @Pipe({name: 'duplicatePipe'}) export class DuplicatePipe1 implements PipeTransform { - transform(value: any): any { return this; } + transform(value: any): any { + return this; + } } @Pipe({name: 'duplicatePipe'}) export class DuplicatePipe2 implements PipeTransform { - transform(value: any): any { return this; } + transform(value: any): any { + return this; + } } @Component({selector: 'root', template: ''}) @@ -183,15 +217,15 @@ class TestComp { } function createComponentFixture<T>( - template: string, providers?: Provider[] | null, comp?: Type<T>): ComponentFixture<T> { + template: string, providers?: Provider[]|null, comp?: Type<T>): ComponentFixture<T> { if (!comp) { comp = <any>TestComp; } - TestBed.overrideComponent(comp !, {set: {template}}); + TestBed.overrideComponent(comp!, {set: {template}}); if (providers && providers.length) { - TestBed.overrideComponent(comp !, {add: {providers: providers}}); + TestBed.overrideComponent(comp!, {add: {providers: providers}}); } - return TestBed.createComponent(comp !); + return TestBed.createComponent(comp!); } function createComponent(template: string, providers?: Provider[], comp?: Type<any>): DebugElement { @@ -351,7 +385,6 @@ describe('View injector', () => { }); describe('injecting lazy providers into an eager provider via Injector.get', () => { - it('should inject providers that were declared before it', () => { @Component({ template: '', @@ -448,7 +481,9 @@ describe('View injector', () => { @Component({providers: [{provide: 'a', useFactory: () => 'aValue'}], template: ''}) class SomeComponent { public a: string; - constructor(injector: Injector) { this.a = injector.get('a'); } + constructor(injector: Injector) { + this.a = injector.get('a'); + } } const comp = TestBed.configureTestingModule({declarations: [SomeComponent]}) @@ -461,8 +496,12 @@ describe('View injector', () => { let destroyed = false; class SomeInjectable { - constructor() { created = true; } - ngOnDestroy() { destroyed = true; } + constructor() { + created = true; + } + ngOnDestroy() { + destroyed = true; + } } @Component({providers: [SomeInjectable], template: ''}) @@ -910,7 +949,7 @@ describe('View injector', () => { const testInjector = <Injector>{ get: (token: any, notFoundValue: any) => - token === 'someToken' ? 'someNewValue' : notFoundValue + token === 'someToken' ? 'someNewValue' : notFoundValue }; const compFactory = TestBed.configureTestingModule({imports: [TestModule]}) @@ -961,8 +1000,7 @@ describe('View injector', () => { it('should inject ChangeDetectorRef into pipes', () => { TestBed.configureTestingModule({ - declarations: - [SimpleDirective, PipeNeedsChangeDetectorRef, DirectiveNeedsChangeDetectorRef] + declarations: [SimpleDirective, PipeNeedsChangeDetectorRef, DirectiveNeedsChangeDetectorRef] }); const el = createComponent( '<div [simpleDirective]="true | pipeNeedsChangeDetectorRef" directiveNeedsChangeDetectorRef></div>'); diff --git a/packages/core/test/metadata/di_spec.ts b/packages/core/test/metadata/di_spec.ts index 733328e53c540..13421776d5fec 100644 --- a/packages/core/test/metadata/di_spec.ts +++ b/packages/core/test/metadata/di_spec.ts @@ -68,7 +68,6 @@ import {TestBed} from '@angular/core/testing'; view.detectChanges(); expect(view.componentInstance.children).toBeDefined(); expect(view.componentInstance.children.length).toBe(2); - }); }); } @@ -77,30 +76,30 @@ import {TestBed} from '@angular/core/testing'; @Directive({selector: 'simple'}) class Simple { // TODO(issue/24571): remove '!'. - @Input() marker !: string; + @Input() marker!: string; } @Component({selector: 'view-child-type-selector', template: ''}) class ViewChildTypeSelectorComponent { // TODO(issue/24571): remove '!'. - @ViewChild(Simple) child !: Simple; + @ViewChild(Simple) child!: Simple; } @Component({selector: 'view-child-string-selector', template: ''}) class ViewChildStringSelectorComponent { // TODO(issue/24571): remove '!'. - @ViewChild('child') child !: ElementRef; + @ViewChild('child') child!: ElementRef; } @Component({selector: 'view-children-type-selector', template: ''}) class ViewChildrenTypeSelectorComponent { // TODO(issue/24571): remove '!'. - @ViewChildren(Simple) children !: QueryList<Simple>; + @ViewChildren(Simple) children!: QueryList<Simple>; } @Component({selector: 'view-child-string-selector', template: ''}) class ViewChildrenStringSelectorComponent { // Allow comma separated selector (with spaces). // TODO(issue/24571): remove '!'. - @ViewChildren('child1 , child2') children !: QueryList<ElementRef>; + @ViewChildren('child1 , child2') children!: QueryList<ElementRef>; } diff --git a/packages/core/test/metadata/resource_loading_spec.ts b/packages/core/test/metadata/resource_loading_spec.ts index c27f8a5f3e4a2..2fb11b3f1127a 100644 --- a/packages/core/test/metadata/resource_loading_spec.ts +++ b/packages/core/test/metadata/resource_loading_spec.ts @@ -16,7 +16,7 @@ describe('resource_loading', () => { describe('error handling', () => { it('should throw an error when compiling component that has unresolved templateUrl', () => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; compileComponent(MyComponent, {templateUrl: 'someUrl'}); expect(() => MyComponent.ɵcmp).toThrowError(` Component 'MyComponent' is not resolved: @@ -25,7 +25,7 @@ Did you run and wait for 'resolveComponentResources()'?`.trim()); }); it('should throw an error when compiling component that has unresolved styleUrls', () => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; compileComponent(MyComponent, {styleUrls: ['someUrl1', 'someUrl2']}); expect(() => MyComponent.ɵcmp).toThrowError(` Component 'MyComponent' is not resolved: @@ -35,7 +35,7 @@ Did you run and wait for 'resolveComponentResources()'?`.trim()); it('should throw an error when compiling component that has unresolved templateUrl and styleUrls', () => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; compileComponent( MyComponent, {templateUrl: 'someUrl', styleUrls: ['someUrl1', 'someUrl2']}); expect(() => MyComponent.ɵcmp).toThrowError(` @@ -59,8 +59,8 @@ Did you run and wait for 'resolveComponentResources()'?`.trim()); } beforeEach(() => resourceFetchCount = 0); - it('should resolve template', async() => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; + it('should resolve template', async () => { + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; const metadata: Component = {templateUrl: 'test://content'}; compileComponent(MyComponent, metadata); await resolveComponentResources(testResolver); @@ -69,8 +69,8 @@ Did you run and wait for 'resolveComponentResources()'?`.trim()); expect(resourceFetchCount).toBe(1); }); - it('should resolve styleUrls', async() => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; + it('should resolve styleUrls', async () => { + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; const metadata: Component = {template: '', styleUrls: ['test://style1', 'test://style2']}; compileComponent(MyComponent, metadata); await resolveComponentResources(testResolver); @@ -80,8 +80,8 @@ Did you run and wait for 'resolveComponentResources()'?`.trim()); expect(resourceFetchCount).toBe(2); }); - it('should cache multiple resolution to same URL', async() => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; + it('should cache multiple resolution to same URL', async () => { + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; const metadata: Component = {template: '', styleUrls: ['test://style1', 'test://style1']}; compileComponent(MyComponent, metadata); await resolveComponentResources(testResolver); @@ -91,8 +91,8 @@ Did you run and wait for 'resolveComponentResources()'?`.trim()); expect(resourceFetchCount).toBe(1); }); - it('should keep order even if the resolution is out of order', async() => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; + it('should keep order even if the resolution is out of order', async () => { + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; const metadata: Component = { template: '', styles: ['existing'], @@ -113,8 +113,8 @@ Did you run and wait for 'resolveComponentResources()'?`.trim()); }); it('should not add components without external resources to resolution queue', () => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; - const MyComponent2: ComponentType<any> = (class MyComponent{}) as any; + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; + const MyComponent2: ComponentType<any> = (class MyComponent {}) as any; compileComponent(MyComponent, {template: ''}); expect(isComponentResourceResolutionQueueEmpty()).toBe(true); @@ -127,12 +127,14 @@ Did you run and wait for 'resolveComponentResources()'?`.trim()); describe('fetch', () => { function fetch(url: string): Promise<Response> { return Promise.resolve({ - text() { return 'response for ' + url; } + text() { + return 'response for ' + url; + } } as any as Response); } - it('should work with fetch', async() => { - const MyComponent: ComponentType<any> = (class MyComponent{}) as any; + it('should work with fetch', async () => { + const MyComponent: ComponentType<any> = (class MyComponent {}) as any; const metadata: Component = {templateUrl: 'test://content'}; compileComponent(MyComponent, metadata); await resolveComponentResources(fetch); diff --git a/packages/core/test/reflection/reflector_spec.ts b/packages/core/test/reflection/reflector_spec.ts index ff07f8b1f66b1..90a008238fd9f 100644 --- a/packages/core/test/reflection/reflector_spec.ts +++ b/packages/core/test/reflection/reflector_spec.ts @@ -7,13 +7,13 @@ */ import {Reflector} from '@angular/core/src/reflection/reflection'; -import {ReflectionCapabilities, isDelegateCtor} from '@angular/core/src/reflection/reflection_capabilities'; +import {isDelegateCtor, ReflectionCapabilities} from '@angular/core/src/reflection/reflection_capabilities'; import {makeDecorator, makeParamDecorator, makePropDecorator} from '@angular/core/src/util/decorators'; import {global} from '@angular/core/src/util/global'; interface ClassDecoratorFactory { (data: ClassDecorator): any; - new (data: ClassDecorator): ClassDecorator; + new(data: ClassDecorator): ClassDecorator; } interface ClassDecorator { @@ -46,10 +46,12 @@ class ClassWithDecorators { b: AType; @PropDecorator('p3') - set c(value: any) {} + set c(value: any) { + } @PropDecorator('p4') - someMethod() {} + someMethod() { + } constructor(@ParamDecorator('a') a: AType, @ParamDecorator('b') b: AType) { this.a = a; @@ -64,14 +66,18 @@ class ClassWithoutDecorators { class TestObj { constructor(public a: any, public b: any) {} - identity(arg: any) { return arg; } + identity(arg: any) { + return arg; + } } { describe('Reflector', () => { let reflector: Reflector; - beforeEach(() => { reflector = new Reflector(new ReflectionCapabilities()); }); + beforeEach(() => { + reflector = new Reflector(new ReflectionCapabilities()); + }); describe('factory', () => { it('should create a factory for the given type', () => { @@ -131,8 +137,7 @@ class TestObj { it('should also return metadata if the class has no decorator', () => { class Test { - @PropDecorator('test') - prop: any; + @PropDecorator('test') prop: any; } expect(reflector.propMetadata(Test)).toEqual({'prop': [new PropDecorator('test')]}); @@ -183,7 +188,9 @@ class TestObj { class ChildNoCtor extends Parent {} class ChildWithCtor extends Parent { - constructor() { super(); } + constructor() { + super(); + } } class ChildNoCtorPrivateProps extends Parent { private x = 10; @@ -242,12 +249,10 @@ class TestObj { noCtor(`class $Bar1_ extends $Fo0_ {}`); noCtor(`class Bar extends Foo { other(){} }`); }); - }); describe('inheritance with decorators', () => { it('should inherit annotations', () => { - @ClassDecorator({value: 'parent'}) class Parent { } @@ -273,7 +278,7 @@ class TestObj { expect(reflector.annotations(NoDecorators)).toEqual([]); expect(reflector.annotations(<any>{})).toEqual([]); expect(reflector.annotations(<any>1)).toEqual([]); - expect(reflector.annotations(null !)).toEqual([]); + expect(reflector.annotations(null!)).toEqual([]); }); it('should inherit parameters', () => { @@ -303,11 +308,15 @@ class TestObj { // as otherwise TS won't capture the ctor arguments! @ClassDecorator({value: 'child'}) class ChildWithCtor extends Parent { - constructor(@ParamDecorator('c') c: C) { super(null !, null !); } + constructor(@ParamDecorator('c') c: C) { + super(null!, null!); + } } class ChildWithCtorNoDecorator extends Parent { - constructor(a: any, b: any, c: any) { super(null !, null !); } + constructor(a: any, b: any, c: any) { + super(null!, null!); + } } class NoDecorators {} @@ -340,7 +349,7 @@ class TestObj { expect(reflector.parameters(NoDecorators)).toEqual([]); expect(reflector.parameters(<any>{})).toEqual([]); expect(reflector.parameters(<any>1)).toEqual([]); - expect(reflector.parameters(null !)).toEqual([]); + expect(reflector.parameters(null!)).toEqual([]); }); it('should inherit property metadata', () => { @@ -350,20 +359,16 @@ class TestObj { class Parent { // TODO(issue/24571): remove '!'. - @PropDecorator('a') - a !: A; + @PropDecorator('a') a!: A; // TODO(issue/24571): remove '!'. - @PropDecorator('b1') - b !: B; + @PropDecorator('b1') b!: B; } class Child extends Parent { // TODO(issue/24571): remove '!'. - @PropDecorator('b2') - b !: B; + @PropDecorator('b2') b!: B; // TODO(issue/24571): remove '!'. - @PropDecorator('c') - c !: C; + @PropDecorator('c') c!: C; } class NoDecorators {} @@ -383,7 +388,7 @@ class TestObj { expect(reflector.propMetadata(NoDecorators)).toEqual({}); expect(reflector.propMetadata(<any>{})).toEqual({}); expect(reflector.propMetadata(<any>1)).toEqual({}); - expect(reflector.propMetadata(null !)).toEqual({}); + expect(reflector.propMetadata(null!)).toEqual({}); }); it('should inherit lifecycle hooks', () => { @@ -406,12 +411,10 @@ class TestObj { expect(hooks(Child, ['hook1', 'hook2', 'hook3'])).toEqual([true, true, true]); }); - }); describe('inheritance with tsickle', () => { it('should inherit annotations', () => { - class Parent { static decorators = [{type: ClassDecorator, args: [{value: 'parent'}]}]; } @@ -448,9 +451,12 @@ class TestObj { class Child extends Parent {} class ChildWithCtor extends Parent { - static ctorParameters = - () => [{type: C, decorators: [{type: ParamDecorator, args: ['c']}]}, ] - constructor() { super(); } + static ctorParameters = () => + [{type: C, decorators: [{type: ParamDecorator, args: ['c']}]}, + ] + constructor() { + super(); + } } // Check that metadata for Parent was not changed! @@ -496,12 +502,10 @@ class TestObj { 'c': [new PropDecorator('c')] }); }); - }); describe('inheritance with es5 API', () => { it('should inherit annotations', () => { - class Parent { static annotations = [new ClassDecorator({value: 'parent'})]; } @@ -541,7 +545,9 @@ class TestObj { static parameters = [ [C, new ParamDecorator('c')], ]; - constructor() { super(); } + constructor() { + super(); + } } // Check that metadata for Parent was not changed! diff --git a/packages/core/test/render3/basic_perf.ts b/packages/core/test/render3/basic_perf.ts index f719d1c0d1f9b..27e562f64e4ca 100644 --- a/packages/core/test/render3/basic_perf.ts +++ b/packages/core/test/render3/basic_perf.ts @@ -13,7 +13,6 @@ import {RenderFlags} from '../../src/render3/interfaces/definition'; import {document, renderComponent} from './render_util'; describe('iv perf test', () => { - const count = 100000; const noOfIterations = 10; @@ -39,28 +38,29 @@ describe('iv perf test', () => { selectors: [['div']], decls: 1, vars: 0, - template: function Template(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵcontainer(0); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(0); - { - for (let i = 0; i < count; i++) { - let rf0 = ɵɵembeddedViewStart(0, 2, 0); + template: + function Template(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵcontainer(0); + } + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(0); { - if (rf0 & RenderFlags.Create) { - ɵɵelementStart(0, 'div'); - ɵɵtext(1, '-'); - ɵɵelementEnd(); + for (let i = 0; i < count; i++) { + let rf0 = ɵɵembeddedViewStart(0, 2, 0); + { + if (rf0 & RenderFlags.Create) { + ɵɵelementStart(0, 'div'); + ɵɵtext(1, '-'); + ɵɵelementEnd(); + } + } + ɵɵembeddedViewEnd(); } } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } } - ɵɵcontainerRefreshEnd(); - } - } }); } diff --git a/packages/core/test/render3/change_detection_spec.ts b/packages/core/test/render3/change_detection_spec.ts index cc46bf864e7f9..b6844608b0fd5 100644 --- a/packages/core/test/render3/change_detection_spec.ts +++ b/packages/core/test/render3/change_detection_spec.ts @@ -10,7 +10,7 @@ import {withBody} from '@angular/private/testing'; import {ChangeDetectionStrategy, DoCheck} from '../../src/core'; import {whenRendered} from '../../src/render3/component'; -import {LifecycleHooksFeature, getRenderedText, ɵɵadvance, ɵɵdefineComponent, ɵɵgetCurrentView, ɵɵproperty, ɵɵtextInterpolate1, ɵɵtextInterpolate2} from '../../src/render3/index'; +import {getRenderedText, LifecycleHooksFeature, ɵɵadvance, ɵɵdefineComponent, ɵɵgetCurrentView, ɵɵproperty, ɵɵtextInterpolate1, ɵɵtextInterpolate2} from '../../src/render3/index'; import {detectChanges, markDirty, tick, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵlistener, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer'; @@ -23,7 +23,9 @@ describe('change detection', () => { class MyComponent implements DoCheck { value: string = 'works'; doCheckCount = 0; - ngDoCheck(): void { this.doCheckCount++; } + ngDoCheck(): void { + this.doCheckCount++; + } static ɵfac = () => new MyComponent(); static ɵcmp = ɵɵdefineComponent({ @@ -31,17 +33,18 @@ describe('change detection', () => { selectors: [['my-comp']], decls: 2, vars: 1, - template: (rf: RenderFlags, ctx: MyComponent) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'span'); - ɵɵtext(1); - ɵɵelementEnd(); - } - if (rf & RenderFlags.Update) { - ɵɵadvance(1); - ɵɵtextInterpolate(ctx.value); - } - } + template: + (rf: RenderFlags, ctx: MyComponent) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'span'); + ɵɵtext(1); + ɵɵelementEnd(); + } + if (rf & RenderFlags.Update) { + ɵɵadvance(1); + ɵɵtextInterpolate(ctx.value); + } + } }); } @@ -78,7 +81,7 @@ describe('change detection', () => { expect(myComp.doCheckCount).toBe(2); })); - it('should notify whenRendered', withBody('my-comp', async() => { + it('should notify whenRendered', withBody('my-comp', async () => { const myComp = renderComponent(MyComponent, {hostFeatures: [LifecycleHooksFeature]}); await whenRendered(myComp); myComp.value = 'updated'; @@ -97,7 +100,9 @@ describe('change detection', () => { name = 'Nancy'; doCheckCount = 0; - ngDoCheck(): void { this.doCheckCount++; } + ngDoCheck(): void { + this.doCheckCount++; + } onClick() {} @@ -111,19 +116,22 @@ describe('change detection', () => { * {{ doCheckCount }} - {{ name }} * <button (click)="onClick()"></button> */ - template: (rf: RenderFlags, ctx: MyComponent) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0); - ɵɵelementStart(1, 'button'); - { - ɵɵlistener('click', () => { ctx.onClick(); }); - } - ɵɵelementEnd(); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate2('', ctx.doCheckCount, ' - ', ctx.name, ''); - } - }, + template: + (rf: RenderFlags, ctx: MyComponent) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0); + ɵɵelementStart(1, 'button'); + { + ɵɵlistener('click', () => { + ctx.onClick(); + }); + } + ɵɵelementEnd(); + } + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate2('', ctx.doCheckCount, ' - ', ctx.name, ''); + } + }, changeDetection: ChangeDetectionStrategy.OnPush, inputs: {name: 'name'} }); @@ -135,7 +143,9 @@ describe('change detection', () => { name = 'Nancy'; doCheckCount = 0; - ngDoCheck(): void { this.doCheckCount++; } + ngDoCheck(): void { + this.doCheckCount++; + } onClick() {} @@ -149,24 +159,27 @@ describe('change detection', () => { * {{ doCheckCount }} - {{ name }} * <button (click)="onClick()"></button> */ - template: (rf: RenderFlags, ctx: ManualComponent) => { - if (rf & RenderFlags.Create) { - // This is temporarily the only way to turn on manual change detection - // because public API has not yet been added. - const view = ɵɵgetCurrentView() as any; - view[FLAGS] |= LViewFlags.ManualOnPush; - - ɵɵtext(0); - ɵɵelementStart(1, 'button'); - { - ɵɵlistener('click', () => { ctx.onClick(); }); - } - ɵɵelementEnd(); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate2('', ctx.doCheckCount, ' - ', ctx.name, ''); - } - }, + template: + (rf: RenderFlags, ctx: ManualComponent) => { + if (rf & RenderFlags.Create) { + // This is temporarily the only way to turn on manual change detection + // because public API has not yet been added. + const view = ɵɵgetCurrentView() as any; + view[FLAGS] |= LViewFlags.ManualOnPush; + + ɵɵtext(0); + ɵɵelementStart(1, 'button'); + { + ɵɵlistener('click', () => { + ctx.onClick(); + }); + } + ɵɵelementEnd(); + } + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate2('', ctx.doCheckCount, ' - ', ctx.name, ''); + } + }, changeDetection: ChangeDetectionStrategy.OnPush, inputs: {name: 'name'} }); @@ -182,15 +195,15 @@ describe('change detection', () => { decls: 1, vars: 1, /** <manual-comp [name]="name"></manual-comp> */ - template: (rf: RenderFlags, ctx: ManualApp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'manual-comp'); - } - if (rf & RenderFlags.Update) { - ɵɵproperty('name', ctx.name); - } - - }, + template: + (rf: RenderFlags, ctx: ManualApp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'manual-comp'); + } + if (rf & RenderFlags.Update) { + ɵɵproperty('name', ctx.name); + } + }, directives: () => [ManualComponent] }); } @@ -202,7 +215,7 @@ describe('change detection', () => { expect(comp.doCheckCount).toEqual(1); expect(getRenderedText(myApp)).toEqual('1 - Nancy'); - const button = containerEl.querySelector('button') !; + const button = containerEl.querySelector('button')!; button.click(); requestAnimationFrame.flush(); // No ticks should have been scheduled. @@ -228,7 +241,9 @@ describe('change detection', () => { class ButtonParent implements DoCheck { doCheckCount = 0; - ngDoCheck(): void { this.doCheckCount++; } + ngDoCheck(): void { + this.doCheckCount++; + } static ɵfac = () => parent = new ButtonParent(); static ɵcmp = ɵɵdefineComponent({ @@ -237,15 +252,16 @@ describe('change detection', () => { decls: 2, vars: 1, /** {{ doCheckCount }} - <manual-comp></manual-comp> */ - template: (rf: RenderFlags, ctx: ButtonParent) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0); - ɵɵelement(1, 'manual-comp'); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate1('', ctx.doCheckCount, ' - '); - } - }, + template: + (rf: RenderFlags, ctx: ButtonParent) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0); + ɵɵelement(1, 'manual-comp'); + } + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate1('', ctx.doCheckCount, ' - '); + } + }, directives: () => [ManualComponent], changeDetection: ChangeDetectionStrategy.OnPush }); @@ -258,35 +274,35 @@ describe('change detection', () => { }, 1, 0, [ButtonParent]); const myButtonApp = renderComponent(MyButtonApp); - expect(parent !.doCheckCount).toEqual(1); - expect(comp !.doCheckCount).toEqual(1); + expect(parent!.doCheckCount).toEqual(1); + expect(comp!.doCheckCount).toEqual(1); expect(getRenderedText(myButtonApp)).toEqual('1 - 1 - Nancy'); tick(myButtonApp); - expect(parent !.doCheckCount).toEqual(2); + expect(parent!.doCheckCount).toEqual(2); // parent isn't checked, so child doCheck won't run - expect(comp !.doCheckCount).toEqual(1); + expect(comp!.doCheckCount).toEqual(1); expect(getRenderedText(myButtonApp)).toEqual('1 - 1 - Nancy'); const button = containerEl.querySelector('button'); - button !.click(); + button!.click(); requestAnimationFrame.flush(); // No ticks should have been scheduled. - expect(parent !.doCheckCount).toEqual(2); - expect(comp !.doCheckCount).toEqual(1); + expect(parent!.doCheckCount).toEqual(2); + expect(comp!.doCheckCount).toEqual(1); tick(myButtonApp); - expect(parent !.doCheckCount).toEqual(3); + expect(parent!.doCheckCount).toEqual(3); // parent isn't checked, so child doCheck won't run - expect(comp !.doCheckCount).toEqual(1); + expect(comp!.doCheckCount).toEqual(1); expect(getRenderedText(myButtonApp)).toEqual('1 - 1 - Nancy'); markDirty(comp); requestAnimationFrame.flush(); // Now that markDirty has been manually called, both views should be dirty and a tick // should be scheduled to check the view. - expect(parent !.doCheckCount).toEqual(4); - expect(comp !.doCheckCount).toEqual(2); + expect(parent!.doCheckCount).toEqual(4); + expect(comp!.doCheckCount).toEqual(2); expect(getRenderedText(myButtonApp)).toEqual('4 - 2 - Nancy'); }); }); @@ -296,7 +312,9 @@ describe('change detection', () => { const log: string[] = []; const testRendererFactory: RendererFactory3 = { - createRenderer: (): Renderer3 => { return document; }, + createRenderer: (): Renderer3 => { + return document; + }, begin: () => log.push('begin'), end: () => log.push('end'), }; @@ -313,14 +331,15 @@ describe('change detection', () => { selectors: [['my-comp']], decls: 1, vars: 1, - template: (rf: RenderFlags, ctx: MyComponent) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate(ctx.value); - } - } + template: + (rf: RenderFlags, ctx: MyComponent) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0); + } + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate(ctx.value); + } + } }); } @@ -328,5 +347,4 @@ describe('change detection', () => { expect(getRenderedText(myComp)).toEqual('works'); expect(log).toEqual(['begin', 'detect changes', 'end']); }); - }); diff --git a/packages/core/test/render3/common_with_def.ts b/packages/core/test/render3/common_with_def.ts index 96904c1b8e5cc..4a36243bb4527 100644 --- a/packages/core/test/render3/common_with_def.ts +++ b/packages/core/test/render3/common_with_def.ts @@ -9,7 +9,7 @@ import {NgForOf as NgForOfDef, NgIf as NgIfDef, NgTemplateOutlet as NgTemplateOutletDef} from '@angular/common'; import {IterableDiffers, NgIterable, TemplateRef, ViewContainerRef} from '@angular/core'; -import {DirectiveType, ɵɵNgOnChangesFeature, ɵɵdefineDirective, ɵɵdirectiveInject} from '../../src/render3/index'; +import {DirectiveType, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵNgOnChangesFeature} from '../../src/render3/index'; export const NgForOf: DirectiveType<NgForOfDef<any, NgIterable<any>>> = NgForOfDef as any; export const NgIf: DirectiveType<NgIfDef<any>> = NgIfDef as any; @@ -42,8 +42,7 @@ NgTemplateOutlet.ɵdir = ɵɵdefineDirective({ type: NgTemplateOutletDef, selectors: [['', 'ngTemplateOutlet', '']], features: [ɵɵNgOnChangesFeature], - inputs: - {ngTemplateOutlet: 'ngTemplateOutlet', ngTemplateOutletContext: 'ngTemplateOutletContext'} + inputs: {ngTemplateOutlet: 'ngTemplateOutlet', ngTemplateOutletContext: 'ngTemplateOutletContext'} }); NgTemplateOutlet.ɵfac = () => new NgTemplateOutletDef(ɵɵdirectiveInject(ViewContainerRef as any)); diff --git a/packages/core/test/render3/component_ref_spec.ts b/packages/core/test/render3/component_ref_spec.ts index 6d5b4375ab96b..4c06136361dc5 100644 --- a/packages/core/test/render3/component_ref_spec.ts +++ b/packages/core/test/render3/component_ref_spec.ts @@ -11,7 +11,7 @@ import {ComponentFactory} from '../../src/linker/component_factory'; import {RendererFactory2, RendererType2} from '../../src/render/api'; import {injectComponentFactoryResolver} from '../../src/render3/component_ref'; import {AttributeMarker, ɵɵdefineComponent} from '../../src/render3/index'; -import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; +import {domRendererFactory3, RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer'; import {Sanitizer} from '../../src/sanitization/sanitizer'; describe('ComponentFactory', () => { @@ -146,7 +146,7 @@ describe('ComponentFactory', () => { {provide: RendererFactory2, useValue: {createRenderer: createRenderer3Spy}}, ]); - cf.create(injector, undefined, undefined, { injector: mInjector } as NgModuleRef<any>); + cf.create(injector, undefined, undefined, {injector: mInjector} as NgModuleRef<any>); expect(createRenderer2Spy).toHaveBeenCalled(); expect(createRenderer3Spy).not.toHaveBeenCalled(); @@ -159,7 +159,7 @@ describe('ComponentFactory', () => { {provide: RendererFactory2, useValue: {createRenderer: createRenderer2Spy}}, ]); - cf.create(injector, undefined, undefined, { injector: mInjector } as NgModuleRef<any>); + cf.create(injector, undefined, undefined, {injector: mInjector} as NgModuleRef<any>); expect(createRenderer2Spy).toHaveBeenCalled(); expect(createRenderer3Spy).not.toHaveBeenCalled(); @@ -169,7 +169,7 @@ describe('ComponentFactory', () => { const injector = Injector.create([]); const mInjector = Injector.create([]); - cf.create(injector, undefined, undefined, { injector: mInjector } as NgModuleRef<any>); + cf.create(injector, undefined, undefined, {injector: mInjector} as NgModuleRef<any>); expect(createRenderer2Spy).not.toHaveBeenCalled(); expect(createRenderer3Spy).toHaveBeenCalled(); @@ -188,7 +188,7 @@ describe('ComponentFactory', () => { {provide: Sanitizer, useFactory: mSanitizerFactorySpy, deps: []}, ]); - cf.create(injector, undefined, undefined, { injector: mInjector } as NgModuleRef<any>); + cf.create(injector, undefined, undefined, {injector: mInjector} as NgModuleRef<any>); expect(iSanitizerFactorySpy).toHaveBeenCalled(); expect(mSanitizerFactorySpy).not.toHaveBeenCalled(); @@ -205,7 +205,7 @@ describe('ComponentFactory', () => { ]); - cf.create(injector, undefined, undefined, { injector: mInjector } as NgModuleRef<any>); + cf.create(injector, undefined, undefined, {injector: mInjector} as NgModuleRef<any>); expect(mSanitizerFactorySpy).toHaveBeenCalled(); }); diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index ded79d0183e3e..b15319cc0be77 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -8,18 +8,20 @@ import {ViewEncapsulation, ɵɵdefineInjectable, ɵɵdefineInjector} from '../../src/core'; import {createInjector} from '../../src/di/r3_injector'; -import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, getRenderedText, markDirty, ɵɵadvance, ɵɵdefineComponent, ɵɵdirectiveInject, ɵɵproperty, ɵɵselect, ɵɵtemplate} from '../../src/render3/index'; +import {AttributeMarker, ComponentFactory, getRenderedText, LifecycleHooksFeature, markDirty, ɵɵadvance, ɵɵdefineComponent, ɵɵdirectiveInject, ɵɵproperty, ɵɵselect, ɵɵtemplate} from '../../src/render3/index'; import {tick, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵnextContext, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all'; import {ComponentDef, RenderFlags} from '../../src/render3/interfaces/definition'; import {NgIf} from './common_with_def'; -import {ComponentFixture, MockRendererFactory, containerEl, createComponent, renderComponent, renderToHtml, requestAnimationFrame, toHtml} from './render_util'; +import {ComponentFixture, containerEl, createComponent, MockRendererFactory, renderComponent, renderToHtml, requestAnimationFrame, toHtml} from './render_util'; describe('component', () => { class CounterComponent { count = 0; - increment() { this.count++; } + increment() { + this.count++; + } static ɵfac = () => new CounterComponent; static ɵcmp = ɵɵdefineComponent({ @@ -28,14 +30,15 @@ describe('component', () => { selectors: [['counter']], decls: 1, vars: 1, - template: function(rf: RenderFlags, ctx: CounterComponent) { - if (rf & RenderFlags.Create) { - ɵɵtext(0); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate(ctx.count); - } - }, + template: + function(rf: RenderFlags, ctx: CounterComponent) { + if (rf & RenderFlags.Create) { + ɵɵtext(0); + } + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate(ctx.count); + } + }, inputs: {count: 'count'}, }); } @@ -78,14 +81,15 @@ describe('component', () => { selectors: [['my-component']], decls: 1, vars: 1, - template: function(fs: RenderFlags, ctx: MyComponent) { - if (fs & RenderFlags.Create) { - ɵɵtext(0); - } - if (fs & RenderFlags.Update) { - ɵɵtextInterpolate(ctx.myService.value); - } - } + template: + function(fs: RenderFlags, ctx: MyComponent) { + if (fs & RenderFlags.Create) { + ɵɵtext(0); + } + if (fs & RenderFlags.Update) { + ɵɵtextInterpolate(ctx.myService.value); + } + } }); } @@ -105,11 +109,9 @@ describe('component', () => { const fixture = new ComponentFixture(MyComponent, {injector: createInjector(MyModule)}); expect(fixture.html).toEqual('injector'); }); - }); it('should instantiate components at high indices', () => { - // {{ name }} class Comp { // @Input @@ -121,14 +123,15 @@ describe('component', () => { selectors: [['comp']], decls: 1, vars: 1, - template: (rf: RenderFlags, ctx: Comp) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate(ctx.name); - } - }, + template: + (rf: RenderFlags, ctx: Comp) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0); + } + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate(ctx.name); + } + }, inputs: {name: 'name'} }); } @@ -152,7 +155,6 @@ describe('component', () => { fixture.update(); expect(fixture.html).toEqual('<comp>some name</comp>'); }); - }); it('should not invoke renderer destroy method for embedded views', () => { @@ -186,31 +188,32 @@ it('should not invoke renderer destroy method for embedded views', () => { * <div>Root view</div> * <div *ngIf="visible">Child view</div> */ - template: function(rf: RenderFlags, ctx: Comp) { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'div'); - ɵɵtext(1, 'Root view'); - ɵɵelementEnd(); - ɵɵtemplate(2, MyComponent_div_Template_2, 2, 0, 'div', 0); - } - if (rf & RenderFlags.Update) { - ɵɵadvance(2); - ɵɵproperty('ngIf', ctx.visible); - } - } + template: + function(rf: RenderFlags, ctx: Comp) { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'div'); + ɵɵtext(1, 'Root view'); + ɵɵelementEnd(); + ɵɵtemplate(2, MyComponent_div_Template_2, 2, 0, 'div', 0); + } + if (rf & RenderFlags.Update) { + ɵɵadvance(2); + ɵɵproperty('ngIf', ctx.visible); + } + } }); } const rendererFactory = new MockRendererFactory(['destroy']); const fixture = new ComponentFixture(Comp, {rendererFactory}); - comp !.visible = false; + comp!.visible = false; fixture.update(); - comp !.visible = true; + comp!.visible = true; fixture.update(); - const renderer = rendererFactory.lastRenderer !; + const renderer = rendererFactory.lastRenderer!; const destroySpy = renderer.spies['destroy']; // we should never see `destroy` method being called @@ -246,7 +249,7 @@ describe('component with a container', () => { class WrapperComponent { // TODO(issue/24571): remove '!'. - items !: string[]; + items!: string[]; static ɵfac = () => new WrapperComponent; static ɵcmp = ɵɵdefineComponent({ type: WrapperComponent, @@ -254,20 +257,21 @@ describe('component with a container', () => { selectors: [['wrapper']], decls: 1, vars: 0, - template: function ChildComponentTemplate(rf: RenderFlags, ctx: {items: string[]}) { - if (rf & RenderFlags.Create) { - ɵɵcontainer(0); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(0); - { - const rf0 = ɵɵembeddedViewStart(0, 1, 0); - { showItems(rf0, {items: ctx.items}); } - ɵɵembeddedViewEnd(); - } - ɵɵcontainerRefreshEnd(); - } - }, + template: + function ChildComponentTemplate(rf: RenderFlags, ctx: {items: string[]}) { + if (rf & RenderFlags.Create) { + ɵɵcontainer(0); + } + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(0); + { + const rf0 = ɵɵembeddedViewStart(0, 1, 0); + { showItems(rf0, {items: ctx.items}); } + ɵɵembeddedViewEnd(); + } + ɵɵcontainerRefreshEnd(); + } + }, inputs: {items: 'items'} }); } @@ -320,9 +324,13 @@ describe('recursive components', () => { class TreeComponent { data: TreeNode = _buildTree(0); - ngDoCheck() { events.push('check' + this.data.value); } + ngDoCheck() { + events.push('check' + this.data.value); + } - ngOnDestroy() { events.push('destroy' + this.data.value); } + ngOnDestroy() { + events.push('destroy' + this.data.value); + } static ɵfac = () => new TreeComponent(); static ɵcmp = ɵɵdefineComponent({ @@ -331,46 +339,47 @@ describe('recursive components', () => { selectors: [['tree-comp']], decls: 3, vars: 1, - template: (rf: RenderFlags, ctx: TreeComponent) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0); - ɵɵcontainer(1); - ɵɵcontainer(2); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate(ctx.data.value); - ɵɵcontainerRefreshStart(1); - { - if (ctx.data.left != null) { - let rf0 = ɵɵembeddedViewStart(0, 1, 1); - if (rf0 & RenderFlags.Create) { - ɵɵelement(0, 'tree-comp'); - } - if (rf0 & RenderFlags.Update) { - ɵɵselect(0); - ɵɵproperty('data', ctx.data.left); - } - ɵɵembeddedViewEnd(); + template: + (rf: RenderFlags, ctx: TreeComponent) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0); + ɵɵcontainer(1); + ɵɵcontainer(2); } - } - ɵɵcontainerRefreshEnd(); - ɵɵcontainerRefreshStart(2); - { - if (ctx.data.right != null) { - let rf0 = ɵɵembeddedViewStart(0, 1, 1); - if (rf0 & RenderFlags.Create) { - ɵɵelement(0, 'tree-comp'); + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate(ctx.data.value); + ɵɵcontainerRefreshStart(1); + { + if (ctx.data.left != null) { + let rf0 = ɵɵembeddedViewStart(0, 1, 1); + if (rf0 & RenderFlags.Create) { + ɵɵelement(0, 'tree-comp'); + } + if (rf0 & RenderFlags.Update) { + ɵɵselect(0); + ɵɵproperty('data', ctx.data.left); + } + ɵɵembeddedViewEnd(); + } } - if (rf0 & RenderFlags.Update) { - ɵɵselect(0); - ɵɵproperty('data', ctx.data.right); + ɵɵcontainerRefreshEnd(); + ɵɵcontainerRefreshStart(2); + { + if (ctx.data.right != null) { + let rf0 = ɵɵembeddedViewStart(0, 1, 1); + if (rf0 & RenderFlags.Create) { + ɵɵelement(0, 'tree-comp'); + } + if (rf0 & RenderFlags.Update) { + ɵɵselect(0); + ɵɵproperty('data', ctx.data.right); + } + ɵɵembeddedViewEnd(); + } } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } - } - ɵɵcontainerRefreshEnd(); - } - }, + }, inputs: {data: 'data'} }); } @@ -385,9 +394,13 @@ describe('recursive components', () => { class NgIfTree { data: TreeNode = _buildTree(0); - ngDoCheck() { events.push('check' + this.data.value); } + ngDoCheck() { + events.push('check' + this.data.value); + } - ngOnDestroy() { events.push('destroy' + this.data.value); } + ngOnDestroy() { + events.push('destroy' + this.data.value); + } static ɵfac = () => new NgIfTree(); static ɵcmp = ɵɵdefineComponent({ @@ -397,20 +410,21 @@ describe('recursive components', () => { decls: 3, vars: 3, consts: [[AttributeMarker.Bindings, 'data', AttributeMarker.Template, 'ngIf']], - template: (rf: RenderFlags, ctx: NgIfTree) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0); - ɵɵtemplate(1, IfTemplate, 1, 1, 'ng-if-tree', 0); - ɵɵtemplate(2, IfTemplate2, 1, 1, 'ng-if-tree', 0); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate(ctx.data.value); - ɵɵadvance(1); - ɵɵproperty('ngIf', ctx.data.left); - ɵɵadvance(1); - ɵɵproperty('ngIf', ctx.data.right); - } - }, + template: + (rf: RenderFlags, ctx: NgIfTree) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0); + ɵɵtemplate(1, IfTemplate, 1, 1, 'ng-if-tree', 0); + ɵɵtemplate(2, IfTemplate2, 1, 1, 'ng-if-tree', 0); + } + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate(ctx.data.value); + ɵɵadvance(1); + ɵɵproperty('ngIf', ctx.data.left); + ɵɵadvance(1); + ɵɵproperty('ngIf', ctx.data.right); + } + }, inputs: {data: 'data'}, }); } @@ -457,7 +471,6 @@ describe('recursive components', () => { // This tests that the view tree is set up properly for recursive components it('should call onDestroys properly', () => { - /** * % if (!skipContent) { * <tree-comp></tree-comp> @@ -526,10 +539,10 @@ describe('recursive components', () => { ['destroy0', 'destroy1', 'destroy2', 'destroy3', 'destroy4', 'destroy5', 'destroy6']); }); - it('should map inputs minified & unminified names', async() => { + it('should map inputs minified & unminified names', async () => { class TestInputsComponent { // TODO(issue/24571): remove '!'. - minifiedName !: string; + minifiedName!: string; static ɵfac = () => new TestInputsComponent(); static ɵcmp = ɵɵdefineComponent({ type: TestInputsComponent, @@ -538,9 +551,10 @@ describe('recursive components', () => { inputs: {minifiedName: 'unminifiedName'}, decls: 0, vars: 0, - template: function(rf: RenderFlags, ctx: TestInputsComponent): void { - // Template not needed for this test - } + template: function(rf: RenderFlags, ctx: TestInputsComponent): + void { + // Template not needed for this test + } }); } @@ -549,7 +563,5 @@ describe('recursive components', () => { expect([ {propName: 'minifiedName', templateName: 'unminifiedName'} ]).toEqual(testInputsComponentFactory.inputs); - }); - }); diff --git a/packages/core/test/render3/control_flow_spec.ts b/packages/core/test/render3/control_flow_spec.ts index c401d42c9bccb..524d6a184ca48 100644 --- a/packages/core/test/render3/control_flow_spec.ts +++ b/packages/core/test/render3/control_flow_spec.ts @@ -10,7 +10,7 @@ import {ɵɵdefineComponent} from '../../src/render3/definition'; import {ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵselect, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all'; import {RenderFlags} from '../../src/render3/interfaces/definition'; -import {ComponentFixture, TemplateFixture, createComponent} from './render_util'; +import {ComponentFixture, createComponent, TemplateFixture} from './render_util'; describe('JS control flow', () => { it('should work with if block', () => { @@ -151,9 +151,10 @@ describe('JS control flow', () => { }); it('should work with nested adjacent if blocks', () => { - const ctx: {condition: boolean, - condition2: boolean, - condition3: boolean} = {condition: true, condition2: false, condition3: true}; + const ctx: + {condition: boolean, + condition2: boolean, + condition3: boolean} = {condition: true, condition2: false, condition3: true}; /** * % if(ctx.condition) { @@ -165,7 +166,9 @@ describe('JS control flow', () => { * % } * % } */ - function createTemplate() { ɵɵcontainer(0); } + function createTemplate() { + ɵɵcontainer(0); + } function updateTemplate() { ɵɵcontainerRefreshStart(0); @@ -174,7 +177,9 @@ describe('JS control flow', () => { let rf1 = ɵɵembeddedViewStart(1, 2, 0); { if (rf1 & RenderFlags.Create) { - { ɵɵcontainer(0); } + { + ɵɵcontainer(0); + } { ɵɵcontainer(1); } } if (rf1 & RenderFlags.Update) { @@ -222,14 +227,14 @@ describe('JS control flow', () => { it('should work with adjacent if blocks managing views in the same container', () => { /** - * % if(ctx.condition1) { - * 1 - * % }; if(ctx.condition2) { - * 2 - * % }; if(ctx.condition3) { - * 3 - * % } - */ + * % if(ctx.condition1) { + * 1 + * % }; if(ctx.condition2) { + * 2 + * % }; if(ctx.condition3) { + * 3 + * % } + */ const App = createComponent('app', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { ɵɵcontainer(0); @@ -539,15 +544,15 @@ describe('JS control flow', () => { * <div> * Before * % for (let i = 0; i < cafes.length; i++) { - * <h2> {{ cafes[i].name }} </h2> - * % for (let j = 0; j < cafes[i].entrees.length; j++) { - * <h3> {{ cafes[i].entrees[j].name }} </h3> - * % for (let k = 0; k < cafes[i].entrees[j].foods.length; k++) { - * {{ cafes[i].entrees[j].foods[k] }} - * % } - * % } - * - - * % } + * <h2> {{ cafes[i].name }} </h2> + * % for (let j = 0; j < cafes[i].entrees.length; j++) { + * <h3> {{ cafes[i].entrees[j].name }} </h3> + * % for (let k = 0; k < cafes[i].entrees[j].foods.length; k++) { + * {{ cafes[i].entrees[j].foods[k] }} + * % } + * % } + * - + * % } * After * <div> */ @@ -718,37 +723,38 @@ describe('JS control flow', () => { selectors: [['app']], decls: 3, vars: 0, - template: function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div'); - ɵɵcontainer(1); - ɵɵcontainer(2); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(1); - { - if (ctx.condition) { - let rf1 = ɵɵembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - ɵɵelement(0, 'comp'); - } - ɵɵembeddedViewEnd(); + template: + function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div'); + ɵɵcontainer(1); + ɵɵcontainer(2); } - } - ɵɵcontainerRefreshEnd(); - ɵɵcontainerRefreshStart(2); - { - if (ctx.condition2) { - let rf1 = ɵɵembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - ɵɵelement(0, 'comp'); + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(1); + { + if (ctx.condition) { + let rf1 = ɵɵembeddedViewStart(0, 1, 0); + if (rf1 & RenderFlags.Create) { + ɵɵelement(0, 'comp'); + } + ɵɵembeddedViewEnd(); + } } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); + ɵɵcontainerRefreshStart(2); + { + if (ctx.condition2) { + let rf1 = ɵɵembeddedViewStart(0, 1, 0); + if (rf1 & RenderFlags.Create) { + ɵɵelement(0, 'comp'); + } + ɵɵembeddedViewEnd(); + } + } + ɵɵcontainerRefreshEnd(); } - } - ɵɵcontainerRefreshEnd(); - } - }, + }, directives: () => [Comp] }); } @@ -788,37 +794,38 @@ describe('JS control flow', () => { selectors: [['app']], decls: 3, vars: 0, - template: function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div'); - ɵɵcontainer(1); - ɵɵcontainer(2); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(1); - { - if (ctx.condition) { - let rf1 = ɵɵembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - ɵɵelement(0, 'comp'); - } - ɵɵembeddedViewEnd(); + template: + function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div'); + ɵɵcontainer(1); + ɵɵcontainer(2); } - } - ɵɵcontainerRefreshEnd(); - ɵɵcontainerRefreshStart(2); - { - if (ctx.condition2) { - let rf1 = ɵɵembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - ɵɵelement(0, 'comp'); + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(1); + { + if (ctx.condition) { + let rf1 = ɵɵembeddedViewStart(0, 1, 0); + if (rf1 & RenderFlags.Create) { + ɵɵelement(0, 'comp'); + } + ɵɵembeddedViewEnd(); + } } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); + ɵɵcontainerRefreshStart(2); + { + if (ctx.condition2) { + let rf1 = ɵɵembeddedViewStart(0, 1, 0); + if (rf1 & RenderFlags.Create) { + ɵɵelement(0, 'comp'); + } + ɵɵembeddedViewEnd(); + } + } + ɵɵcontainerRefreshEnd(); } - } - ɵɵcontainerRefreshEnd(); - } - }, + }, directives: () => [Comp] }); } @@ -902,7 +909,7 @@ describe('function calls', () => { it('should work', () => { let data: string[] = ['foo', 'bar']; - function spanify(rf: RenderFlags, ctx: {message: string | null}) { + function spanify(rf: RenderFlags, ctx: {message: string|null}) { const message = ctx.message; if (rf & RenderFlags.Create) { ɵɵelementStart(0, 'span'); @@ -950,6 +957,5 @@ describe('function calls', () => { data = []; fixture.update(); expect(fixture.html).toEqual('<div>Before<span></span><span></span>After</div>'); - }); }); diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 1bcd79c5a193d..8a6857622887a 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -11,7 +11,7 @@ import {createLView, createTView, getOrCreateTNode} from '@angular/core/src/rend import {RenderFlags} from '@angular/core/src/render3/interfaces/definition'; import {ɵɵdefineComponent} from '../../src/render3/definition'; -import {bloomAdd, bloomHasToken, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjectorForNode} from '../../src/render3/di'; +import {bloomAdd, bloomHashBitOrFactory as bloomHash, bloomHasToken, getOrCreateNodeInjectorForNode} from '../../src/render3/di'; import {ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵtext} from '../../src/render3/index'; import {TNODE} from '../../src/render3/interfaces/injector'; import {TNodeType} from '../../src/render3/interfaces/node'; @@ -24,7 +24,6 @@ import {ComponentFixture, createComponent, createDirective} from './render_util' describe('di', () => { describe('directive injection', () => { - class DirB { value = 'DirB'; @@ -34,10 +33,9 @@ describe('di', () => { } describe('flags', () => { - class DirB { // TODO(issue/24571): remove '!'. - value !: string; + value!: string; static ɵfac = () => new DirB(); static ɵdir = @@ -62,7 +60,6 @@ describe('di', () => { beforeEach(() => dirA = null); it('should not throw if dependency is @Optional (limp mode)', () => { - /** <div dirA></div> */ const App = createComponent('app', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { @@ -70,8 +67,10 @@ describe('di', () => { } }, 1, 0, [DirA, DirB], [], undefined, [], [], undefined, [['dirA', '']]); - expect(() => { new ComponentFixture(App); }).not.toThrow(); - expect(dirA !.dirB).toEqual(null); + expect(() => { + new ComponentFixture(App); + }).not.toThrow(); + expect(dirA!.dirB).toEqual(null); }); }); @@ -123,11 +122,12 @@ describe('di', () => { selectors: [['my-comp']], decls: 1, vars: 0, - template: function(rf: RenderFlags, ctx: MyComp) { - if (rf & RenderFlags.Create) { - ɵɵtext(0, 'Foo'); - } - } + template: + function(rf: RenderFlags, ctx: MyComp) { + if (rf & RenderFlags.Create) { + ɵɵtext(0, 'Foo'); + } + } }); } @@ -137,8 +137,9 @@ describe('di', () => { expect(isProceduralRenderer(fixture.component.renderer)).toBeTruthy(); }); - it('should throw when injecting Renderer2 but the application is using Renderer3', - () => { expect(() => new ComponentFixture(MyComp)).toThrow(); }); + it('should throw when injecting Renderer2 but the application is using Renderer3', () => { + expect(() => new ComponentFixture(MyComp)).toThrow(); + }); }); describe('ɵɵinject', () => { @@ -148,7 +149,9 @@ describe('di', () => { mockTView = {data: [0, 0, 0, 0, 0, 0, 0, 0, null], firstCreatePass: true}; }); - function bloomState() { return mockTView.data.slice(0, TNODE).reverse(); } + function bloomState() { + return mockTView.data.slice(0, TNODE).reverse(); + } class Dir0 { /** @internal */ static __NG_ELEMENT_ID__ = 0; @@ -232,7 +235,7 @@ describe('di', () => { // Simulate the situation where the previous parent is not initialized. // This happens on first bootstrap because we don't init existing values // so that we have smaller HelloWorld. - (parentTNode as{parent: any}).parent = undefined; + (parentTNode as {parent: any}).parent = undefined; const injector = getOrCreateNodeInjectorForNode(parentTNode, contentView); expect(injector).not.toEqual(-1); @@ -241,5 +244,4 @@ describe('di', () => { } }); }); - }); diff --git a/packages/core/test/render3/global_utils_spec.ts b/packages/core/test/render3/global_utils_spec.ts index a0923bb02f82f..a95cb61d22f5b 100644 --- a/packages/core/test/render3/global_utils_spec.ts +++ b/packages/core/test/render3/global_utils_spec.ts @@ -25,30 +25,45 @@ describe('global utils', () => { describe('publishDefaultGlobalUtils', () => { beforeEach(() => publishDefaultGlobalUtils()); - it('should publish getComponent', () => { assertPublished('getComponent', getComponent); }); + it('should publish getComponent', () => { + assertPublished('getComponent', getComponent); + }); - it('should publish getContext', () => { assertPublished('getContext', getContext); }); + it('should publish getContext', () => { + assertPublished('getContext', getContext); + }); - it('should publish getListeners', () => { assertPublished('getListeners', getListeners); }); + it('should publish getListeners', () => { + assertPublished('getListeners', getListeners); + }); - it('should publish getOwningComponent', - () => { assertPublished('getOwningComponent', getOwningComponent); }); + it('should publish getOwningComponent', () => { + assertPublished('getOwningComponent', getOwningComponent); + }); - it('should publish getRootComponents', - () => { assertPublished('getRootComponents', getRootComponents); }); + it('should publish getRootComponents', () => { + assertPublished('getRootComponents', getRootComponents); + }); - it('should publish getDirectives', () => { assertPublished('getDirectives', getDirectives); }); + it('should publish getDirectives', () => { + assertPublished('getDirectives', getDirectives); + }); - it('should publish getHostComponent', - () => { assertPublished('getHostElement', getHostElement); }); + it('should publish getHostComponent', () => { + assertPublished('getHostElement', getHostElement); + }); - it('should publish getInjector', () => { assertPublished('getInjector', getInjector); }); + it('should publish getInjector', () => { + assertPublished('getInjector', getInjector); + }); - it('should publish applyChanges', () => { assertPublished('applyChanges', applyChanges); }); + it('should publish applyChanges', () => { + assertPublished('applyChanges', applyChanges); + }); }); }); -function assertPublished(name: string, value: {}) { +function assertPublished(name: string, value: Function) { const w = global as any as GlobalDevModeContainer; expect(w[GLOBAL_PUBLISH_EXPANDO_KEY][name]).toBe(value); } diff --git a/packages/core/test/render3/i18n_spec.ts b/packages/core/test/render3/i18n_spec.ts index 0535ce454bec5..1a2cb3d2eda51 100644 --- a/packages/core/test/render3/i18n_spec.ts +++ b/packages/core/test/render3/i18n_spec.ts @@ -15,7 +15,9 @@ import {getNativeByIndex} from '../../src/render3/util/view_utils'; import {TemplateFixture} from './render_util'; describe('Runtime i18n', () => { - afterEach(() => { setDelayProjection(false); }); + afterEach(() => { + setDelayProjection(false); + }); describe('getTranslationForTemplate', () => { it('should crop messages for the selected template', () => { let message = `simple text`; @@ -69,10 +71,12 @@ describe('Runtime i18n', () => { const MSG_DIV = `simple text`; const nbConsts = 1; const index = 0; - const opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV); + }, null, nbConsts, index); // Check debug - const debugOps = (opCodes as any).create.debug !.operations; + const debugOps = (opCodes as any).create.debug!.operations; expect(debugOps[0].__raw_opCode).toBe('simple text'); expect(debugOps[0].type).toBe('Create Text Node'); expect(debugOps[0].nodeIndex).toBe(1); @@ -100,7 +104,9 @@ describe('Runtime i18n', () => { const index = 1; const elementIndex = 2; const elementIndex2 = 3; - const opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV); + }, null, nbConsts, index); expect(opCodes).toEqual({ vars: 5, @@ -136,7 +142,9 @@ describe('Runtime i18n', () => { const MSG_DIV = `Hello �0�!`; const nbConsts = 2; const index = 1; - const opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV); + }, null, nbConsts, index); expect((opCodes as any).update.debug.operations).toEqual([ {__raw_opCode: 8, checkBit: 1, type: 'Text', nodeIndex: 2, text: 'Hello �0�!'} @@ -161,7 +169,9 @@ describe('Runtime i18n', () => { const MSG_DIV = `Hello �0� and �1�, again �0�!`; const nbConsts = 2; const index = 1; - const opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV); + }, null, nbConsts, index); expect(opCodes).toEqual({ vars: 1, @@ -195,7 +205,9 @@ describe('Runtime i18n', () => { let index = 1; const firstTextNode = 3; const rootTemplate = 2; - let opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV); }, null, nbConsts, index); + let opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV); + }, null, nbConsts, index); expect(opCodes).toEqual({ vars: 2, @@ -225,7 +237,9 @@ describe('Runtime i18n', () => { index = 0; const spanElement = 1; const bElementSubTemplate = 2; - opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV, 1); }, null, nbConsts, index); + opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV, 1); + }, null, nbConsts, index); expect(opCodes).toEqual({ vars: 2, @@ -252,7 +266,9 @@ describe('Runtime i18n', () => { nbConsts = 2; index = 0; const bElement = 1; - opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV, 2); }, null, nbConsts, index); + opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV, 2); + }, null, nbConsts, index); expect(opCodes).toEqual({ vars: 1, @@ -277,7 +293,9 @@ describe('Runtime i18n', () => { }`; const nbConsts = 1; const index = 0; - const opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV); + }, null, nbConsts, index); const tIcuIndex = 0; const icuCommentNodeIndex = index + 1; const firstTextNodeIndex = index + 2; @@ -465,7 +483,9 @@ describe('Runtime i18n', () => { }`; const nbConsts = 1; const index = 0; - const opCodes = getOpCodes(() => { ɵɵi18nStart(index, MSG_DIV); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nStart(index, MSG_DIV); + }, null, nbConsts, index); const icuCommentNodeIndex = index + 1; const firstTextNodeIndex = index + 2; const nestedIcuCommentNodeIndex = index + 3; @@ -497,18 +517,18 @@ describe('Runtime i18n', () => { cases: ['cat', 'dog', 'other'], create: [ [ - 'cats', nestedTextNodeIndex, nestedIcuCommentNodeIndex - << I18nMutateOpCode.SHIFT_PARENT | + 'cats', nestedTextNodeIndex, + nestedIcuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild ], [ - 'dogs', nestedTextNodeIndex, nestedIcuCommentNodeIndex - << I18nMutateOpCode.SHIFT_PARENT | + 'dogs', nestedTextNodeIndex, + nestedIcuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild ], [ - 'animals', nestedTextNodeIndex, nestedIcuCommentNodeIndex - << I18nMutateOpCode.SHIFT_PARENT | + 'animals', nestedTextNodeIndex, + nestedIcuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild ] ], @@ -599,8 +619,9 @@ describe('Runtime i18n', () => { const MSG_div_attr = ['title', MSG_title]; const nbConsts = 2; const index = 1; - const opCodes = - getOpCodes(() => { ɵɵi18nAttributes(index, MSG_div_attr); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nAttributes(index, MSG_div_attr); + }, null, nbConsts, index); expect(opCodes).toEqual([ 0b1, // bindings mask @@ -616,8 +637,9 @@ describe('Runtime i18n', () => { const MSG_div_attr = ['title', MSG_title]; const nbConsts = 2; const index = 1; - const opCodes = - getOpCodes(() => { ɵɵi18nAttributes(index, MSG_div_attr); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nAttributes(index, MSG_div_attr); + }, null, nbConsts, index); expect(opCodes).toEqual([ 0b11, // bindings mask @@ -632,8 +654,9 @@ describe('Runtime i18n', () => { const MSG_div_attr = ['title', MSG_title, 'aria-label', MSG_title]; const nbConsts = 2; const index = 1; - const opCodes = - getOpCodes(() => { ɵɵi18nAttributes(index, MSG_div_attr); }, null, nbConsts, index); + const opCodes = getOpCodes(() => { + ɵɵi18nAttributes(index, MSG_div_attr); + }, null, nbConsts, index); expect(opCodes).toEqual([ 0b1, // bindings mask diff --git a/packages/core/test/render3/imported_renderer2.ts b/packages/core/test/render3/imported_renderer2.ts index 07978d744629a..88d224e9deac4 100644 --- a/packages/core/test/render3/imported_renderer2.ts +++ b/packages/core/test/render3/imported_renderer2.ts @@ -17,9 +17,13 @@ import {EventManagerPlugin} from '@angular/platform-browser/src/dom/events/event import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; export class SimpleDomEventsPlugin extends EventManagerPlugin { - constructor(doc: any) { super(doc); } + constructor(doc: any) { + super(doc); + } - supports(eventName: string): boolean { return true; } + supports(eventName: string): boolean { + return true; + } addEventListener(element: HTMLElement, eventName: string, handler: Function): Function { let callback: EventListener = handler as EventListener; diff --git a/packages/core/test/render3/instructions/lview_debug_spec.ts b/packages/core/test/render3/instructions/lview_debug_spec.ts index 28d51c3002516..55f9d63a45ea6 100644 --- a/packages/core/test/render3/instructions/lview_debug_spec.ts +++ b/packages/core/test/render3/instructions/lview_debug_spec.ts @@ -21,13 +21,13 @@ describe('lView_debug', () => { afterEach(() => leaveView()); describe('TNode', () => { - let tNode !: TNodeDebug; - let tView !: TView; + let tNode!: TNodeDebug; + let tView!: TView; beforeEach(() => { tView = createTView(TViewType.Component, 0, null, 0, 0, null, null, null, null, null); - tNode = createTNode(tView, null !, TNodeType.Element, 0, '', null) as TNodeDebug; + tNode = createTNode(tView, null!, TNodeType.Element, 0, '', null) as TNodeDebug; }); - afterEach(() => tNode = tView = null !); + afterEach(() => tNode = tView = null!); describe('styling', () => { it('should decode no styling', () => { diff --git a/packages/core/test/render3/instructions/shared_spec.ts b/packages/core/test/render3/instructions/shared_spec.ts index 1649d8dfe8c6f..73c1e61e321d5 100644 --- a/packages/core/test/render3/instructions/shared_spec.ts +++ b/packages/core/test/render3/instructions/shared_spec.ts @@ -39,7 +39,7 @@ export function enterViewWithOneDiv() { TViewType.Component, -1, emptyTemplate, consts, vars, null, null, null, null, null); // Just assume that the expando starts after 10 initial bindings. tView.expandoStartIndex = HEADER_OFFSET + 10; - const tNode = tView.firstChild = createTNode(tView, null !, TNodeType.Element, 0, 'div', null); + const tNode = tView.firstChild = createTNode(tView, null!, TNodeType.Element, 0, 'div', null); const lView = createLView( null, tView, null, LViewFlags.CheckAlways, null, null, domRendererFactory3, renderer, null, null); diff --git a/packages/core/test/render3/instructions/styling_spec.ts b/packages/core/test/render3/instructions/styling_spec.ts index 22a09c4e68163..8751b15bff1d1 100644 --- a/packages/core/test/render3/instructions/styling_spec.ts +++ b/packages/core/test/render3/instructions/styling_spec.ts @@ -1,16 +1,16 @@ /** - * @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 - */ + * @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 {DirectiveDef} from '@angular/core/src/render3'; import {ɵɵdefineDirective} from '@angular/core/src/render3/definition'; import {classStringParser, styleStringParser, toStylingKeyValueArray, ɵɵclassProp, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer} from '@angular/core/src/render3/instructions/styling'; import {AttributeMarker, TAttributes} from '@angular/core/src/render3/interfaces/node'; -import {StylingRange, TStylingKey, TStylingRange, getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, setTStylingRangeNext, setTStylingRangePrev, toTStylingRange} from '@angular/core/src/render3/interfaces/styling'; +import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, setTStylingRangeNext, setTStylingRangePrev, StylingRange, toTStylingRange, TStylingKey, TStylingRange} from '@angular/core/src/render3/interfaces/styling'; import {HEADER_OFFSET, TVIEW} from '@angular/core/src/render3/interfaces/view'; import {getLView, leaveView, setBindingRootForHostBindings} from '@angular/core/src/render3/state'; import {getNativeByIndex} from '@angular/core/src/render3/util/view_utils'; @@ -27,7 +27,7 @@ describe('styling', () => { beforeEach(enterViewWithOneDiv); afterEach(leaveView); - let div !: HTMLElement; + let div!: HTMLElement; beforeEach(() => div = getNativeByIndex(0, getLView()) as HTMLElement); it('should do set basic style', () => { @@ -68,7 +68,7 @@ describe('styling', () => { ɵɵstyleProp('color', 'blue'); // Higher priority, should win. expectStyle(div).toEqual({color: 'blue'}); // The intermediate value has to set since it does not know if it is last one. - expect(ngDevMode !.rendererSetStyle).toEqual(2); + expect(ngDevMode!.rendererSetStyle).toEqual(2); ngDevModeResetPerfCounters(); clearFirstUpdatePass(); @@ -76,34 +76,34 @@ describe('styling', () => { ɵɵstyleProp('color', 'red'); // no change ɵɵstyleProp('color', 'green'); // change to green expectStyle(div).toEqual({color: 'green'}); - expect(ngDevMode !.rendererSetStyle).toEqual(1); + expect(ngDevMode!.rendererSetStyle).toEqual(1); ngDevModeResetPerfCounters(); rewindBindingIndex(); ɵɵstyleProp('color', 'black'); // First binding update expectStyle(div).toEqual({color: 'green'}); // Green still has priority. - expect(ngDevMode !.rendererSetStyle).toEqual(0); + expect(ngDevMode!.rendererSetStyle).toEqual(0); ngDevModeResetPerfCounters(); rewindBindingIndex(); ɵɵstyleProp('color', 'black'); // no change ɵɵstyleProp('color', undefined); // Clear second binding expectStyle(div).toEqual({color: 'black'}); // default to first binding - expect(ngDevMode !.rendererSetStyle).toEqual(1); + expect(ngDevMode!.rendererSetStyle).toEqual(1); ngDevModeResetPerfCounters(); rewindBindingIndex(); ɵɵstyleProp('color', null); expectStyle(div).toEqual({}); // default to first binding - expect(ngDevMode !.rendererSetStyle).toEqual(0); - expect(ngDevMode !.rendererRemoveStyle).toEqual(1); + expect(ngDevMode!.rendererSetStyle).toEqual(0); + expect(ngDevMode!.rendererRemoveStyle).toEqual(1); }); it('should set class based on priority', () => { ɵɵclassProp('foo', false); ɵɵclassProp('foo', true); // Higher priority, should win. expectClass(div).toEqual({foo: true}); - expect(ngDevMode !.rendererAddClass).toEqual(1); + expect(ngDevMode!.rendererAddClass).toEqual(1); ngDevModeResetPerfCounters(); clearFirstUpdatePass(); @@ -111,8 +111,8 @@ describe('styling', () => { ɵɵclassProp('foo', false); // no change ɵɵclassProp('foo', undefined); // change (have no opinion) expectClass(div).toEqual({}); - expect(ngDevMode !.rendererAddClass).toEqual(0); - expect(ngDevMode !.rendererRemoveClass).toEqual(1); + expect(ngDevMode!.rendererAddClass).toEqual(0); + expect(ngDevMode!.rendererRemoveClass).toEqual(1); ngDevModeResetPerfCounters(); rewindBindingIndex(); @@ -130,8 +130,8 @@ describe('styling', () => { it('should work with maps', () => { ɵɵstyleMap({}); expectStyle(div).toEqual({}); - expect(ngDevMode !.rendererSetStyle).toEqual(0); - expect(ngDevMode !.rendererRemoveStyle).toEqual(0); + expect(ngDevMode!.rendererSetStyle).toEqual(0); + expect(ngDevMode!.rendererRemoveStyle).toEqual(0); ngDevModeResetPerfCounters(); clearFirstUpdatePass(); @@ -139,30 +139,30 @@ describe('styling', () => { rewindBindingIndex(); ɵɵstyleMap({color: 'blue'}); expectStyle(div).toEqual({color: 'blue'}); - expect(ngDevMode !.rendererSetStyle).toEqual(1); - expect(ngDevMode !.rendererRemoveStyle).toEqual(0); + expect(ngDevMode!.rendererSetStyle).toEqual(1); + expect(ngDevMode!.rendererRemoveStyle).toEqual(0); ngDevModeResetPerfCounters(); rewindBindingIndex(); ɵɵstyleMap({color: 'red'}); expectStyle(div).toEqual({color: 'red'}); - expect(ngDevMode !.rendererSetStyle).toEqual(1); - expect(ngDevMode !.rendererRemoveStyle).toEqual(0); + expect(ngDevMode!.rendererSetStyle).toEqual(1); + expect(ngDevMode!.rendererRemoveStyle).toEqual(0); ngDevModeResetPerfCounters(); rewindBindingIndex(); ɵɵstyleMap({color: null, width: '100px'}); expectStyle(div).toEqual({width: '100px'}); - expect(ngDevMode !.rendererSetStyle).toEqual(1); - expect(ngDevMode !.rendererRemoveStyle).toEqual(1); + expect(ngDevMode!.rendererSetStyle).toEqual(1); + expect(ngDevMode!.rendererRemoveStyle).toEqual(1); ngDevModeResetPerfCounters(); }); it('should work with object literal and strings', () => { ɵɵstyleMap(''); expectStyle(div).toEqual({}); - expect(ngDevMode !.rendererSetStyle).toEqual(0); - expect(ngDevMode !.rendererRemoveStyle).toEqual(0); + expect(ngDevMode!.rendererSetStyle).toEqual(0); + expect(ngDevMode!.rendererRemoveStyle).toEqual(0); ngDevModeResetPerfCounters(); clearFirstUpdatePass(); @@ -170,22 +170,22 @@ describe('styling', () => { rewindBindingIndex(); ɵɵstyleMap('color: blue'); expectStyle(div).toEqual({color: 'blue'}); - expect(ngDevMode !.rendererSetStyle).toEqual(1); - expect(ngDevMode !.rendererRemoveStyle).toEqual(0); + expect(ngDevMode!.rendererSetStyle).toEqual(1); + expect(ngDevMode!.rendererRemoveStyle).toEqual(0); ngDevModeResetPerfCounters(); rewindBindingIndex(); ɵɵstyleMap('color: red'); expectStyle(div).toEqual({color: 'red'}); - expect(ngDevMode !.rendererSetStyle).toEqual(1); - expect(ngDevMode !.rendererRemoveStyle).toEqual(0); + expect(ngDevMode!.rendererSetStyle).toEqual(1); + expect(ngDevMode!.rendererRemoveStyle).toEqual(0); ngDevModeResetPerfCounters(); rewindBindingIndex(); ɵɵstyleMap('width: 100px'); expectStyle(div).toEqual({width: '100px'}); - expect(ngDevMode !.rendererSetStyle).toEqual(1); - expect(ngDevMode !.rendererRemoveStyle).toEqual(1); + expect(ngDevMode!.rendererSetStyle).toEqual(1); + expect(ngDevMode!.rendererRemoveStyle).toEqual(1); ngDevModeResetPerfCounters(); }); @@ -395,7 +395,6 @@ describe('styling', () => { ['color', 'red', 'content', '"TEMPLATE"'] ]); expectStyle(div).toEqual({content: '"TEMPLATE"', color: 'red', width: '100px'}); - }); }); }); @@ -404,23 +403,23 @@ describe('styling', () => { describe('toStylingArray', () => { describe('falsy', () => { it('should return empty KeyValueArray', () => { - expect(toStylingKeyValueArray(keyValueArraySet, null !, '')).toEqual([] as any); - expect(toStylingKeyValueArray(keyValueArraySet, null !, null)).toEqual([] as any); - expect(toStylingKeyValueArray(keyValueArraySet, null !, undefined)).toEqual([] as any); - expect(toStylingKeyValueArray(keyValueArraySet, null !, [])).toEqual([] as any); - expect(toStylingKeyValueArray(keyValueArraySet, null !, {})).toEqual([] as any); + expect(toStylingKeyValueArray(keyValueArraySet, null!, '')).toEqual([] as any); + expect(toStylingKeyValueArray(keyValueArraySet, null!, null)).toEqual([] as any); + expect(toStylingKeyValueArray(keyValueArraySet, null!, undefined)).toEqual([] as any); + expect(toStylingKeyValueArray(keyValueArraySet, null!, [])).toEqual([] as any); + expect(toStylingKeyValueArray(keyValueArraySet, null!, {})).toEqual([] as any); }); describe('string', () => { it('should parse classes', () => { - expect(toStylingKeyValueArray(keyValueArraySet, classStringParser, ' ')).toEqual([ - ] as any); + expect(toStylingKeyValueArray(keyValueArraySet, classStringParser, ' ')) + .toEqual([] as any); expect(toStylingKeyValueArray(keyValueArraySet, classStringParser, ' X A ')).toEqual([ 'A', true, 'X', true ] as any); }); it('should parse styles', () => { - expect(toStylingKeyValueArray(keyValueArraySet, styleStringParser, ' ')).toEqual([ - ] as any); + expect(toStylingKeyValueArray(keyValueArraySet, styleStringParser, ' ')) + .toEqual([] as any); expect(toStylingKeyValueArray(keyValueArraySet, styleStringParser, 'B:b;A:a')).toEqual([ 'A', 'a', 'B', 'b' ] as any); @@ -428,14 +427,14 @@ describe('styling', () => { }); describe('array', () => { it('should parse', () => { - expect(toStylingKeyValueArray(keyValueArraySet, null !, ['X', 'A'])).toEqual([ + expect(toStylingKeyValueArray(keyValueArraySet, null!, ['X', 'A'])).toEqual([ 'A', true, 'X', true ] as any); }); }); describe('object', () => { it('should parse', () => { - expect(toStylingKeyValueArray(keyValueArraySet, null !, {X: 'x', A: 'a'})).toEqual([ + expect(toStylingKeyValueArray(keyValueArraySet, null!, {X: 'x', A: 'a'})).toEqual([ 'A', 'a', 'X', 'x' ] as any); }); @@ -495,7 +494,7 @@ function givenTemplateAttrs(tAttrs: TAttributes) { } function getTNode() { - return getLView()[TVIEW].firstChild !; + return getLView()[TVIEW].firstChild!; } function getTData() { @@ -517,7 +516,7 @@ function givenDirectiveAttrs(tAttrs: TAttributes[]) { } } -function applyTAttributes(attrs: TAttributes | null) { +function applyTAttributes(attrs: TAttributes|null) { if (attrs === null) return; const div: HTMLElement = getLView()[HEADER_OFFSET]; let mode: AttributeMarker = AttributeMarker.ImplicitAttributes; @@ -553,12 +552,12 @@ function getTDataIndexFromDirectiveIndex(index: number) { return HEADER_OFFSET + index + 10; // offset to give template bindings space. } -function expectTStylingKeys(styling: 'style' | 'class') { +function expectTStylingKeys(styling: 'style'|'class') { const tNode = getTNode(); const tData = getTData(); const isClassBased = styling === 'class'; const headIndex = getTStylingRangePrev(isClassBased ? tNode.classBindings : tNode.styleBindings); - const tStylingKeys: (string | (null | string)[] | null)[] = []; + const tStylingKeys: (string|(null | string)[]|null)[] = []; let index = headIndex; let prevIndex = index; // rewind to beginning of list. diff --git a/packages/core/test/render3/instructions_spec.ts b/packages/core/test/render3/instructions_spec.ts index 09139881ad48c..3f90a06cda9e9 100644 --- a/packages/core/test/render3/instructions_spec.ts +++ b/packages/core/test/render3/instructions_spec.ts @@ -12,7 +12,7 @@ import {getSortedClassName} from '@angular/core/testing/src/styling'; import {ɵɵdefineComponent} from '../../src/render3/definition'; import {RenderFlags, ɵɵattribute, ɵɵclassMap, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵproperty, ɵɵselect, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer, ɵɵtemplate, ɵɵtext, ɵɵtextInterpolate1} from '../../src/render3/index'; import {AttributeMarker} from '../../src/render3/interfaces/node'; -import {SafeValue, bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, getSanitizationBypassType, unwrapSafeValue} from '../../src/sanitization/bypass'; +import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, getSanitizationBypassType, SafeValue, unwrapSafeValue} from '../../src/sanitization/bypass'; import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization'; import {Sanitizer} from '../../src/sanitization/sanitizer'; import {SecurityContext} from '../../src/sanitization/security'; @@ -21,18 +21,32 @@ import {NgForOf} from './common_with_def'; import {ComponentFixture, TemplateFixture} from './render_util'; describe('instructions', () => { - function createAnchor() { ɵɵelement(0, 'a'); } + function createAnchor() { + ɵɵelement(0, 'a'); + } - function createDiv() { ɵɵelement(0, 'div', 0); } + function createDiv() { + ɵɵelement(0, 'div', 0); + } - function createScript() { ɵɵelement(0, 'script'); } + function createScript() { + ɵɵelement(0, 'script'); + } describe('ɵɵselect', () => { it('should error in DevMode if index is out of range', () => { // Only one constant added, meaning only index `0` is valid. const t = new TemplateFixture(createDiv, () => {}, 1, 0); - expect(() => { t.update(() => { ɵɵselect(-1); }); }).toThrow(); - expect(() => { t.update(() => { ɵɵselect(1); }); }).toThrow(); + expect(() => { + t.update(() => { + ɵɵselect(-1); + }); + }).toThrow(); + expect(() => { + t.update(() => { + ɵɵselect(1); + }); + }).toThrow(); }); }); @@ -40,10 +54,14 @@ describe('instructions', () => { it('should update bindings when value changes with the correct perf counters', () => { const t = new TemplateFixture(createAnchor, () => {}, 1, 1); - t.update(() => { ɵɵproperty('title', 'Hello'); }); + t.update(() => { + ɵɵproperty('title', 'Hello'); + }); expect(t.html).toEqual('<a title="Hello"></a>'); - t.update(() => { ɵɵproperty('title', 'World'); }); + t.update(() => { + ɵɵproperty('title', 'World'); + }); expect(t.html).toEqual('<a title="World"></a>'); expect(ngDevMode).toHaveProperties({ firstCreatePass: 1, @@ -56,7 +74,9 @@ describe('instructions', () => { it('should not update bindings when value does not change, with the correct perf counters', () => { - const idempotentUpdate = () => { ɵɵproperty('title', 'Hello'); }; + const idempotentUpdate = () => { + ɵɵproperty('title', 'Hello'); + }; const t = new TemplateFixture(createAnchor, idempotentUpdate, 1, 1); t.update(); @@ -80,7 +100,7 @@ describe('instructions', () => { ɵɵelement(0, 'div', 0); }, () => {}, 1, 0, null, null, null, undefined, [['id', 'test', 'title', 'Hello']]); - const div = (t.hostElement as HTMLElement).querySelector('div') !; + const div = (t.hostElement as HTMLElement).querySelector('div')!; expect(div.id).toEqual('test'); expect(div.title).toEqual('Hello'); expect(ngDevMode).toHaveProperties({ @@ -96,7 +116,9 @@ describe('instructions', () => { it('should use sanitizer function', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 1); - t.update(() => { ɵɵattribute('title', 'javascript:true', ɵɵsanitizeUrl); }); + t.update(() => { + ɵɵattribute('title', 'javascript:true', ɵɵsanitizeUrl); + }); expect(t.html).toEqual('<div title="unsafe:javascript:true"></div>'); t.update(() => { @@ -122,9 +144,13 @@ describe('instructions', () => { it('should chain', () => { // <div [title]="title" [accesskey]="key"></div> const t = new TemplateFixture(createDiv, () => {}, 1, 2); - t.update(() => { ɵɵproperty('title', 'one')('accessKey', 'A'); }); + t.update(() => { + ɵɵproperty('title', 'one')('accessKey', 'A'); + }); expect(t.html).toEqual('<div accesskey="A" title="one"></div>'); - t.update(() => { ɵɵproperty('title', 'two')('accessKey', 'B'); }); + t.update(() => { + ɵɵproperty('title', 'two')('accessKey', 'B'); + }); expect(t.html).toEqual('<div accesskey="B" title="two"></div>'); expect(ngDevMode).toHaveProperties({ firstCreatePass: 1, @@ -140,7 +166,9 @@ describe('instructions', () => { it('should automatically sanitize unless a bypass operation is applied', () => { let backgroundImage: string|SafeValue = 'url("http://server")'; const t = new TemplateFixture( - () => { return createDiv(); }, + () => { + return createDiv(); + }, () => { ɵɵstyleSanitizer(ɵɵdefaultStyleSanitizer); ɵɵstyleProp('background-image', backgroundImage); @@ -160,7 +188,9 @@ describe('instructions', () => { describe('styleMap', () => { const attrs = [[AttributeMarker.Styles, 'height', '10px']]; - function createDivWithStyle() { ɵɵelement(0, 'div', 0); } + function createDivWithStyle() { + ɵɵelement(0, 'div', 0); + } it('should add style', () => { const fixture = new TemplateFixture(createDivWithStyle, () => { @@ -172,10 +202,13 @@ describe('instructions', () => { it('should sanitize new styles that may contain `url` properties', () => { const detectedValues: string[] = []; - const sanitizerInterceptor = - new MockSanitizerInterceptor(value => { detectedValues.push(value); }); + const sanitizerInterceptor = new MockSanitizerInterceptor(value => { + detectedValues.push(value); + }); const fixture = new TemplateFixture( - () => { return createDiv(); }, // + () => { + return createDiv(); + }, // () => { ɵɵstyleSanitizer(sanitizerInterceptor.getStyleSanitizer()); ɵɵstyleMap({ @@ -198,12 +231,15 @@ describe('instructions', () => { }); describe('elementClass', () => { - function createDivWithStyling() { ɵɵelement(0, 'div'); } + function createDivWithStyling() { + ɵɵelement(0, 'div'); + } it('should add class', () => { - const fixture = new TemplateFixture( - createDivWithStyling, () => { ɵɵclassMap('multiple classes'); }, 1, 2); - const div = fixture.containerElement.querySelector('div.multiple') !; + const fixture = new TemplateFixture(createDivWithStyling, () => { + ɵɵclassMap('multiple classes'); + }, 1, 2); + const div = fixture.containerElement.querySelector('div.multiple')!; expect(getSortedClassName(div)).toEqual('classes multiple'); }); }); @@ -246,21 +282,24 @@ describe('instructions', () => { class NestedLoops { rows = [['a', 'b'], ['A', 'B'], ['a', 'b'], ['A', 'B']]; - static ɵfac = function ToDoAppComponent_Factory() { return new NestedLoops(); }; + static ɵfac = function ToDoAppComponent_Factory() { + return new NestedLoops(); + }; static ɵcmp = ɵɵdefineComponent({ type: NestedLoops, selectors: [['nested-loops']], decls: 1, vars: 1, consts: [[AttributeMarker.Template, 'ngFor', 'ngForOf']], - template: function ToDoAppComponent_Template(rf: RenderFlags, ctx: NestedLoops) { - if (rf & RenderFlags.Create) { - ɵɵtemplate(0, ToDoAppComponent_NgForOf_Template_0, 2, 1, 'ul', 0); - } - if (rf & RenderFlags.Update) { - ɵɵproperty('ngForOf', ctx.rows); - } - }, + template: + function ToDoAppComponent_Template(rf: RenderFlags, ctx: NestedLoops) { + if (rf & RenderFlags.Create) { + ɵɵtemplate(0, ToDoAppComponent_NgForOf_Template_0, 2, 1, 'ul', 0); + } + if (rf & RenderFlags.Update) { + ɵɵproperty('ngForOf', ctx.rows); + } + }, directives: [NgForOf] }); } @@ -269,7 +308,6 @@ describe('instructions', () => { // Expect: fixture view/Host view + component + ngForRow + ngForCol tView: 4, // should be: 4, }); - }); }); @@ -280,7 +318,9 @@ describe('instructions', () => { const inputValue = 'http://foo'; const outputValue = 'http://foo-sanitized'; - t.update(() => { ɵɵattribute('href', inputValue, ɵɵsanitizeUrl); }); + t.update(() => { + ɵɵattribute('href', inputValue, ɵɵsanitizeUrl); + }); expect(t.html).toEqual(`<a href="${outputValue}"></a>`); expect(s.lastSanitizedValue).toEqual(outputValue); }); @@ -291,7 +331,9 @@ describe('instructions', () => { const inputValue = s.bypassSecurityTrustUrl('http://foo'); const outputValue = 'http://foo'; - t.update(() => { ɵɵattribute('href', inputValue, ɵɵsanitizeUrl); }); + t.update(() => { + ɵɵattribute('href', inputValue, ɵɵsanitizeUrl); + }); expect(t.html).toEqual(`<a href="${outputValue}"></a>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -302,7 +344,9 @@ describe('instructions', () => { const inputValue = bypassSanitizationTrustUrl('http://foo'); const outputValue = 'http://foo-ivy'; - t.update(() => { ɵɵattribute('href', inputValue, ɵɵsanitizeUrl); }); + t.update(() => { + ɵɵattribute('href', inputValue, ɵɵsanitizeUrl); + }); expect(t.html).toEqual(`<a href="${outputValue}"></a>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -313,7 +357,9 @@ describe('instructions', () => { const inputValue = 'color:red'; const outputValue = 'color:blue'; - t.update(() => { ɵɵattribute('style', inputValue, ɵɵsanitizeStyle); }); + t.update(() => { + ɵɵattribute('style', inputValue, ɵɵsanitizeStyle); + }); expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`); expect(s.lastSanitizedValue).toEqual(outputValue); }); @@ -324,7 +370,9 @@ describe('instructions', () => { const inputValue = s.bypassSecurityTrustStyle('color:maroon'); const outputValue = 'color:maroon'; - t.update(() => { ɵɵattribute('style', inputValue, ɵɵsanitizeStyle); }); + t.update(() => { + ɵɵattribute('style', inputValue, ɵɵsanitizeStyle); + }); expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -335,7 +383,9 @@ describe('instructions', () => { const inputValue = bypassSanitizationTrustStyle('font-family:foo'); const outputValue = 'font-family:foo-ivy'; - t.update(() => { ɵɵattribute('style', inputValue, ɵɵsanitizeStyle); }); + t.update(() => { + ɵɵattribute('style', inputValue, ɵɵsanitizeStyle); + }); expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -346,7 +396,9 @@ describe('instructions', () => { const inputValue = 'http://resource'; const outputValue = 'http://resource-sanitized'; - t.update(() => { ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl); }); + t.update(() => { + ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl); + }); expect(t.html).toEqual(`<script src="${outputValue}"></script>`); expect(s.lastSanitizedValue).toEqual(outputValue); }); @@ -357,7 +409,9 @@ describe('instructions', () => { const inputValue = s.bypassSecurityTrustResourceUrl('file://all-my-secrets.pdf'); const outputValue = 'file://all-my-secrets.pdf'; - t.update(() => { ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl); }); + t.update(() => { + ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl); + }); expect(t.html).toEqual(`<script src="${outputValue}"></script>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -368,7 +422,9 @@ describe('instructions', () => { const inputValue = bypassSanitizationTrustResourceUrl('file://all-my-secrets.pdf'); const outputValue = 'file://all-my-secrets.pdf-ivy'; - t.update(() => { ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl); }); + t.update(() => { + ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl); + }); expect(t.html).toEqual(`<script src="${outputValue}"></script>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -379,7 +435,9 @@ describe('instructions', () => { const inputValue = 'fn();'; const outputValue = 'fn(); //sanitized'; - t.update(() => { ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeScript); }); + t.update(() => { + ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeScript); + }); expect(t.html).toEqual(`<script>${outputValue}</script>`); expect(s.lastSanitizedValue).toEqual(outputValue); }); @@ -390,7 +448,9 @@ describe('instructions', () => { const inputValue = s.bypassSecurityTrustScript('alert("bar")'); const outputValue = 'alert("bar")'; - t.update(() => { ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeScript); }); + t.update(() => { + ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeScript); + }); expect(t.html).toEqual(`<script>${outputValue}</script>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -401,7 +461,9 @@ describe('instructions', () => { const inputValue = bypassSanitizationTrustScript('alert("bar")'); const outputValue = 'alert("bar")-ivy'; - t.update(() => { ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeScript); }); + t.update(() => { + ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeScript); + }); expect(t.html).toEqual(`<script>${outputValue}</script>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -412,7 +474,9 @@ describe('instructions', () => { const inputValue = '<header></header>'; const outputValue = '<header></header> <!--sanitized-->'; - t.update(() => { ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeHtml); }); + t.update(() => { + ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeHtml); + }); expect(t.html).toEqual(`<div>${outputValue}</div>`); expect(s.lastSanitizedValue).toEqual(outputValue); }); @@ -423,7 +487,9 @@ describe('instructions', () => { const inputValue = s.bypassSecurityTrustHtml('<div onclick="alert(123)"></div>'); const outputValue = '<div onclick="alert(123)"></div>'; - t.update(() => { ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeHtml); }); + t.update(() => { + ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeHtml); + }); expect(t.html).toEqual(`<div>${outputValue}</div>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -434,7 +500,9 @@ describe('instructions', () => { const inputValue = bypassSanitizationTrustHtml('<div onclick="alert(123)"></div>'); const outputValue = '<div onclick="alert(123)"></div>-ivy'; - t.update(() => { ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeHtml); }); + t.update(() => { + ɵɵproperty('innerHTML', inputValue, ɵɵsanitizeHtml); + }); expect(t.html).toEqual(`<div>${outputValue}</div>`); expect(s.lastSanitizedValue).toBeFalsy(); }); @@ -444,12 +512,14 @@ describe('instructions', () => { class LocalSanitizedValue { constructor(public value: any) {} - toString() { return this.value; } + toString() { + return this.value; + } } class LocalMockSanitizer implements Sanitizer { // TODO(issue/24571): remove '!'. - public lastSanitizedValue !: string | null; + public lastSanitizedValue!: string|null; constructor(private _interceptor: (value: string|null|any) => string) {} @@ -465,21 +535,33 @@ class LocalMockSanitizer implements Sanitizer { return this.lastSanitizedValue = this._interceptor(value); } - bypassSecurityTrustHtml(value: string) { return new LocalSanitizedValue(value); } + bypassSecurityTrustHtml(value: string) { + return new LocalSanitizedValue(value); + } - bypassSecurityTrustStyle(value: string) { return new LocalSanitizedValue(value); } + bypassSecurityTrustStyle(value: string) { + return new LocalSanitizedValue(value); + } - bypassSecurityTrustScript(value: string) { return new LocalSanitizedValue(value); } + bypassSecurityTrustScript(value: string) { + return new LocalSanitizedValue(value); + } - bypassSecurityTrustUrl(value: string) { return new LocalSanitizedValue(value); } + bypassSecurityTrustUrl(value: string) { + return new LocalSanitizedValue(value); + } - bypassSecurityTrustResourceUrl(value: string) { return new LocalSanitizedValue(value); } + bypassSecurityTrustResourceUrl(value: string) { + return new LocalSanitizedValue(value); + } } class MockSanitizerInterceptor { public lastValue: string|null = null; constructor(private _interceptorFn?: ((value: any) => any)|null) {} - getStyleSanitizer() { return ɵɵdefaultStyleSanitizer; } + getStyleSanitizer() { + return ɵɵdefaultStyleSanitizer; + } sanitize(context: SecurityContext, value: LocalSanitizedValue|string|null|any): string|null { if (this._interceptorFn) { this._interceptorFn(value); diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index cba57eb192ab1..055c91a2496cd 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -12,7 +12,7 @@ import {AttributeMarker, ɵɵadvance, ɵɵattribute, ɵɵdefineComponent, ɵɵde import {ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵprojection, ɵɵprojectionDef, ɵɵtemplate, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all'; import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context'; import {RenderFlags} from '../../src/render3/interfaces/definition'; -import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; +import {domRendererFactory3, RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer'; import {CONTEXT, HEADER_OFFSET} from '../../src/render3/interfaces/view'; import {ɵɵsanitizeUrl} from '../../src/sanitization/sanitization'; import {Sanitizer} from '../../src/sanitization/sanitizer'; @@ -22,7 +22,6 @@ import {NgIf} from './common_with_def'; import {ComponentFixture, MockRendererFactory, renderToHtml} from './render_util'; describe('render3 integration test', () => { - describe('render', () => { describe('text bindings', () => { it('should support creation-time values in text nodes', () => { @@ -56,7 +55,7 @@ describe('render3 integration test', () => { afterTree: Tree; } - function showLabel(rf: RenderFlags, ctx: {label: string | undefined}) { + function showLabel(rf: RenderFlags, ctx: {label: string|undefined}) { if (rf & RenderFlags.Create) { ɵɵcontainer(0); } @@ -113,9 +112,9 @@ describe('render3 integration test', () => { class ChildComponent { // TODO(issue/24571): remove '!'. - beforeTree !: Tree; + beforeTree!: Tree; // TODO(issue/24571): remove '!'. - afterTree !: Tree; + afterTree!: Tree; static ɵfac = () => new ChildComponent; static ɵcmp = ɵɵdefineComponent({ @@ -123,31 +122,32 @@ describe('render3 integration test', () => { type: ChildComponent, decls: 3, vars: 0, - template: function ChildComponentTemplate( - rf: RenderFlags, ctx: {beforeTree: Tree, afterTree: Tree}) { - if (rf & RenderFlags.Create) { - ɵɵprojectionDef(); - ɵɵcontainer(0); - ɵɵprojection(1); - ɵɵcontainer(2); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(0); - { - const rf0 = ɵɵembeddedViewStart(0, 3, 0); - { showTree(rf0, {tree: ctx.beforeTree}); } - ɵɵembeddedViewEnd(); - } - ɵɵcontainerRefreshEnd(); - ɵɵcontainerRefreshStart(2); - { - const rf0 = ɵɵembeddedViewStart(0, 3, 0); - { showTree(rf0, {tree: ctx.afterTree}); } - ɵɵembeddedViewEnd(); - } - ɵɵcontainerRefreshEnd(); - } - }, + template: + function ChildComponentTemplate( + rf: RenderFlags, ctx: {beforeTree: Tree, afterTree: Tree}) { + if (rf & RenderFlags.Create) { + ɵɵprojectionDef(); + ɵɵcontainer(0); + ɵɵprojection(1); + ɵɵcontainer(2); + } + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(0); + { + const rf0 = ɵɵembeddedViewStart(0, 3, 0); + { showTree(rf0, {tree: ctx.beforeTree}); } + ɵɵembeddedViewEnd(); + } + ɵɵcontainerRefreshEnd(); + ɵɵcontainerRefreshStart(2); + { + const rf0 = ɵɵembeddedViewStart(0, 3, 0); + { showTree(rf0, {tree: ctx.afterTree}); } + ɵɵembeddedViewEnd(); + } + ɵɵcontainerRefreshEnd(); + } + }, inputs: {beforeTree: 'beforeTree', afterTree: 'afterTree'} }); } @@ -172,7 +172,6 @@ describe('render3 integration test', () => { } it('should work with a tree', () => { - const ctx: ParentCtx = { beforeTree: {subTrees: [{beforeLabel: 'a'}]}, projectedTree: {beforeLabel: 'p'}, @@ -181,19 +180,17 @@ describe('render3 integration test', () => { const defs = [ChildComponent]; expect(renderToHtml(parentTemplate, ctx, 2, 2, defs)).toEqual('<child>apz</child>'); ctx.projectedTree = {subTrees: [{}, {}, {subTrees: [{}, {}]}, {}]}; - ctx.beforeTree.subTrees !.push({afterLabel: 'b'}); + ctx.beforeTree.subTrees!.push({afterLabel: 'b'}); expect(renderToHtml(parentTemplate, ctx, 2, 2, defs)).toEqual('<child>abz</child>'); - ctx.projectedTree.subTrees ![1].afterLabel = 'h'; + ctx.projectedTree.subTrees![1].afterLabel = 'h'; expect(renderToHtml(parentTemplate, ctx, 2, 2, defs)).toEqual('<child>abhz</child>'); - ctx.beforeTree.subTrees !.push({beforeLabel: 'c'}); + ctx.beforeTree.subTrees!.push({beforeLabel: 'c'}); expect(renderToHtml(parentTemplate, ctx, 2, 2, defs)).toEqual('<child>abchz</child>'); // To check the context easily: // console.log(JSON.stringify(ctx)); }); - }); - }); describe('component styles', () => { @@ -207,17 +204,18 @@ describe('component styles', () => { vars: 0, encapsulation: 100, selectors: [['foo']], - template: (rf: RenderFlags, ctx: StyledComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div'); - } - } + template: + (rf: RenderFlags, ctx: StyledComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div'); + } + } }); } const rendererFactory = new ProxyRenderer3Factory(); new ComponentFixture(StyledComp, {rendererFactory}); - expect(rendererFactory.lastCapturedType !.styles).toEqual(['div { color: red; }']); - expect(rendererFactory.lastCapturedType !.encapsulation).toEqual(100); + expect(rendererFactory.lastCapturedType!.styles).toEqual(['div { color: red; }']); + expect(rendererFactory.lastCapturedType!.encapsulation).toEqual(100); }); }); @@ -233,10 +231,11 @@ describe('component animations', () => { decls: 0, vars: 0, data: { - animation: [ - animA, - animB, - ], + animation: + [ + animA, + animB, + ], }, selectors: [['foo']], template: (rf: RenderFlags, ctx: AnimComp) => {} @@ -245,7 +244,7 @@ describe('component animations', () => { const rendererFactory = new ProxyRenderer3Factory(); new ComponentFixture(AnimComp, {rendererFactory}); - const capturedAnimations = rendererFactory.lastCapturedType !.data !['animation']; + const capturedAnimations = rendererFactory.lastCapturedType!.data!['animation']; expect(Array.isArray(capturedAnimations)).toBeTruthy(); expect(capturedAnimations.length).toEqual(2); expect(capturedAnimations).toContain(animA); @@ -268,7 +267,7 @@ describe('component animations', () => { } const rendererFactory = new ProxyRenderer3Factory(); new ComponentFixture(AnimComp, {rendererFactory}); - const data = rendererFactory.lastCapturedType !.data; + const data = rendererFactory.lastCapturedType!.data; expect(data.animation).toEqual([]); }); @@ -281,14 +280,15 @@ describe('component animations', () => { vars: 1, selectors: [['foo']], consts: [[AttributeMarker.Bindings, '@fooAnimation']], - template: (rf: RenderFlags, ctx: AnimComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div', 0); - } - if (rf & RenderFlags.Update) { - ɵɵattribute('@fooAnimation', ctx.animationValue); - } - } + template: + (rf: RenderFlags, ctx: AnimComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div', 0); + } + if (rf & RenderFlags.Update) { + ɵɵattribute('@fooAnimation', ctx.animationValue); + } + } }); animationValue = '123'; @@ -297,7 +297,7 @@ describe('component animations', () => { const rendererFactory = new MockRendererFactory(['setAttribute']); const fixture = new ComponentFixture(AnimComp, {rendererFactory}); - const renderer = rendererFactory.lastRenderer !; + const renderer = rendererFactory.lastRenderer!; fixture.component.animationValue = '456'; fixture.update(); @@ -318,18 +318,19 @@ describe('component animations', () => { vars: 1, selectors: [['foo']], consts: [['@fooAnimation', '']], - template: (rf: RenderFlags, ctx: AnimComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div', 0); - } - } + template: + (rf: RenderFlags, ctx: AnimComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div', 0); + } + } }); } const rendererFactory = new MockRendererFactory(['setProperty']); const fixture = new ComponentFixture(AnimComp, {rendererFactory}); - const renderer = rendererFactory.lastRenderer !; + const renderer = rendererFactory.lastRenderer!; fixture.update(); const spy = renderer.spies['setProperty']; @@ -396,16 +397,17 @@ describe('element discovery', () => { selectors: [['structured-comp']], decls: 2, vars: 0, - template: (rf: RenderFlags, ctx: StructuredComp) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'div'); - ɵɵelementStart(1, 'p'); - ɵɵelementEnd(); - ɵɵelementEnd(); - } - if (rf & RenderFlags.Update) { - } - } + template: + (rf: RenderFlags, ctx: StructuredComp) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'div'); + ɵɵelementStart(1, 'p'); + ɵɵelementEnd(); + ɵɵelementEnd(); + } + if (rf & RenderFlags.Update) { + } + } }); } @@ -428,13 +430,14 @@ describe('element discovery', () => { selectors: [['child-comp']], decls: 3, vars: 0, - template: (rf: RenderFlags, ctx: ChildComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div'); - ɵɵelement(1, 'div'); - ɵɵelement(2, 'div'); - } - } + template: + (rf: RenderFlags, ctx: ChildComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div'); + ɵɵelement(1, 'div'); + ɵɵelement(2, 'div'); + } + } }); } @@ -446,14 +449,15 @@ describe('element discovery', () => { directives: [ChildComp], decls: 2, vars: 0, - template: (rf: RenderFlags, ctx: ParentComp) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'section'); - ɵɵelementStart(1, 'child-comp'); - ɵɵelementEnd(); - ɵɵelementEnd(); - } - } + template: + (rf: RenderFlags, ctx: ParentComp) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'section'); + ɵɵelementStart(1, 'child-comp'); + ɵɵelementEnd(); + ɵɵelementEnd(); + } + } }); } @@ -480,24 +484,25 @@ describe('element discovery', () => { decls: 2, vars: 1, consts: [['ngIf', '']], - template: (rf: RenderFlags, ctx: StructuredComp) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'section'); - ɵɵtemplate(1, (rf, ctx) => { + template: + (rf: RenderFlags, ctx: StructuredComp) => { if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'div'); - ɵɵelement(1, 'p'); + ɵɵelementStart(0, 'section'); + ɵɵtemplate(1, (rf, ctx) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'div'); + ɵɵelement(1, 'p'); + ɵɵelementEnd(); + ɵɵelement(2, 'div'); + } + }, 3, 0, 'ng-template', 0); ɵɵelementEnd(); - ɵɵelement(2, 'div'); } - }, 3, 0, 'ng-template', 0); - ɵɵelementEnd(); - } - if (rf & RenderFlags.Update) { - ɵɵadvance(1); - ɵɵproperty('ngIf', true); - } - } + if (rf & RenderFlags.Update) { + ɵɵadvance(1); + ɵɵproperty('ngIf', true); + } + } }); } @@ -529,28 +534,29 @@ describe('element discovery', () => { directives: [NgIf], decls: 2, vars: 0, - template: (rf: RenderFlags, ctx: StructuredComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'section'); - ɵɵelement(1, 'div'); - } - } + template: + (rf: RenderFlags, ctx: StructuredComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'section'); + ɵɵelement(1, 'div'); + } + } }); } const fixture = new ComponentFixture(StructuredComp); fixture.update(); - const section = fixture.hostElement.querySelector('section') !; - const sectionContext = getLContext(section) !; - const sectionLView = sectionContext.lView !; + const section = fixture.hostElement.querySelector('section')!; + const sectionContext = getLContext(section)!; + const sectionLView = sectionContext.lView!; expect(sectionContext.nodeIndex).toEqual(HEADER_OFFSET); expect(sectionLView.length).toBeGreaterThan(HEADER_OFFSET); expect(sectionContext.native).toBe(section); - const div = fixture.hostElement.querySelector('div') !; - const divContext = getLContext(div) !; - const divLView = divContext.lView !; + const div = fixture.hostElement.querySelector('div')!; + const divContext = getLContext(div)!; + const divLView = divContext.lView!; expect(divContext.nodeIndex).toEqual(HEADER_OFFSET + 1); expect(divLView.length).toBeGreaterThan(HEADER_OFFSET); expect(divContext.native).toBe(div); @@ -566,22 +572,23 @@ describe('element discovery', () => { selectors: [['structured-comp']], decls: 1, vars: 0, - template: (rf: RenderFlags, ctx: StructuredComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'section'); - } - } + template: + (rf: RenderFlags, ctx: StructuredComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'section'); + } + } }); } const fixture = new ComponentFixture(StructuredComp); fixture.update(); - const section = fixture.hostElement.querySelector('section') !as any; + const section = fixture.hostElement.querySelector('section')! as any; const result1 = section[MONKEY_PATCH_KEY_NAME]; expect(Array.isArray(result1)).toBeTruthy(); - const context = getLContext(section) !; + const context = getLContext(section)!; const result2 = section[MONKEY_PATCH_KEY_NAME]; expect(Array.isArray(result2)).toBeFalsy(); @@ -598,26 +605,27 @@ describe('element discovery', () => { selectors: [['structured-comp']], decls: 2, vars: 0, - template: (rf: RenderFlags, ctx: StructuredComp) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'section'); - ɵɵelement(1, 'p'); - ɵɵelementEnd(); - } - } + template: + (rf: RenderFlags, ctx: StructuredComp) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'section'); + ɵɵelement(1, 'p'); + ɵɵelementEnd(); + } + } }); } const fixture = new ComponentFixture(StructuredComp); fixture.update(); - const section = fixture.hostElement.querySelector('section') !as any; + const section = fixture.hostElement.querySelector('section')! as any; expect(section[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - const p = fixture.hostElement.querySelector('p') !as any; + const p = fixture.hostElement.querySelector('p')! as any; expect(p[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); - const pContext = getLContext(p) !; + const pContext = getLContext(p)!; expect(pContext.native).toBe(p); expect(p[MONKEY_PATCH_KEY_NAME]).toBe(pContext); }); @@ -631,25 +639,26 @@ describe('element discovery', () => { selectors: [['structured-comp']], decls: 1, vars: 0, - template: (rf: RenderFlags, ctx: StructuredComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'section'); - } - } + template: + (rf: RenderFlags, ctx: StructuredComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'section'); + } + } }); } const fixture = new ComponentFixture(StructuredComp); fixture.update(); - const section = fixture.hostElement.querySelector('section') !as any; + const section = fixture.hostElement.querySelector('section')! as any; const result1 = section[MONKEY_PATCH_KEY_NAME]; expect(Array.isArray(result1)).toBeTruthy(); const elementResult = result1[HEADER_OFFSET]; // first element expect(elementResult).toBe(section); - const context = getLContext(section) !; + const context = getLContext(section)!; const result2 = section[MONKEY_PATCH_KEY_NAME]; expect(Array.isArray(result2)).toBeFalsy(); @@ -679,19 +688,20 @@ describe('element discovery', () => { selectors: [['projector-comp']], decls: 4, vars: 0, - template: (rf: RenderFlags, ctx: ProjectorComp) => { - if (rf & RenderFlags.Create) { - ɵɵprojectionDef(); - ɵɵtext(0, 'welcome'); - ɵɵelementStart(1, 'header'); - ɵɵelementStart(2, 'h1'); - ɵɵprojection(3); - ɵɵelementEnd(); - ɵɵelementEnd(); - } - if (rf & RenderFlags.Update) { - } - } + template: + (rf: RenderFlags, ctx: ProjectorComp) => { + if (rf & RenderFlags.Create) { + ɵɵprojectionDef(); + ɵɵtext(0, 'welcome'); + ɵɵelementStart(1, 'header'); + ɵɵelementStart(2, 'h1'); + ɵɵprojection(3); + ɵɵelementEnd(); + ɵɵelementEnd(); + } + if (rf & RenderFlags.Update) { + } + } }); } @@ -703,18 +713,19 @@ describe('element discovery', () => { directives: [ProjectorComp], decls: 5, vars: 0, - template: (rf: RenderFlags, ctx: ParentComp) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'section'); - ɵɵelementStart(1, 'projector-comp'); - ɵɵelementStart(2, 'p'); - ɵɵtext(3, 'this content is projected'); - ɵɵelementEnd(); - ɵɵtext(4, 'this content is projected also'); - ɵɵelementEnd(); - ɵɵelementEnd(); - } - } + template: + (rf: RenderFlags, ctx: ParentComp) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'section'); + ɵɵelementStart(1, 'projector-comp'); + ɵɵelementStart(2, 'p'); + ɵɵtext(3, 'this content is projected'); + ɵɵelementEnd(); + ɵɵtext(4, 'this content is projected also'); + ɵɵelementEnd(); + ɵɵelementEnd(); + } + } }); } @@ -723,11 +734,11 @@ describe('element discovery', () => { const host = fixture.hostElement; const textNode = host.firstChild as any; - const section = host.querySelector('section') !as any; - const projectorComp = host.querySelector('projector-comp') !as any; - const header = host.querySelector('header') !as any; - const h1 = host.querySelector('h1') !as any; - const p = host.querySelector('p') !as any; + const section = host.querySelector('section')! as any; + const projectorComp = host.querySelector('projector-comp')! as any; + const header = host.querySelector('header')! as any; + const h1 = host.querySelector('h1')! as any; + const p = host.querySelector('p')! as any; const pText = p.firstChild as any; const projectedTextNode = p.nextSibling; @@ -743,9 +754,9 @@ describe('element discovery', () => { expect(pText[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); expect(projectedTextNode[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - const parentContext = getLContext(section) !; - const shadowContext = getLContext(header) !; - const projectedContext = getLContext(p) !; + const parentContext = getLContext(section)!; + const shadowContext = getLContext(header)!; + const projectedContext = getLContext(p)!; const parentComponentData = parentContext.lView; const shadowComponentData = shadowContext.lView; @@ -776,18 +787,19 @@ describe('element discovery', () => { selectors: [['structured-comp']], decls: 1, vars: 0, - template: (rf: RenderFlags, ctx: StructuredComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'section'); - } - } + template: + (rf: RenderFlags, ctx: StructuredComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'section'); + } + } }); } const fixture = new ComponentFixture(StructuredComp); fixture.update(); - const section = fixture.hostElement.querySelector('section') !as any; + const section = fixture.hostElement.querySelector('section')! as any; const manuallyCreatedElement = document.createElement('div'); section.appendChild(manuallyCreatedElement); @@ -819,11 +831,11 @@ describe('element discovery', () => { const hostLView = (hostElm as any)[MONKEY_PATCH_KEY_NAME]; expect(hostLView).toBe(componentLView); - const context1 = getLContext(hostElm) !; + const context1 = getLContext(hostElm)!; expect(context1.lView).toBe(hostLView); expect(context1.native).toEqual(hostElm); - const context2 = getLContext(component) !; + const context2 = getLContext(component)!; expect(context2).toBe(context1); expect(context2.lView).toBe(hostLView); expect(context2.native).toEqual(hostElm); @@ -859,12 +871,13 @@ describe('element discovery', () => { decls: 2, vars: 0, consts: [['my-dir-1', '', 'my-dir-2', ''], ['my-dir-3']], - template: (rf: RenderFlags, ctx: StructuredComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div', 0); - ɵɵelement(1, 'div', 1); - } - } + template: + (rf: RenderFlags, ctx: StructuredComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div', 0); + ɵɵelement(1, 'div', 1); + } + } }); } @@ -872,9 +885,9 @@ describe('element discovery', () => { fixture.update(); const hostElm = fixture.hostElement; - const div1 = hostElm.querySelector('div:first-child') !as any; - const div2 = hostElm.querySelector('div:last-child') !as any; - const context = getLContext(hostElm) !; + const div1 = hostElm.querySelector('div:first-child')! as any; + const div2 = hostElm.querySelector('div:last-child')! as any; + const context = getLContext(hostElm)!; const componentView = context.lView[context.nodeIndex]; expect(componentView).toContain(myDir1Instance); @@ -885,9 +898,9 @@ describe('element discovery', () => { expect(Array.isArray((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy(); expect(Array.isArray((myDir3Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy(); - const d1Context = getLContext(myDir1Instance) !; - const d2Context = getLContext(myDir2Instance) !; - const d3Context = getLContext(myDir3Instance) !; + const d1Context = getLContext(myDir1Instance)!; + const d2Context = getLContext(myDir2Instance)!; + const d3Context = getLContext(myDir3Instance)!; expect(d1Context.lView).toEqual(componentView); expect(d2Context.lView).toEqual(componentView); @@ -933,11 +946,12 @@ describe('element discovery', () => { selectors: [['child-comp']], decls: 1, vars: 0, - template: (rf: RenderFlags, ctx: ChildComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div'); - } - } + template: + (rf: RenderFlags, ctx: ChildComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div'); + } + } }); } @@ -950,18 +964,19 @@ describe('element discovery', () => { decls: 1, vars: 0, consts: [['my-dir-1', '', 'my-dir-2', '']], - template: (rf: RenderFlags, ctx: ParentComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'child-comp', 0); - } - } + template: + (rf: RenderFlags, ctx: ParentComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'child-comp', 0); + } + } }); } const fixture = new ComponentFixture(ParentComp); fixture.update(); - const childCompHostElm = fixture.hostElement.querySelector('child-comp') !as any; + const childCompHostElm = fixture.hostElement.querySelector('child-comp')! as any; const lView = childCompHostElm[MONKEY_PATCH_KEY_NAME]; expect(Array.isArray(lView)).toBeTruthy(); @@ -969,7 +984,7 @@ describe('element discovery', () => { expect((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(lView); expect((childComponentInstance as any)[MONKEY_PATCH_KEY_NAME]).toBe(lView); - const childNodeContext = getLContext(childCompHostElm) !; + const childNodeContext = getLContext(childCompHostElm)!; expect(childNodeContext.component).toBeFalsy(); expect(childNodeContext.directives).toBeFalsy(); assertMonkeyPatchValueIsLView(myDir1Instance); @@ -978,21 +993,21 @@ describe('element discovery', () => { expect(getLContext(myDir1Instance)).toBe(childNodeContext); expect(childNodeContext.component).toBeFalsy(); - expect(childNodeContext.directives !.length).toEqual(2); + expect(childNodeContext.directives!.length).toEqual(2); assertMonkeyPatchValueIsLView(myDir1Instance, false); assertMonkeyPatchValueIsLView(myDir2Instance, false); assertMonkeyPatchValueIsLView(childComponentInstance); expect(getLContext(myDir2Instance)).toBe(childNodeContext); expect(childNodeContext.component).toBeFalsy(); - expect(childNodeContext.directives !.length).toEqual(2); + expect(childNodeContext.directives!.length).toEqual(2); assertMonkeyPatchValueIsLView(myDir1Instance, false); assertMonkeyPatchValueIsLView(myDir2Instance, false); assertMonkeyPatchValueIsLView(childComponentInstance); expect(getLContext(childComponentInstance)).toBe(childNodeContext); expect(childNodeContext.component).toBeTruthy(); - expect(childNodeContext.directives !.length).toEqual(2); + expect(childNodeContext.directives!.length).toEqual(2); assertMonkeyPatchValueIsLView(myDir1Instance, false); assertMonkeyPatchValueIsLView(myDir2Instance, false); assertMonkeyPatchValueIsLView(childComponentInstance, false); @@ -1011,13 +1026,14 @@ describe('element discovery', () => { selectors: [['child-comp']], decls: 3, vars: 0, - template: (rf: RenderFlags, ctx: ChildComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div'); - ɵɵelement(1, 'div'); - ɵɵelement(2, 'div'); - } - } + template: + (rf: RenderFlags, ctx: ChildComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div'); + ɵɵelement(1, 'div'); + ɵɵelement(2, 'div'); + } + } }); } @@ -1029,14 +1045,15 @@ describe('element discovery', () => { directives: [ChildComp], decls: 2, vars: 0, - template: (rf: RenderFlags, ctx: ParentComp) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'section'); - ɵɵelementStart(1, 'child-comp'); - ɵɵelementEnd(); - ɵɵelementEnd(); - } - } + template: + (rf: RenderFlags, ctx: ParentComp) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'section'); + ɵɵelementStart(1, 'child-comp'); + ɵɵelementEnd(); + ɵɵelementEnd(); + } + } }); } @@ -1047,7 +1064,7 @@ describe('element discovery', () => { const child = host.querySelector('child-comp') as any; expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - const context = getLContext(child) !; + const context = getLContext(child)!; expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); const componentData = context.lView[context.nodeIndex]; @@ -1055,7 +1072,7 @@ describe('element discovery', () => { expect(component instanceof ChildComp).toBeTruthy(); expect(component[MONKEY_PATCH_KEY_NAME]).toBe(context.lView); - const componentContext = getLContext(component) !; + const componentContext = getLContext(component)!; expect(component[MONKEY_PATCH_KEY_NAME]).toBe(componentContext); expect(componentContext.nodeIndex).toEqual(context.nodeIndex); expect(componentContext.native).toEqual(context.native); @@ -1072,28 +1089,33 @@ describe('sanitization', () => { selectors: [['sanitize-this']], decls: 1, vars: 1, - template: (rf: RenderFlags, ctx: SanitizationComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'a'); - } - if (rf & RenderFlags.Update) { - ɵɵproperty('href', ctx.href, ɵɵsanitizeUrl); - } - } + template: + (rf: RenderFlags, ctx: SanitizationComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'a'); + } + if (rf & RenderFlags.Update) { + ɵɵproperty('href', ctx.href, ɵɵsanitizeUrl); + } + } }); private href = ''; - updateLink(href: any) { this.href = href; } + updateLink(href: any) { + this.href = href; + } } - const sanitizer = new LocalSanitizer((value) => { return 'http://bar'; }); + const sanitizer = new LocalSanitizer((value) => { + return 'http://bar'; + }); const fixture = new ComponentFixture(SanitizationComp, {sanitizer}); fixture.component.updateLink('http://foo'); fixture.update(); - const anchor = fixture.hostElement.querySelector('a') !; + const anchor = fixture.hostElement.querySelector('a')!; expect(anchor.getAttribute('href')).toEqual('http://bar'); fixture.component.updateLink(sanitizer.bypassSecurityTrustUrl('http://foo')); @@ -1113,11 +1135,12 @@ describe('sanitization', () => { type: UnsafeUrlHostBindingDir, selectors: [['', 'unsafeUrlHostBindingDir', '']], hostVars: 1, - hostBindings: (rf: RenderFlags, ctx: any) => { - if (rf & RenderFlags.Update) { - ɵɵhostProperty('cite', ctx.cite, ɵɵsanitizeUrl); - } - } + hostBindings: + (rf: RenderFlags, ctx: any) => { + if (rf & RenderFlags.Update) { + ɵɵhostProperty('cite', ctx.cite, ɵɵsanitizeUrl); + } + } }); } @@ -1129,11 +1152,12 @@ describe('sanitization', () => { decls: 1, vars: 0, consts: [['unsafeUrlHostBindingDir', '']], - template: (rf: RenderFlags, ctx: SimpleComp) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'blockquote', 0); - } - }, + template: + (rf: RenderFlags, ctx: SimpleComp) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'blockquote', 0); + } + }, directives: [UnsafeUrlHostBindingDir] }); } @@ -1141,13 +1165,13 @@ describe('sanitization', () => { const sanitizer = new LocalSanitizer((value) => 'http://bar'); const fixture = new ComponentFixture(SimpleComp, {sanitizer}); - hostBindingDir !.cite = 'http://foo'; + hostBindingDir!.cite = 'http://foo'; fixture.update(); - const anchor = fixture.hostElement.querySelector('blockquote') !; + const anchor = fixture.hostElement.querySelector('blockquote')!; expect(anchor.getAttribute('cite')).toEqual('http://bar'); - hostBindingDir !.cite = sanitizer.bypassSecurityTrustUrl('http://foo'); + hostBindingDir!.cite = sanitizer.bypassSecurityTrustUrl('http://foo'); fixture.update(); expect(anchor.getAttribute('cite')).toEqual('http://foo'); @@ -1156,7 +1180,9 @@ describe('sanitization', () => { class LocalSanitizedValue { constructor(public value: any) {} - toString() { return this.value; } + toString() { + return this.value; + } } class LocalSanitizer implements Sanitizer { @@ -1174,7 +1200,9 @@ class LocalSanitizer implements Sanitizer { bypassSecurityTrustScript(value: string) {} bypassSecurityTrustResourceUrl(value: string) {} - bypassSecurityTrustUrl(value: string) { return new LocalSanitizedValue(value); } + bypassSecurityTrustUrl(value: string) { + return new LocalSanitizedValue(value); + } } class ProxyRenderer3Factory implements RendererFactory3 { diff --git a/packages/core/test/render3/ivy/jit_spec.ts b/packages/core/test/render3/ivy/jit_spec.ts index 6b14b3fe8b55c..9aad29f7ac67d 100644 --- a/packages/core/test/render3/ivy/jit_spec.ts +++ b/packages/core/test/render3/ivy/jit_spec.ts @@ -10,7 +10,7 @@ import 'reflect-metadata'; import {ElementRef, QueryList, ɵɵsetComponentScope as setComponentScope} from '@angular/core'; import {Injectable} from '@angular/core/src/di/injectable'; import {setCurrentInjector, ɵɵinject} from '@angular/core/src/di/injector_compatibility'; -import {ɵɵInjectorDef, ɵɵdefineInjectable} from '@angular/core/src/di/interface/defs'; +import {ɵɵdefineInjectable, ɵɵInjectorDef} from '@angular/core/src/di/interface/defs'; import {ivyEnabled} from '@angular/core/src/ivy_switch'; import {ContentChild, ContentChildren, ViewChild, ViewChildren} from '@angular/core/src/metadata/di'; import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata/directives'; @@ -20,9 +20,13 @@ import {ComponentDef, FactoryFn, PipeDef} from '@angular/core/src/render3/interf ivyEnabled && describe('render3 jit', () => { let injector: any; - beforeAll(() => { injector = setCurrentInjector(null); }); + beforeAll(() => { + injector = setCurrentInjector(null); + }); - afterAll(() => { setCurrentInjector(injector); }); + afterAll(() => { + setCurrentInjector(injector); + }); it('compiles a component', () => { @Component({ @@ -69,7 +73,6 @@ ivyEnabled && describe('render3 jit', () => { }); it('compiles an injectable with a useFactory provider, without deps', () => { - @Injectable({providedIn: 'root', useFactory: () => 'test'}) class Service { } @@ -100,7 +103,9 @@ ivyEnabled && describe('render3 jit', () => { @Injectable({providedIn: 'root', useClass: Other, deps: [Existing]}) class Service { - get value(): any { return null; } + get value(): any { + return null; + } } const ServiceAny = Service as any; @@ -116,7 +121,9 @@ ivyEnabled && describe('render3 jit', () => { @Injectable({providedIn: 'root', useClass: Existing}) class Service { - get value(): number { return 0; } + get value(): number { + return 0; + } } expect(ɵɵinject(Existing).value).toBe(1); @@ -223,17 +230,17 @@ ivyEnabled && describe('render3 jit', () => { }, }) class Cmp { - @HostBinding('class.green') - green: boolean = false; + @HostBinding('class.green') green: boolean = false; @HostListener('change', ['$event']) - onChange(event: any): void {} + onChange(event: any): void { + } } const cmpDef = (Cmp as any).ɵcmp as ComponentDef<Cmp>; expect(cmpDef.hostBindings).toBeDefined(); - expect(cmpDef.hostBindings !.length).toBe(2); + expect(cmpDef.hostBindings!.length).toBe(2); }); it('should compile @Pipes without errors', () => { diff --git a/packages/core/test/render3/jit/directive_spec.ts b/packages/core/test/render3/jit/directive_spec.ts index 3013e05a80d1e..8b94dadb5e406 100644 --- a/packages/core/test/render3/jit/directive_spec.ts +++ b/packages/core/test/render3/jit/directive_spec.ts @@ -12,16 +12,14 @@ import {setClassMetadata} from '@angular/core/src/render3/metadata'; import {convertToR3QueryMetadata, directiveMetadata, extendsDirectlyFromObject} from '../../../src/render3/jit/directive'; describe('jit directive helper functions', () => { - describe('extendsDirectlyFromObject', () => { - // Inheritance Example using Classes class Parent {} class Child extends Parent {} // Inheritance Example using Function - const Parent5 = function Parent5() {} as any as{new (): {}}; - const Child5 = function Child5() {} as any as{new (): {}}; + const Parent5 = function Parent5() {} as any as {new (): {}}; + const Child5 = function Child5() {} as any as {new (): {}}; Child5.prototype = new Parent5; Child5.prototype.constructor = Child5; @@ -45,7 +43,6 @@ describe('jit directive helper functions', () => { }); describe('convertToR3QueryMetadata', () => { - it('should convert decorator with a single string selector', () => { expect(convertToR3QueryMetadata('propName', { selector: 'localRef', @@ -83,7 +80,6 @@ describe('jit directive helper functions', () => { }); it('should convert decorator with type selector and read option', () => { - class Directive {} const converted = convertToR3QueryMetadata('propName', { @@ -98,7 +94,6 @@ describe('jit directive helper functions', () => { expect(converted.predicate).toEqual(Directive); expect(converted.read).toEqual(Directive); }); - }); describe('directiveMetadata', () => { @@ -155,6 +150,5 @@ describe('jit directive helper functions', () => { expect(subPropMetadata.someInput).toBeFalsy(); expect(subPropMetadata.someOtherInput).toBeTruthy(); }); - }); }); diff --git a/packages/core/test/render3/jit_environment_spec.ts b/packages/core/test/render3/jit_environment_spec.ts index c7e5b4640e0de..18d26e2599d4a 100644 --- a/packages/core/test/render3/jit_environment_spec.ts +++ b/packages/core/test/render3/jit_environment_spec.ts @@ -29,7 +29,7 @@ describe('r3 jit environment', () => { Object // Map over the static properties of Identifiers. .keys(Identifiers) - .map(key => (Identifiers as any as{[key: string]: string | ExternalReference})[key]) + .map(key => (Identifiers as any as {[key: string]: string | ExternalReference})[key]) // A few such properties are string constants. Ignore them, and focus on ExternalReferences. .filter(isExternalReference) // Some references are to interface types. Only take properties which have runtime values. @@ -42,7 +42,7 @@ describe('r3 jit environment', () => { }); }); -function isExternalReference(sym: ExternalReference | string): sym is ExternalReference& +function isExternalReference(sym: ExternalReference|string): sym is ExternalReference& {name: string} { return typeof sym === 'object' && sym.name !== null && sym.moduleName !== null; } diff --git a/packages/core/test/render3/lifecycle_spec.ts b/packages/core/test/render3/lifecycle_spec.ts index 87271664d965e..b8abf18072f19 100644 --- a/packages/core/test/render3/lifecycle_spec.ts +++ b/packages/core/test/render3/lifecycle_spec.ts @@ -13,7 +13,6 @@ import {NgIf} from './common_with_def'; import {ComponentFixture, createComponent} from './render_util'; describe('lifecycles', () => { - function getParentTemplate(name: string) { return (rf: RenderFlags, ctx: any) => { if (rf & RenderFlags.Create) { @@ -28,7 +27,9 @@ describe('lifecycles', () => { describe('onInit', () => { let events: string[]; - beforeEach(() => { events = []; }); + beforeEach(() => { + events = []; + }); let Comp = createOnInitComponent('comp', (rf: RenderFlags) => { if (rf & RenderFlags.Create) { @@ -61,14 +62,17 @@ describe('lifecycles', () => { selectors: [[name]], decls: decls, vars: vars, - inputs: {val: 'val'}, template, + inputs: {val: 'val'}, + template, directives: directives }); }; } class Directive { - ngOnInit() { events.push('dir'); } + ngOnInit() { + events.push('dir'); + } static ɵfac = () => new Directive(); static ɵdir = ɵɵdefineDirective({type: Directive, selectors: [['', 'dir', '']]}); diff --git a/packages/core/test/render3/listeners_spec.ts b/packages/core/test/render3/listeners_spec.ts index 27455f683628a..67fe696feb32f 100644 --- a/packages/core/test/render3/listeners_spec.ts +++ b/packages/core/test/render3/listeners_spec.ts @@ -13,8 +13,9 @@ import {ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵe import {RenderFlags} from '../../src/render3/interfaces/definition'; import {GlobalTargetResolver} from '../../src/render3/interfaces/renderer'; import {ɵɵrestoreView} from '../../src/render3/state'; + import {getRendererFactory2} from './imported_renderer2'; -import {ComponentFixture, TemplateFixture, containerEl, createComponent, getDirectiveOnNode, renderToHtml, requestAnimationFrame} from './render_util'; +import {ComponentFixture, containerEl, createComponent, getDirectiveOnNode, renderToHtml, requestAnimationFrame, TemplateFixture} from './render_util'; describe('event listeners', () => { @@ -25,7 +26,9 @@ describe('event listeners', () => { showing = true; counter = 0; - onClick() { this.counter++; } + onClick() { + this.counter++; + } static ɵfac = () => { @@ -40,25 +43,32 @@ describe('event listeners', () => { decls: 2, vars: 0, /** <button (click)="onClick()"> Click me </button> */ - template: function CompTemplate(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'button'); - { - ɵɵlistener('click', function() { return ctx.onClick(); }); - ɵɵtext(1, 'Click me'); + template: + function CompTemplate(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'button'); + { + ɵɵlistener('click', function() { + return ctx.onClick(); + }); + ɵɵtext(1, 'Click me'); + } + ɵɵelementEnd(); + } } - ɵɵelementEnd(); - } - } }); } class MyCompWithGlobalListeners { /* @HostListener('document:custom') */ - onDocumentCustomEvent() { events.push('component - document:custom'); } + onDocumentCustomEvent() { + events.push('component - document:custom'); + } /* @HostListener('body:click') */ - onBodyClick() { events.push('component - body:click'); } + onBodyClick() { + events.push('component - body:click'); + } static ɵfac = () => { @@ -72,51 +82,60 @@ describe('event listeners', () => { selectors: [['comp']], decls: 1, vars: 0, - template: function CompTemplate(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵtext(0, 'Some text'); - } - }, - hostBindings: function HostListenerDir_HostBindings(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵlistener('custom', function() { - return ctx.onDocumentCustomEvent(); - }, false, ɵɵresolveDocument as GlobalTargetResolver); - ɵɵlistener('click', function() { - return ctx.onBodyClick(); - }, false, ɵɵresolveBody as GlobalTargetResolver); - } - } + template: + function CompTemplate(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵtext(0, 'Some text'); + } + }, + hostBindings: + function HostListenerDir_HostBindings(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵlistener('custom', function() { + return ctx.onDocumentCustomEvent(); + }, false, ɵɵresolveDocument as GlobalTargetResolver); + ɵɵlistener('click', function() { + return ctx.onBodyClick(); + }, false, ɵɵresolveBody as GlobalTargetResolver); + } + } }); } class GlobalHostListenerDir { /* @HostListener('document:custom') */ - onDocumentCustomEvent() { events.push('directive - document:custom'); } + onDocumentCustomEvent() { + events.push('directive - document:custom'); + } /* @HostListener('body:click') */ - onBodyClick() { events.push('directive - body:click'); } + onBodyClick() { + events.push('directive - body:click'); + } - static ɵfac = function HostListenerDir_Factory() { return new GlobalHostListenerDir(); }; + static ɵfac = function HostListenerDir_Factory() { + return new GlobalHostListenerDir(); + }; static ɵdir = ɵɵdefineDirective({ type: GlobalHostListenerDir, selectors: [['', 'hostListenerDir', '']], - hostBindings: function HostListenerDir_HostBindings(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵlistener('custom', function() { - return ctx.onDocumentCustomEvent(); - }, false, ɵɵresolveDocument as GlobalTargetResolver)('click', function() { - return ctx.onBodyClick(); - }, false, ɵɵresolveBody as GlobalTargetResolver); - } - } + hostBindings: + function HostListenerDir_HostBindings(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵlistener('custom', function() { + return ctx.onDocumentCustomEvent(); + }, false, ɵɵresolveDocument as GlobalTargetResolver)('click', function() { + return ctx.onBodyClick(); + }, false, ɵɵresolveBody as GlobalTargetResolver); + } + } }); } class PreventDefaultComp { handlerReturnValue: any = true; // TODO(issue/24571): remove '!'. - event !: Event; + event!: Event; onClick(e: any) { this.event = e; @@ -136,16 +155,19 @@ describe('event listeners', () => { decls: 2, vars: 0, /** <button (click)="onClick($event)">Click</button> */ - template: (rf: RenderFlags, ctx: PreventDefaultComp) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'button'); - { - ɵɵlistener('click', function($event: any) { return ctx.onClick($event); }); - ɵɵtext(1, 'Click'); + template: + (rf: RenderFlags, ctx: PreventDefaultComp) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'button'); + { + ɵɵlistener('click', function($event: any) { + return ctx.onClick($event); + }); + ɵɵtext(1, 'Click'); + } + ɵɵelementEnd(); + } } - ɵɵelementEnd(); - } - } }); } @@ -157,7 +179,7 @@ describe('event listeners', () => { it('should call function on event emit', () => { const fixture = new ComponentFixture(MyComp); const comp = fixture.component; - const button = fixture.hostElement.querySelector('button') !; + const button = fixture.hostElement.querySelector('button')!; button.click(); expect(comp.counter).toEqual(1); @@ -169,36 +191,36 @@ describe('event listeners', () => { it('should retain event handler return values using document', () => { const fixture = new ComponentFixture(PreventDefaultComp); const preventDefaultComp = fixture.component; - const button = fixture.hostElement.querySelector('button') !; + const button = fixture.hostElement.querySelector('button')!; button.click(); - expect(preventDefaultComp.event !.preventDefault).not.toHaveBeenCalled(); + expect(preventDefaultComp.event!.preventDefault).not.toHaveBeenCalled(); preventDefaultComp.handlerReturnValue = undefined; button.click(); - expect(preventDefaultComp.event !.preventDefault).not.toHaveBeenCalled(); + expect(preventDefaultComp.event!.preventDefault).not.toHaveBeenCalled(); preventDefaultComp.handlerReturnValue = false; button.click(); - expect(preventDefaultComp.event !.preventDefault).toHaveBeenCalled(); + expect(preventDefaultComp.event!.preventDefault).toHaveBeenCalled(); }); it('should retain event handler return values with renderer2', () => { const fixture = new ComponentFixture(PreventDefaultComp, {rendererFactory: getRendererFactory2(document)}); const preventDefaultComp = fixture.component; - const button = fixture.hostElement.querySelector('button') !; + const button = fixture.hostElement.querySelector('button')!; button.click(); - expect(preventDefaultComp.event !.preventDefault).not.toHaveBeenCalled(); + expect(preventDefaultComp.event!.preventDefault).not.toHaveBeenCalled(); preventDefaultComp.handlerReturnValue = undefined; button.click(); - expect(preventDefaultComp.event !.preventDefault).not.toHaveBeenCalled(); + expect(preventDefaultComp.event!.preventDefault).not.toHaveBeenCalled(); preventDefaultComp.handlerReturnValue = false; button.click(); - expect(preventDefaultComp.event !.preventDefault).toHaveBeenCalled(); + expect(preventDefaultComp.event!.preventDefault).toHaveBeenCalled(); }); it('should call function chain on event emit', () => { @@ -220,11 +242,15 @@ describe('event listeners', () => { const ctx = { counter: 0, counter2: 0, - onClick: function() { this.counter++; }, - onClick2: function() { this.counter2++; } + onClick: function() { + this.counter++; + }, + onClick2: function() { + this.counter2++; + } }; renderToHtml(Template, ctx, 2); - const button = containerEl.querySelector('button') !; + const button = containerEl.querySelector('button')!; button.click(); expect(ctx.counter).toBe(1); @@ -236,13 +262,14 @@ describe('event listeners', () => { }); it('should evaluate expression on event emit', () => { - /** <button (click)="showing=!showing"> Click me </button> */ function Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { ɵɵelementStart(0, 'button'); { - ɵɵlistener('click', function() { return ctx.showing = !ctx.showing; }); + ɵɵlistener('click', function() { + return ctx.showing = !ctx.showing; + }); ɵɵtext(1, 'Click me'); } ɵɵelementEnd(); @@ -251,7 +278,7 @@ describe('event listeners', () => { const ctx = {showing: false}; renderToHtml(Template, ctx, 2); - const button = containerEl.querySelector('button') !; + const button = containerEl.querySelector('button')!; button.click(); expect(ctx.showing).toBe(true); @@ -261,11 +288,10 @@ describe('event listeners', () => { }); it('should support listeners in views', () => { - /** * % if (ctx.showing) { - * <button (click)="onClick()"> Click me </button> - * % } + * <button (click)="onClick()"> Click me </button> + * % } */ function Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { @@ -278,7 +304,9 @@ describe('event listeners', () => { if (ɵɵembeddedViewStart(1, 2, 0)) { ɵɵelementStart(0, 'button'); { - ɵɵlistener('click', function() { return ctx.onClick(); }); + ɵɵlistener('click', function() { + return ctx.onClick(); + }); ɵɵtext(1, 'Click me'); } ɵɵelementEnd(); @@ -292,7 +320,7 @@ describe('event listeners', () => { let comp = new MyComp(); renderToHtml(Template, comp, 1); - const button = containerEl.querySelector('button') !; + const button = containerEl.querySelector('button')!; button.click(); expect(comp.counter).toEqual(1); @@ -308,17 +336,18 @@ describe('event listeners', () => { }); it('should destroy listeners in views with renderer2', () => { - /** - * % if (ctx.showing) { - * <button (click)="onClick()"> Click me </button> - * % } + * % if (ctx.showing) { + * <button (click)="onClick()"> Click me </button> + * % } */ class AppComp { counter = 0; showing = true; - onClick() { this.counter++; } + onClick() { + this.counter++; + } static ɵfac = () => new AppComp(); static ɵcmp = ɵɵdefineComponent({ @@ -326,34 +355,37 @@ describe('event listeners', () => { selectors: [['app-comp']], decls: 1, vars: 0, - template: function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵcontainer(0); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(0); - { - if (ctx.showing) { - if (ɵɵembeddedViewStart(0, 2, 0)) { - ɵɵelementStart(0, 'button'); - { - ɵɵlistener('click', function() { return ctx.onClick(); }); - ɵɵtext(1, 'Click me'); + template: + function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵcontainer(0); + } + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(0); + { + if (ctx.showing) { + if (ɵɵembeddedViewStart(0, 2, 0)) { + ɵɵelementStart(0, 'button'); + { + ɵɵlistener('click', function() { + return ctx.onClick(); + }); + ɵɵtext(1, 'Click me'); + } + ɵɵelementEnd(); + } + ɵɵembeddedViewEnd(); } - ɵɵelementEnd(); } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } } - ɵɵcontainerRefreshEnd(); - } - } }); } const fixture = new ComponentFixture(AppComp, {rendererFactory: getRendererFactory2(document)}); const comp = fixture.component; - const button = fixture.hostElement.querySelector('button') !; + const button = fixture.hostElement.querySelector('button')!; button.click(); expect(comp.counter).toEqual(1); @@ -369,17 +401,18 @@ describe('event listeners', () => { }); it('should destroy listeners in for loops', () => { - /** - * % for (let i = 0; i < ctx.buttons; i++) { - * <button (click)="onClick(i)"> Click me </button> - * % } + * % for (let i = 0; i < ctx.buttons; i++) { + * <button (click)="onClick(i)"> Click me </button> + * % } */ class AppComp { buttons = 2; counters = [0, 0]; - onClick(index: number) { this.counters[index]++; } + onClick(index: number) { + this.counters[index]++; + } static ɵfac = () => new AppComp(); static ɵcmp = ɵɵdefineComponent({ @@ -387,34 +420,37 @@ describe('event listeners', () => { selectors: [['app-comp']], decls: 1, vars: 0, - template: function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵcontainer(0); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(0); - { - for (let i = 0; i < ctx.buttons; i++) { - if (ɵɵembeddedViewStart(0, 2, 0)) { - ɵɵelementStart(0, 'button'); - { - ɵɵlistener('click', function() { return ctx.onClick(i); }); - ɵɵtext(1, 'Click me'); + template: + function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵcontainer(0); + } + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(0); + { + for (let i = 0; i < ctx.buttons; i++) { + if (ɵɵembeddedViewStart(0, 2, 0)) { + ɵɵelementStart(0, 'button'); + { + ɵɵlistener('click', function() { + return ctx.onClick(i); + }); + ɵɵtext(1, 'Click me'); + } + ɵɵelementEnd(); + } + ɵɵembeddedViewEnd(); } - ɵɵelementEnd(); } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } } - ɵɵcontainerRefreshEnd(); - } - } }); } const fixture = new ComponentFixture(AppComp); const comp = fixture.component; - const buttons = fixture.hostElement.querySelectorAll('button') !; + const buttons = fixture.hostElement.querySelectorAll('button')!; buttons[0].click(); expect(comp.counters).toEqual([1, 0]); @@ -432,18 +468,19 @@ describe('event listeners', () => { }); it('should destroy listeners in for loops with renderer2', () => { - /** - * % for (let i = 0; i < ctx.buttons; i++) { - * <button (click)="onClick(i)"> Click me </button> - * {{ counters[i] }} - * % } + * % for (let i = 0; i < ctx.buttons; i++) { + * <button (click)="onClick(i)"> Click me </button> + * {{ counters[i] }} + * % } */ class AppComp { buttons = 2; counters = [0, 0]; - onClick(index: number) { this.counters[index]++; } + onClick(index: number) { + this.counters[index]++; + } static ɵfac = () => new AppComp(); static ɵcmp = ɵɵdefineComponent({ @@ -451,42 +488,45 @@ describe('event listeners', () => { selectors: [['app-comp']], decls: 1, vars: 0, - template: function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵcontainer(0); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(0); - { - for (let i = 0; i < ctx.buttons; i++) { - const rf1 = ɵɵembeddedViewStart(1, 4, 1); - if (rf1 & RenderFlags.Create) { - ɵɵelementStart(0, 'button'); - { - ɵɵlistener('click', function() { return ctx.onClick(i); }); - ɵɵtext(1, 'Click me'); + template: + function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵcontainer(0); + } + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(0); + { + for (let i = 0; i < ctx.buttons; i++) { + const rf1 = ɵɵembeddedViewStart(1, 4, 1); + if (rf1 & RenderFlags.Create) { + ɵɵelementStart(0, 'button'); + { + ɵɵlistener('click', function() { + return ctx.onClick(i); + }); + ɵɵtext(1, 'Click me'); + } + ɵɵelementEnd(); + ɵɵelementStart(2, 'div'); + { ɵɵtext(3); } + ɵɵelementEnd(); + } + if (rf1 & RenderFlags.Update) { + ɵɵselect(3); + ɵɵtextInterpolate(ctx.counters[i]); + } + ɵɵembeddedViewEnd(); } - ɵɵelementEnd(); - ɵɵelementStart(2, 'div'); - { ɵɵtext(3); } - ɵɵelementEnd(); - } - if (rf1 & RenderFlags.Update) { - ɵɵselect(3); - ɵɵtextInterpolate(ctx.counters[i]); } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } } - ɵɵcontainerRefreshEnd(); - } - } }); } const fixture = new ComponentFixture(AppComp, {rendererFactory: getRendererFactory2(document)}); const comp = fixture.component; - const buttons = fixture.hostElement.querySelectorAll('button') !; + const buttons = fixture.hostElement.querySelectorAll('button')!; const divs = fixture.hostElement.querySelectorAll('div'); buttons[0].click(); @@ -522,24 +562,34 @@ describe('event listeners', () => { let events: string[] = []; class MyComp { /* @HostListener('click') */ - onClick() { events.push('click!'); } + onClick() { + events.push('click!'); + } + + static ɵfac = + () => { + return new MyComp(); + } - static ɵfac = () => { return new MyComp(); }; static ɵcmp = ɵɵdefineComponent({ type: MyComp, selectors: [['comp']], decls: 1, vars: 0, - template: function CompTemplate(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵtext(0, 'Some text'); - } - }, - hostBindings: function HostListenerDir_HostBindings(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵlistener('click', function() { return ctx.onClick(); }); - } - } + template: + function CompTemplate(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵtext(0, 'Some text'); + } + }, + hostBindings: + function HostListenerDir_HostBindings(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵlistener('click', function() { + return ctx.onClick(); + }); + } + } }); } @@ -555,7 +605,7 @@ describe('event listeners', () => { it('should support global host listeners on components', () => { const fixture = new ComponentFixture(MyCompWithGlobalListeners); - const doc = fixture.hostElement.ownerDocument !; + const doc = fixture.hostElement.ownerDocument!; dispatchEvent(doc, 'custom'); expect(events).toEqual(['component - document:custom']); @@ -572,17 +622,24 @@ describe('event listeners', () => { class HostListenerDir { /* @HostListener('click') */ - onClick() { events.push('click!'); } + onClick() { + events.push('click!'); + } - static ɵfac = function HostListenerDir_Factory() { return new HostListenerDir(); }; + static ɵfac = function HostListenerDir_Factory() { + return new HostListenerDir(); + }; static ɵdir = ɵɵdefineDirective({ type: HostListenerDir, selectors: [['', 'hostListenerDir', '']], - hostBindings: function HostListenerDir_HostBindings(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵlistener('click', function() { return ctx.onClick(); }); - } - } + hostBindings: + function HostListenerDir_HostBindings(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵlistener('click', function() { + return ctx.onClick(); + }); + } + } }); } @@ -592,7 +649,7 @@ describe('event listeners', () => { ɵɵelementEnd(); }, () => {}, 2, 0, [HostListenerDir], null, null, undefined, [['hostListenerDir', '']]); - const button = fixture.hostElement.querySelector('button') !; + const button = fixture.hostElement.querySelector('button')!; button.click(); expect(events).toEqual(['click!']); @@ -606,7 +663,7 @@ describe('event listeners', () => { ɵɵelement(0, 'div', 0); }, () => {}, 1, 0, [GlobalHostListenerDir], null, null, undefined, [['hostListenerDir', '']]); - const doc = fixture.hostElement.ownerDocument !; + const doc = fixture.hostElement.ownerDocument!; dispatchEvent(doc, 'custom'); expect(events).toEqual(['directive - document:custom']); @@ -623,7 +680,9 @@ describe('event listeners', () => { counter = 0; data = {a: 1, b: 2}; - onClick(a: any, b: any) { this.counter += a + b; } + onClick(a: any, b: any) { + this.counter += a + b; + } static ɵfac = () => new MyComp(); static ɵcmp = ɵɵdefineComponent({ @@ -632,22 +691,25 @@ describe('event listeners', () => { decls: 2, vars: 0, /** <button (click)="onClick(data.a, data.b)"> Click me </button> */ - template: function CompTemplate(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'button'); - { - ɵɵlistener('click', function() { return ctx.onClick(ctx.data.a, ctx.data.b); }); - ɵɵtext(1, 'Click me'); + template: + function CompTemplate(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'button'); + { + ɵɵlistener('click', function() { + return ctx.onClick(ctx.data.a, ctx.data.b); + }); + ɵɵtext(1, 'Click me'); + } + ɵɵelementEnd(); + } } - ɵɵelementEnd(); - } - } }); } const fixture = new ComponentFixture(MyComp); const comp = fixture.component; - const button = fixture.hostElement.querySelector('button') !; + const button = fixture.hostElement.querySelector('button')!; button.click(); expect(comp.counter).toEqual(3); @@ -657,14 +719,13 @@ describe('event listeners', () => { }); it('should destroy listeners in nested views', () => { - /** - * % if (showing) { - * Hello - * % if (button) { - * <button (click)="onClick()"> Click </button> - * % } - * % } + * % if (showing) { + * Hello + * % if (button) { + * <button (click)="onClick()"> Click </button> + * % } + * % } */ function Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { @@ -687,7 +748,9 @@ describe('event listeners', () => { if (rf1 & RenderFlags.Create) { ɵɵelementStart(0, 'button'); { - ɵɵlistener('click', function() { return ctx.onClick(); }); + ɵɵlistener('click', function() { + return ctx.onClick(); + }); ɵɵtext(1, 'Click'); } ɵɵelementEnd(); @@ -704,9 +767,16 @@ describe('event listeners', () => { } } - const comp = {showing: true, counter: 0, button: true, onClick: function() { this.counter++; }}; + const comp = { + showing: true, + counter: 0, + button: true, + onClick: function() { + this.counter++; + } + }; renderToHtml(Template, comp, 1); - const button = containerEl.querySelector('button') !; + const button = containerEl.querySelector('button')!; button.click(); expect(comp.counter).toEqual(1); @@ -719,13 +789,12 @@ describe('event listeners', () => { }); it('should destroy listeners in component views', () => { - /** - * % if (showing) { - * Hello - * <comp></comp> - * <comp></comp> - * % } + * % if (showing) { + * Hello + * <comp></comp> + * <comp></comp> + * % } * * comp: * <button (click)="onClick()"> Click </button> @@ -753,28 +822,30 @@ describe('event listeners', () => { const ctx = {showing: true}; renderToHtml(Template, ctx, 1, 0, [MyComp]); - const buttons = containerEl.querySelectorAll('button') !; + const buttons = containerEl.querySelectorAll('button')!; buttons[0].click(); - expect(comps[0] !.counter).toEqual(1); + expect(comps[0]!.counter).toEqual(1); buttons[1].click(); - expect(comps[1] !.counter).toEqual(1); + expect(comps[1]!.counter).toEqual(1); // the child view listener should be removed when the parent view is removed ctx.showing = false; renderToHtml(Template, ctx, 1, 0, [MyComp]); buttons[0].click(); buttons[1].click(); - expect(comps[0] !.counter).toEqual(1); - expect(comps[1] !.counter).toEqual(1); + expect(comps[0]!.counter).toEqual(1); + expect(comps[1]!.counter).toEqual(1); }); it('should destroy global listeners in component views', () => { const ctx = {showing: true}; const fixture = new TemplateFixture( - () => { ɵɵcontainer(0); }, + () => { + ɵɵcontainer(0); + }, () => { ɵɵcontainerRefreshStart(0); { @@ -790,7 +861,7 @@ describe('event listeners', () => { }, 1, 0, [MyCompWithGlobalListeners]); - const body = fixture.hostElement.ownerDocument !.body; + const body = fixture.hostElement.ownerDocument!.body; body.click(); expect(events).toEqual(['component - body:click']); @@ -839,7 +910,9 @@ describe('event listeners', () => { if (rf1 & RenderFlags.Create) { ɵɵelementStart(0, 'button'); { - ɵɵlistener('click', function() { return ctx.counter1++; }); + ɵɵlistener('click', function() { + return ctx.counter1++; + }); ɵɵtext(1, 'Click'); } ɵɵelementEnd(); @@ -855,7 +928,9 @@ describe('event listeners', () => { if (rf1 & RenderFlags.Create) { ɵɵelementStart(0, 'button'); { - ɵɵlistener('click', function() { return ctx.counter2++; }); + ɵɵlistener('click', function() { + return ctx.counter2++; + }); ɵɵtext(1, 'Click'); } ɵɵelementEnd(); @@ -874,7 +949,7 @@ describe('event listeners', () => { const ctx = {condition: true, counter1: 0, counter2: 0, sub1: true, sub2: true}; renderToHtml(Template, ctx, 1); - const buttons = containerEl.querySelectorAll('button') !; + const buttons = containerEl.querySelectorAll('button')!; buttons[0].click(); expect(ctx.counter1).toEqual(1); @@ -889,7 +964,6 @@ describe('event listeners', () => { buttons[1].click(); expect(ctx.counter1).toEqual(1); expect(ctx.counter2).toEqual(1); - }); it('should support local refs in listeners', () => { @@ -904,7 +978,9 @@ describe('event listeners', () => { class App { comp: any = null; - onClick(comp: any) { this.comp = comp; } + onClick(comp: any) { + this.comp = comp; + } static ɵfac = () => new App(); static ɵcmp = ɵɵdefineComponent({ @@ -913,24 +989,25 @@ describe('event listeners', () => { decls: 3, vars: 0, consts: [['comp', '']], - template: (rf: RenderFlags, ctx: App) => { - if (rf & RenderFlags.Create) { - const state = ɵɵgetCurrentView(); - ɵɵelement(0, 'comp', null, 0); - ɵɵelementStart(2, 'button'); - { - ɵɵlistener('click', function() { - ɵɵrestoreView(state); - const comp = ɵɵreference(1); - return ctx.onClick(comp); - }); - } - ɵɵelementEnd(); - } + template: + (rf: RenderFlags, ctx: App) => { + if (rf & RenderFlags.Create) { + const state = ɵɵgetCurrentView(); + ɵɵelement(0, 'comp', null, 0); + ɵɵelementStart(2, 'button'); + { + ɵɵlistener('click', function() { + ɵɵrestoreView(state); + const comp = ɵɵreference(1); + return ctx.onClick(comp); + }); + } + ɵɵelementEnd(); + } - // testing only - compInstance = getDirectiveOnNode(0); - }, + // testing only + compInstance = getDirectiveOnNode(0); + }, directives: [Comp] }); } @@ -942,5 +1019,4 @@ describe('event listeners', () => { button.click(); expect(fixture.component.comp).toEqual(compInstance); }); - }); diff --git a/packages/core/test/render3/metadata_spec.ts b/packages/core/test/render3/metadata_spec.ts index 7a124d9ee160d..0f9a7d4f132a1 100644 --- a/packages/core/test/render3/metadata_spec.ts +++ b/packages/core/test/render3/metadata_spec.ts @@ -31,33 +31,33 @@ function metadataOf(value: Type<any>): HasMetadata { describe('render3 setClassMetadata()', () => { it('should set decorator metadata on a type', () => { - const Foo = metadataOf(class Foo{}); + const Foo = metadataOf(class Foo {}); setClassMetadata(Foo, [{type: 'test', args: ['arg']}], null, null); expect(Foo.decorators).toEqual([{type: 'test', args: ['arg']}]); }); it('should merge decorator metadata on a type', () => { - const Foo = metadataOf(class Foo{}); + const Foo = metadataOf(class Foo {}); Foo.decorators = [{type: 'first'}]; setClassMetadata(Foo, [{type: 'test', args: ['arg']}], null, null); expect(Foo.decorators).toEqual([{type: 'first'}, {type: 'test', args: ['arg']}]); }); it('should set ctor parameter metadata on a type', () => { - const Foo = metadataOf(class Foo{}); + const Foo = metadataOf(class Foo {}); Foo.ctorParameters = () => [{type: 'initial'}]; setClassMetadata(Foo, null, () => [{type: 'test'}], null); expect(Foo.ctorParameters()).toEqual([{type: 'test'}]); }); it('should set parameter decorator metadata on a type', () => { - const Foo = metadataOf(class Foo{}); + const Foo = metadataOf(class Foo {}); setClassMetadata(Foo, null, null, {field: [{type: 'test', args: ['arg']}]}); expect(Foo.propDecorators).toEqual({field: [{type: 'test', args: ['arg']}]}); }); it('should merge parameter decorator metadata on a type', () => { - const Foo = metadataOf(class Foo{}); + const Foo = metadataOf(class Foo {}); Foo.propDecorators = {initial: [{type: 'first'}]}; setClassMetadata(Foo, null, null, {field: [{type: 'test', args: ['arg']}]}); expect(Foo.propDecorators) diff --git a/packages/core/test/render3/node_selector_matcher_spec.ts b/packages/core/test/render3/node_selector_matcher_spec.ts index 17c8550a62e50..5565f8a867b2d 100644 --- a/packages/core/test/render3/node_selector_matcher_spec.ts +++ b/packages/core/test/render3/node_selector_matcher_spec.ts @@ -12,24 +12,21 @@ import {AttributeMarker, TAttributes, TNode, TNodeType} from '../../src/render3/ import {CssSelector, CssSelectorList, SelectorFlags} from '../../src/render3/interfaces/projection'; import {extractAttrsAndClassesFromSelector, getProjectAsAttrValue, isNodeMatchingSelector, isNodeMatchingSelectorList, stringifyCSSSelectorList} from '../../src/render3/node_selector_matcher'; -function testLStaticData(tagName: string, attrs: TAttributes | null): TNode { - return createTNode(null !, null, TNodeType.Element, 0, tagName, attrs); +function testLStaticData(tagName: string, attrs: TAttributes|null): TNode { + return createTNode(null!, null, TNodeType.Element, 0, tagName, attrs); } describe('css selector matching', () => { function isMatching( - tagName: string, attrsOrTNode: TAttributes | TNode | null, selector: CssSelector): boolean { + tagName: string, attrsOrTNode: TAttributes|TNode|null, selector: CssSelector): boolean { const tNode = (!attrsOrTNode || Array.isArray(attrsOrTNode)) ? - createTNode(null !, null, TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes) : + createTNode(null!, null, TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes) : (attrsOrTNode as TNode); return isNodeMatchingSelector(tNode, selector, true); } describe('isNodeMatchingSimpleSelector', () => { - - describe('element matching', () => { - it('should match element name only if names are the same', () => { expect(isMatching('span', null, ['span'])) .toBeTruthy(`Selector 'span' should match <span>`); @@ -55,11 +52,9 @@ describe('css selector matching', () => { }); describe('attributes matching', () => { - // TODO: do we need to differentiate no value and empty value? that is: title vs. title="" ? it('should match single attribute without value', () => { - expect(isMatching('span', ['title', ''], [ '', 'title', '' ])).toBeTruthy(`Selector '[title]' should match <span title>`); @@ -81,10 +76,13 @@ describe('css selector matching', () => { ])).toBeFalsy(`Selector '[other]' should NOT match <span title="">'`); }); - it('should match namespaced attributes', () => { + // TODO: this case will not work, need more discussion + // https://github.com/angular/angular/pull/34625#discussion_r401791275 + xit('should match namespaced attributes', () => { expect(isMatching( - 'span', [AttributeMarker.NamespaceURI, 'http://some/uri', 'title', 'name'], - ['', 'title', ''])); + 'span', [AttributeMarker.NamespaceURI, 'http://some/uri', 'title', 'name'], + ['', 'title', ''])) + .toBeTruthy(); }); it('should match selector with one attribute without value when element has several attributes', @@ -226,7 +224,6 @@ describe('css selector matching', () => { }); describe('class matching', () => { - it('should match with a class selector when an element has multiple classes', () => { expect(isMatching('span', ['class', 'foo bar'], [ '', SelectorFlags.CLASS, 'foo' @@ -326,7 +323,6 @@ describe('css selector matching', () => { }); describe('negations', () => { - it('should match when negation part is null', () => { expect(isMatching('span', null, ['span'])).toBeTruthy(`Selector 'span' should match <span>`); }); @@ -434,13 +430,11 @@ describe('css selector matching', () => { expect(isMatching('div', ['name', 'name', 'title', '', 'class', 'foo bar'], selector)) .toBeFalsy(); }); - }); describe('isNodeMatchingSelectorList', () => { - function isAnyMatching( - tagName: string, attrs: string[] | null, selector: CssSelectorList): boolean { + tagName: string, attrs: string[]|null, selector: CssSelectorList): boolean { return isNodeMatchingSelectorList(testLStaticData(tagName, attrs), selector, false); } @@ -466,16 +460,18 @@ describe('css selector matching', () => { }); describe('reading the ngProjectAs attribute value', function() { - - function testTNode(attrs: TAttributes | null) { return testLStaticData('tag', attrs); } + function testTNode(attrs: TAttributes|null) { + return testLStaticData('tag', attrs); + } it('should get ngProjectAs value if present', function() { expect(getProjectAsAttrValue(testTNode([AttributeMarker.ProjectAs, ['tag', 'foo', 'bar']]))) .toEqual(['tag', 'foo', 'bar']); }); - it('should return null if there are no attributes', - function() { expect(getProjectAsAttrValue(testTNode(null))).toBe(null); }); + it('should return null if there are no attributes', function() { + expect(getProjectAsAttrValue(testTNode(null))).toBe(null); + }); it('should return if ngProjectAs is not present', function() { expect(getProjectAsAttrValue(testTNode(['foo', 'bar']))).toBe(null); @@ -484,15 +480,13 @@ describe('css selector matching', () => { it('should not accidentally identify ngProjectAs in attribute values', function() { expect(getProjectAsAttrValue(testTNode(['foo', AttributeMarker.ProjectAs]))).toBe(null); }); - }); - }); describe('stringifyCSSSelectorList', () => { - - it('should stringify selector with a tag name only', - () => { expect(stringifyCSSSelectorList([['button']])).toBe('button'); }); + it('should stringify selector with a tag name only', () => { + expect(stringifyCSSSelectorList([['button']])).toBe('button'); + }); it('should stringify selector with attributes', () => { expect(stringifyCSSSelectorList([['', 'id', '']])).toBe('[id]'); @@ -616,4 +610,4 @@ describe('extractAttrsAndClassesFromSelector', () => { expect(extracted.classes).toEqual(classes as string[]); }); }); -}); \ No newline at end of file +}); diff --git a/packages/core/test/render3/outputs_spec.ts b/packages/core/test/render3/outputs_spec.ts index 9e5f95f4c0d65..fd009950bd698 100644 --- a/packages/core/test/render3/outputs_spec.ts +++ b/packages/core/test/render3/outputs_spec.ts @@ -59,7 +59,9 @@ describe('outputs', () => { if (rf & RenderFlags.Create) { ɵɵelementStart(0, 'button'); { - ɵɵlistener('click', function() { return ctx.onClick(); }); + ɵɵlistener('click', function() { + return ctx.onClick(); + }); ɵɵtext(1, 'Click me'); } ɵɵelementEnd(); @@ -73,7 +75,9 @@ describe('outputs', () => { if (rf1 & RenderFlags.Create) { ɵɵelementStart(0, 'button-toggle'); { - ɵɵlistener('change', function() { return ctx.onChange(); }); + ɵɵlistener('change', function() { + return ctx.onChange(); + }); } ɵɵelementEnd(); } @@ -82,7 +86,9 @@ describe('outputs', () => { if (ɵɵembeddedViewStart(1, 1, 0)) { ɵɵelementStart(0, 'div', 0); { - ɵɵlistener('change', function() { return ctx.onChange(); }); + ɵɵlistener('change', function() { + return ctx.onChange(); + }); } ɵɵelementEnd(); } @@ -98,15 +104,14 @@ describe('outputs', () => { const attrs = [['otherDir', '']]; renderToHtml(Template, ctx, 3, 0, deps, null, null, false, attrs); - buttonToggle !.change.next(); + buttonToggle!.change.next(); expect(counter).toEqual(1); ctx.condition = false; renderToHtml(Template, ctx, 3, 0, deps, null, null, false, attrs); expect(counter).toEqual(1); - otherDir !.changeStream.next(); + otherDir!.changeStream.next(); expect(counter).toEqual(2); }); - }); diff --git a/packages/core/test/render3/perf/directive_instantiate/index.ts b/packages/core/test/render3/perf/directive_instantiate/index.ts index ea3d5263c512b..c5d6a47cd4a6a 100644 --- a/packages/core/test/render3/perf/directive_instantiate/index.ts +++ b/packages/core/test/render3/perf/directive_instantiate/index.ts @@ -78,7 +78,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null); -const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( TViewType.Embedded, -1, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, [['position', 'top', 3, 'tooltip']]); diff --git a/packages/core/test/render3/perf/duplicate_map_based_style_and_class_bindings/index.ts b/packages/core/test/render3/perf/duplicate_map_based_style_and_class_bindings/index.ts index b8d3a02b31aa5..0d234a1205116 100644 --- a/packages/core/test/render3/perf/duplicate_map_based_style_and_class_bindings/index.ts +++ b/packages/core/test/render3/perf/duplicate_map_based_style_and_class_bindings/index.ts @@ -1,10 +1,10 @@ /** -* @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 -*/ + * @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 {ɵɵadvance} from '../../../../src/render3/instructions/advance'; import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element'; import {refreshView} from '../../../../src/render3/instructions/shared'; diff --git a/packages/core/test/render3/perf/element_text_create/index.ts b/packages/core/test/render3/perf/element_text_create/index.ts index 65cc7ec7ba3a3..2851c278fd4dd 100644 --- a/packages/core/test/render3/perf/element_text_create/index.ts +++ b/packages/core/test/render3/perf/element_text_create/index.ts @@ -67,7 +67,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null); -const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( TViewType.Embedded, -1, testTemplate, 21, 0, null, null, null, null, [[ 'name1', 'value1', 'name2', 'value2', 'name3', 'value3', 'name4', 'value4', 'name5', 'value5' diff --git a/packages/core/test/render3/perf/host_binding/index.ts b/packages/core/test/render3/perf/host_binding/index.ts index e42e9a627a60a..ec41b9d83e2a6 100644 --- a/packages/core/test/render3/perf/host_binding/index.ts +++ b/packages/core/test/render3/perf/host_binding/index.ts @@ -31,19 +31,24 @@ function generateHostBindingDirDef() { } `; class HostBindingDir { - static ɵfac() { return new HostBindingDir(); } + static ɵfac() { + return new HostBindingDir(); + } static ɵdir = ɵɵdefineDirective({ type: HostBindingDir, selectors: [['', 'hostBindingDir', '']], hostVars: 2, - hostBindings: function(rf: RenderFlags, ctx: any) { - if (rf & 1) { - ɵɵlistener('click', function() { return ctx.onClick(); }); - } - if (rf & 2) { - ɵɵhostProperty('data-a', ctx.exp); - } - } + hostBindings: + function(rf: RenderFlags, ctx: any) { + if (rf & 1) { + ɵɵlistener('click', function() { + return ctx.onClick(); + }); + } + if (rf & 2) { + ɵɵhostProperty('data-a', ctx.exp); + } + } }); exp = 'string-exp'; diff --git a/packages/core/test/render3/perf/listeners/index.ts b/packages/core/test/render3/perf/listeners/index.ts index 0c6f228f49061..f11e6d3910bda 100644 --- a/packages/core/test/render3/perf/listeners/index.ts +++ b/packages/core/test/render3/perf/listeners/index.ts @@ -69,7 +69,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null); -const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( TViewType.Embedded, -1, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); diff --git a/packages/core/test/render3/perf/micro_bench.ts b/packages/core/test/render3/perf/micro_bench.ts index 2f7e714fc0679..b9c12454fcb45 100644 --- a/packages/core/test/render3/perf/micro_bench.ts +++ b/packages/core/test/render3/perf/micro_bench.ts @@ -64,8 +64,8 @@ export function createBenchmark(benchmarkName: string): Benchmark { } if (!runAgain) { // tslint:disable-next-line:no-console - console.log( - ` ${formatTime(profile.bestTime)} (count: ${profile.sampleCount}, iterations: ${profile.iterationCount})`); + console.log(` ${formatTime(profile.bestTime)} (count: ${ + profile.sampleCount}, iterations: ${profile.iterationCount})`); } } iterationCounter = profile.iterationCount; @@ -91,11 +91,14 @@ export function createBenchmark(benchmarkName: string): Benchmark { return (previous.bestTime < current.bestTime) ? previous : current; }); const unitOffset = findUnit(fastest.bestTime); - (fn || console.info)(`\nBenchmark: ${benchmarkName}\n${profiles.map((profile: Profile) => { - const time = formatTime(profile.bestTime, unitOffset); - const percent = formatPercent(1 - profile.bestTime / fastest.bestTime); - return ` ${profile.profileName}: ${time}(${percent}) `; - }).join('\n')}`); + (fn || console.info)(`\nBenchmark: ${benchmarkName}\n${ + profiles + .map((profile: Profile) => { + const time = formatTime(profile.bestTime, unitOffset); + const percent = formatPercent(1 - profile.bestTime / fastest.bestTime); + return ` ${profile.profileName}: ${time}(${percent}) `; + }) + .join('\n')}`); }; return benchmark; } diff --git a/packages/core/test/render3/perf/ng_template/index.ts b/packages/core/test/render3/perf/ng_template/index.ts index 324bd65849d86..8c9754ed3aa0f 100644 --- a/packages/core/test/render3/perf/ng_template/index.ts +++ b/packages/core/test/render3/perf/ng_template/index.ts @@ -62,7 +62,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null); -const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( TViewType.Root, -1, testTemplate, 2, 0, [NgIfLike.ɵdir], null, null, null, [['viewManipulation', '']]); diff --git a/packages/core/test/render3/perf/noop_renderer.ts b/packages/core/test/render3/perf/noop_renderer.ts index ef93923c16e71..1401df52ea683 100644 --- a/packages/core/test/render3/perf/noop_renderer.ts +++ b/packages/core/test/render3/perf/noop_renderer.ts @@ -5,33 +5,49 @@ * 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 {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3} from '../../../src/render3/interfaces/renderer'; +import {ProceduralRenderer3, RComment, RElement, Renderer3, RendererFactory3, RendererStyleFlags3, RNode, RText} from '../../../src/render3/interfaces/renderer'; export class MicroBenchmarkRenderNode implements RNode, RComment, RText { textContent: string|null = null; parentNode: RNode|null = null; parentElement: RElement|null = null; nextSibling: RNode|null = null; - removeChild(oldChild: RNode): RNode { return oldChild; } + removeChild(oldChild: RNode): RNode { + return oldChild; + } insertBefore(newChild: RNode, refChild: RNode|null, isViewRoot: boolean): void {} - appendChild(newChild: RNode): RNode { return newChild; } + appendChild(newChild: RNode): RNode { + return newChild; + } className: string = ''; } export class MicroBenchmarkRenderer implements ProceduralRenderer3 { - destroy(): void { throw new Error('Method not implemented.'); } - createComment(value: string): RComment { return new MicroBenchmarkRenderNode(); } + destroy(): void { + throw new Error('Method not implemented.'); + } + createComment(value: string): RComment { + return new MicroBenchmarkRenderNode(); + } createElement(name: string, namespace?: string|null|undefined): RElement { return new MicroBenchmarkRenderNode() as any as RElement; } - createText(value: string): RText { return new MicroBenchmarkRenderNode(); } + createText(value: string): RText { + return new MicroBenchmarkRenderNode(); + } destroyNode?: ((node: RNode) => void)|null|undefined; appendChild(parent: RElement, newChild: RNode): void {} insertBefore(parent: RNode, newChild: RNode, refChild: RNode|null): void {} removeChild(parent: RElement, oldChild: RNode, isHostElement?: boolean|undefined): void {} - selectRootElement(selectorOrNode: any): RElement { throw new Error('Method not implemented.'); } - parentNode(node: RNode): RElement|null { throw new Error('Method not implemented.'); } - nextSibling(node: RNode): RNode|null { throw new Error('Method not implemented.'); } + selectRootElement(selectorOrNode: any): RElement { + throw new Error('Method not implemented.'); + } + parentNode(node: RNode): RElement|null { + throw new Error('Method not implemented.'); + } + nextSibling(node: RNode): RNode|null { + throw new Error('Method not implemented.'); + } setAttribute(el: RElement, name: string, value: string, namespace?: string|null|undefined): void { if (name === 'class' && isOurNode(el)) { el.className = value; @@ -51,7 +67,9 @@ export class MicroBenchmarkRenderer implements ProceduralRenderer3 { setStyle(el: RElement, style: string, value: any, flags?: RendererStyleFlags3|undefined): void {} removeStyle(el: RElement, style: string, flags?: RendererStyleFlags3|undefined): void {} setProperty(el: RElement, name: string, value: any): void {} - setValue(node: RComment|RText, value: string): void { node.textContent = value; } + setValue(node: RComment|RText, value: string): void { + node.textContent = value; + } listen( target: RNode|'document'|'window'|'body', eventName: string, callback: (event: any) => boolean | void): () => void { diff --git a/packages/core/test/render3/perf/noop_renderer_spec.ts b/packages/core/test/render3/perf/noop_renderer_spec.ts index 63cd2f1a7c90f..d569096d7441c 100644 --- a/packages/core/test/render3/perf/noop_renderer_spec.ts +++ b/packages/core/test/render3/perf/noop_renderer_spec.ts @@ -8,7 +8,7 @@ import {ProceduralRenderer3} from '@angular/core/src/render3/interfaces/renderer'; -import {MicroBenchmarkRenderNode, MicroBenchmarkRendererFactory} from './noop_renderer'; +import {MicroBenchmarkRendererFactory, MicroBenchmarkRenderNode} from './noop_renderer'; describe('MicroBenchmarkRenderNode', () => { const renderer = diff --git a/packages/core/test/render3/perf/setup.ts b/packages/core/test/render3/perf/setup.ts index 51b2579428f2c..cd90954f73f3c 100644 --- a/packages/core/test/render3/perf/setup.ts +++ b/packages/core/test/render3/perf/setup.ts @@ -8,7 +8,7 @@ import {addToViewTree, createLContainer, createLView, createTNode, createTView, getOrCreateTNode, refreshView, renderView} from '../../../src/render3/instructions/shared'; import {ComponentTemplate, DirectiveDefList} from '../../../src/render3/interfaces/definition'; import {TAttributes, TNodeType, TViewNode} from '../../../src/render3/interfaces/node'; -import {RendererFactory3, domRendererFactory3} from '../../../src/render3/interfaces/renderer'; +import {domRendererFactory3, RendererFactory3} from '../../../src/render3/interfaces/renderer'; import {LView, LViewFlags, TVIEW, TView, TViewType} from '../../../src/render3/interfaces/view'; import {insertView} from '../../../src/render3/node_manipulation'; @@ -28,9 +28,9 @@ export function createAndRenderLView( } export function setupRootViewWithEmbeddedViews( - templateFn: ComponentTemplate<any>| null, decls: number, vars: number, noOfViews: number, - embeddedViewContext: any = {}, consts: TAttributes[] | null = null, - directiveRegistry: DirectiveDefList | null = null): LView { + templateFn: ComponentTemplate<any>|null, decls: number, vars: number, noOfViews: number, + embeddedViewContext: any = {}, consts: TAttributes[]|null = null, + directiveRegistry: DirectiveDefList|null = null): LView { return setupTestHarness( templateFn, decls, vars, noOfViews, embeddedViewContext, consts, directiveRegistry) .hostLView; @@ -45,9 +45,9 @@ export interface TestHarness { } export function setupTestHarness( - templateFn: ComponentTemplate<any>| null, decls: number, vars: number, noOfViews: number, - embeddedViewContext: any = {}, consts: TAttributes[] | null = null, - directiveRegistry: DirectiveDefList | null = null): TestHarness { + templateFn: ComponentTemplate<any>|null, decls: number, vars: number, noOfViews: number, + embeddedViewContext: any = {}, consts: TAttributes[]|null = null, + directiveRegistry: DirectiveDefList|null = null): TestHarness { // Create a root view with a container const hostTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, consts); const tContainerNode = getOrCreateTNode(hostTView, null, 0, TNodeType.Container, null, null); diff --git a/packages/core/test/render3/perf/shared.ts b/packages/core/test/render3/perf/shared.ts index 353a03046eacf..55038df751b60 100644 --- a/packages/core/test/render3/perf/shared.ts +++ b/packages/core/test/render3/perf/shared.ts @@ -19,5 +19,7 @@ export function defineBenchmarkTestDirective( } class FakeDirectiveType { - static ɵfac = () => { return {}; }; + static ɵfac = () => { + return {}; + } } diff --git a/packages/core/test/render3/perf/view_destroy_hook/index.ts b/packages/core/test/render3/perf/view_destroy_hook/index.ts index 67f61095603c6..98aa6de0b97b5 100644 --- a/packages/core/test/render3/perf/view_destroy_hook/index.ts +++ b/packages/core/test/render3/perf/view_destroy_hook/index.ts @@ -55,7 +55,7 @@ const rootLView = createLView( null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, LViewFlags.IsRoot, null, null); -const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; +const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( TViewType.Embedded, -1, testTemplate, 21, 10, [ToDestroy.ɵdir], null, null, null, [['to-destroy']]); diff --git a/packages/core/test/render3/pipe_spec.ts b/packages/core/test/render3/pipe_spec.ts index 06584ce27fdd6..81312294362ff 100644 --- a/packages/core/test/render3/pipe_spec.ts +++ b/packages/core/test/render3/pipe_spec.ts @@ -32,9 +32,13 @@ describe('pipe', () => { describe('WrappedValue', () => { @Pipe({name: 'wrappingPipe'}) class WrappingPipe implements PipeTransform { - transform(value: any) { return new WrappedValue('Bar'); } + transform(value: any) { + return new WrappedValue('Bar'); + } - static ɵfac = function WrappingPipe_Factory() { return new WrappingPipe(); }; + static ɵfac = function WrappingPipe_Factory() { + return new WrappingPipe(); + }; static ɵpipe = ɵɵdefinePipe({name: 'wrappingPipe', type: WrappingPipe, pure: false}); } @@ -43,7 +47,9 @@ describe('pipe', () => { ɵɵpipe(1, 'wrappingPipe'); } - function updateTemplate() { ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), ''); } + function updateTemplate() { + ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), ''); + } it('should unwrap', () => { const fixture = @@ -56,7 +62,7 @@ describe('pipe', () => { new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]); expect(fixture.html).toEqual('Bar'); - fixture.hostElement.childNodes[0] !.textContent = 'Foo'; + fixture.hostElement.childNodes[0]!.textContent = 'Foo'; expect(fixture.html).toEqual('Foo'); fixture.update(); @@ -71,7 +77,9 @@ describe('pipe', () => { it('should be able to use DI in a Pipe that extends an Injectable', () => { @Injectable({providedIn: 'root'}) class SayHelloService { - getHello() { return 'Hello there'; } + getHello() { + return 'Hello there'; + } static ɵfac = () => new SayHelloService(); static ɵprov = ɵɵdefineInjectable( {token: SayHelloService, factory: SayHelloService.ɵfac, providedIn: 'root'}); @@ -80,13 +88,15 @@ describe('pipe', () => { @Injectable() class ParentPipe { constructor(protected sayHelloService: SayHelloService) {} - static ɵfac = (t?: any) => new (t || ParentPipe)(ɵɵinject(SayHelloService)); + static ɵfac = (t?: any) => new(t || ParentPipe)(ɵɵinject(SayHelloService)); static ɵprov = ɵɵdefineInjectable({token: ParentPipe, factory: ParentPipe.ɵfac}); } @Pipe({name: 'sayHello', pure: true}) class SayHelloPipe extends ParentPipe implements PipeTransform { - transform() { return this.sayHelloService.getHello(); } + transform() { + return this.sayHelloService.getHello(); + } static ɵfac = (t?: any) => ɵɵgetInheritedFactory(t || SayHelloPipe)(SayHelloPipe); static ɵpipe = ɵɵdefinePipe({name: 'sayHello', type: SayHelloPipe, pure: true}); } @@ -96,10 +106,11 @@ describe('pipe', () => { ɵɵtext(0); ɵɵpipe(1, 'sayHello'); }, - () => { ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), ''); }, 2, 3, undefined, - [SayHelloPipe]); + () => { + ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), ''); + }, + 2, 3, undefined, [SayHelloPipe]); expect(fixture.html).toBe('Hello there'); }); - }); diff --git a/packages/core/test/render3/providers_spec.ts b/packages/core/test/render3/providers_spec.ts index 566bcd8538983..6f3e25d003404 100644 --- a/packages/core/test/render3/providers_spec.ts +++ b/packages/core/test/render3/providers_spec.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component as _Component, ComponentFactoryResolver, ElementRef, InjectFlags, Injectable as _Injectable, InjectionToken, InjectorType, Provider, RendererFactory2, ViewContainerRef, ɵNgModuleDef as NgModuleDef, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵinject} from '../../src/core'; +import {Component as _Component, ComponentFactoryResolver, ElementRef, Injectable as _Injectable, InjectFlags, InjectionToken, InjectorType, Provider, RendererFactory2, ViewContainerRef, ɵNgModuleDef as NgModuleDef, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵinject} from '../../src/core'; import {forwardRef} from '../../src/di/forward_ref'; import {createInjector} from '../../src/di/r3_injector'; -import {injectComponentFactoryResolver, ɵɵProvidersFeature, ɵɵadvance, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵtextInterpolate1} from '../../src/render3/index'; +import {injectComponentFactoryResolver, ɵɵadvance, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵProvidersFeature, ɵɵtextInterpolate1} from '../../src/render3/index'; import {ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {NgModuleFactory} from '../../src/render3/ng_module_ref'; @@ -30,7 +30,9 @@ const Injectable: typeof _Injectable = function(...args: any[]): any { describe('providers', () => { describe('should support all types of Provider:', () => { - abstract class Greeter { abstract greet: string; } + abstract class Greeter { + abstract greet: string; + } const GREETER = new InjectionToken<Greeter>('greeter'); @@ -50,13 +52,17 @@ describe('providers', () => { } class GreeterProvider { - provide() { return 'Provided'; } + provide() { + return 'Provided'; + } } @Injectable() class GreeterInj implements Greeter { public greet: string; - constructor(private provider: GreeterProvider) { this.greet = this.provider.provide(); } + constructor(private provider: GreeterProvider) { + this.greet = this.provider.provide(); + } static ɵprov = ɵɵdefineInjectable({ token: GreeterInj, @@ -68,8 +74,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [GreeterClass], - componentAssertion: - () => { expect(ɵɵdirectiveInject(GreeterClass).greet).toEqual('Class'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GreeterClass).greet).toEqual('Class'); + } } }); }); @@ -78,7 +85,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [{provide: GREETER, useValue: {greet: 'Value'}}], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Value'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Value'); + } } }); }); @@ -87,7 +96,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [{provide: GREETER, useClass: GreeterClass}], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); + } } }); }); @@ -96,7 +107,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [GreeterClass, {provide: GREETER, useExisting: GreeterClass}], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); + } } }); }); @@ -105,7 +118,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [GreeterClass, {provide: GREETER, useFactory: () => new GreeterClass()}], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); + } } }); }); @@ -119,7 +134,9 @@ describe('providers', () => { {provide: MESSAGE, useValue: 'Message'}, {provide: GREETER, useClass: GreeterDeps, deps: [MESSAGE]} ], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Message'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Message'); + } } }); }); @@ -131,8 +148,9 @@ describe('providers', () => { {provide: MESSAGE, useValue: 'Message'}, {provide: GREETER, useClass: GreeterBuiltInDeps, deps: [MESSAGE, ElementRef]} ], - componentAssertion: - () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Message from PARENT'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Message from PARENT'); + } } }); }); @@ -144,7 +162,9 @@ describe('providers', () => { {provide: MESSAGE, useValue: 'Message'}, {provide: GREETER, useFactory: (msg: string) => new GreeterDeps(msg), deps: [MESSAGE]} ], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Message'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Message'); + } } }); }); @@ -156,12 +176,13 @@ describe('providers', () => { {provide: MESSAGE, useValue: 'Message'}, { provide: GREETER, useFactory: (msg: string, elementRef: ElementRef) => - new GreeterBuiltInDeps(msg, elementRef), + new GreeterBuiltInDeps(msg, elementRef), deps: [MESSAGE, ElementRef] } ], - componentAssertion: - () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Message from PARENT'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Message from PARENT'); + } } }); }); @@ -170,8 +191,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [GreeterProvider, {provide: GREETER, useClass: GreeterInj}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Provided'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Provided'); + } } }); }); @@ -182,8 +204,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [forwardRef(() => ForLater)], - componentAssertion: - () => { expect(ɵɵdirectiveInject(ForLater) instanceof ForLater).toBeTruthy(); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(ForLater) instanceof ForLater).toBeTruthy(); + } } }); done(); @@ -196,9 +219,15 @@ describe('providers', () => { it('ValueProvider wrapped in forwardRef', () => { expectProvidersScenario({ parent: { - providers: - [{provide: GREETER, useValue: forwardRef(() => { return {greet: 'Value'}; })}], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Value'); } + providers: [{ + provide: GREETER, + useValue: forwardRef(() => { + return {greet: 'Value'}; + }) + }], + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Value'); + } } }); }); @@ -207,7 +236,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [{provide: GREETER, useClass: forwardRef(() => GreeterClass)}], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); + } } }); }); @@ -217,7 +248,9 @@ describe('providers', () => { parent: { providers: [GreeterClass, {provide: GREETER, useExisting: forwardRef(() => GreeterClass)}], - componentAssertion: () => { expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(GREETER).greet).toEqual('Class'); + } } }); }); @@ -233,9 +266,7 @@ describe('providers', () => { } }); }); - }); - }); /* @@ -258,7 +289,9 @@ describe('providers', () => { parent: { providers: [{provide: String, useValue: 'Message 1'}], directiveProviders: [{provide: String, useValue: 'Message 2'}], - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); + } } }); }); @@ -268,7 +301,9 @@ describe('providers', () => { parent: { providers: [{provide: String, useValue: 'Message 1'}], viewProviders: [{provide: String, useValue: 'Message 2'}], - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); + } } }); }); @@ -278,7 +313,9 @@ describe('providers', () => { parent: { directiveProviders: [{provide: String, useValue: 'Message 1'}], viewProviders: [{provide: String, useValue: 'Message 2'}], - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); + } } }); }); @@ -288,7 +325,9 @@ describe('providers', () => { parent: { directive2Providers: [{provide: String, useValue: 'Message 1'}], directiveProviders: [{provide: String, useValue: 'Message 2'}], - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); + } } }); }); @@ -298,7 +337,9 @@ describe('providers', () => { parent: { providers: [{provide: String, useValue: 'Message 1'}, {provide: String, useValue: 'Message 2'}], - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); + } } }); }); @@ -308,7 +349,9 @@ describe('providers', () => { parent: { viewProviders: [{provide: String, useValue: 'Message 1'}, {provide: String, useValue: 'Message 2'}], - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); + } } }); }); @@ -318,7 +361,9 @@ describe('providers', () => { parent: { directiveProviders: [{provide: String, useValue: 'Message 1'}, {provide: String, useValue: 'Message 2'}], - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('Message 2'); + } } }); }); @@ -334,16 +379,28 @@ describe('providers', () => { it('should work without providers nor viewProviders in component', () => { expectProvidersScenario({ parent: { - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); }, - directiveAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + } }, viewChild: { - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); }, - directiveAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + } }, contentChild: { - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); }, - directiveAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + } }, ngModule: MyModule }); @@ -353,22 +410,28 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [{provide: String, useValue: 'From providers'}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + } }, ngModule: MyModule }); @@ -378,19 +441,28 @@ describe('providers', () => { expectProvidersScenario({ parent: { viewProviders: [{provide: String, useValue: 'From viewProviders'}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); }, - directiveAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + } }, contentChild: { - componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); }, - directiveAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual('From module'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From module'); + } }, ngModule: MyModule }); @@ -401,22 +473,28 @@ describe('providers', () => { parent: { providers: [{provide: String, useValue: 'From providers'}], viewProviders: [{provide: String, useValue: 'From viewProviders'}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From providers'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From providers'); + } }, ngModule: MyModule }); @@ -429,22 +507,28 @@ describe('providers', () => { parent: { directiveProviders: [{provide: String, useValue: 'From directive'}], directive2Providers: [{provide: String, useValue: 'Never'}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, ngModule: MyModule }); @@ -456,22 +540,28 @@ describe('providers', () => { providers: [{provide: String, useValue: 'From providers'}], directiveProviders: [{provide: String, useValue: 'From directive'}], directive2Providers: [{provide: String, useValue: 'Never'}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, ngModule: MyModule }); @@ -483,22 +573,28 @@ describe('providers', () => { viewProviders: [{provide: String, useValue: 'From viewProviders'}], directiveProviders: [{provide: String, useValue: 'From directive'}], directive2Providers: [{provide: String, useValue: 'Never'}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, ngModule: MyModule }); @@ -511,22 +607,28 @@ describe('providers', () => { viewProviders: [{provide: String, useValue: 'From viewProviders'}], directiveProviders: [{provide: String, useValue: 'From directive'}], directive2Providers: [{provide: String, useValue: 'Never'}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From viewProviders'); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual('From directive'); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual('From directive'); + } }, ngModule: MyModule }); @@ -546,22 +648,28 @@ describe('providers', () => { it('should work without providers nor viewProviders in component', () => { expectProvidersScenario({ parent: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + } }, ngModule: MyModule }); @@ -571,22 +679,28 @@ describe('providers', () => { expectProvidersScenario({ parent: { providers: [{provide: String, useValue: 'From providers', multi: true}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + } }, ngModule: MyModule }); @@ -596,22 +710,28 @@ describe('providers', () => { expectProvidersScenario({ parent: { viewProviders: [{provide: String, useValue: 'From viewProviders', multi: true}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From viewProviders']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From viewProviders']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + } }, viewChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From viewProviders']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From viewProviders']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From viewProviders']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From viewProviders']); + } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From module']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From module']); + } }, ngModule: MyModule }); @@ -625,8 +745,9 @@ describe('providers', () => { componentAssertion: () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers', 'From viewProviders']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); } + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + } }, viewChild: { componentAssertion: () => { @@ -637,10 +758,12 @@ describe('providers', () => { } }, contentChild: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); }, - directiveAssertion: - () => { expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + }, + directiveAssertion: () => { + expect(ɵɵdirectiveInject(String)).toEqual(['From providers']); + } }, ngModule: MyModule }); @@ -825,8 +948,9 @@ describe('providers', () => { expectProvidersScenario({ parent: { - componentAssertion: - () => { expect(ɵɵdirectiveInject(FooForRoot) instanceof FooForRoot).toBeTruthy(); } + componentAssertion: () => { + expect(ɵɵdirectiveInject(FooForRoot) instanceof FooForRoot).toBeTruthy(); + } } }); }); @@ -868,24 +992,27 @@ describe('providers', () => { constructor(private s: String, private n: Number) {} static ɵfac = - () => { return new Repeated(ɵɵdirectiveInject(String), ɵɵdirectiveInject(Number)); } + () => { + return new Repeated(ɵɵdirectiveInject(String), ɵɵdirectiveInject(Number)); + } static ɵcmp = ɵɵdefineComponent({ type: Repeated, selectors: [['repeated']], decls: 2, vars: 2, - template: function(fs: RenderFlags, ctx: Repeated) { - if (fs & RenderFlags.Create) { - ɵɵtext(0); - ɵɵtext(1); - } - if (fs & RenderFlags.Update) { - ɵɵtextInterpolate(ctx.s); - ɵɵadvance(1); - ɵɵtextInterpolate(ctx.n); - } - } + template: + function(fs: RenderFlags, ctx: Repeated) { + if (fs & RenderFlags.Create) { + ɵɵtext(0); + ɵɵtext(1); + } + if (fs & RenderFlags.Update) { + ɵɵtextInterpolate(ctx.s); + ɵɵadvance(1); + ɵɵtextInterpolate(ctx.n); + } + } }); } @@ -906,33 +1033,38 @@ describe('providers', () => { selectors: [['component-with-providers']], decls: 2, vars: 0, - template: function(fs: RenderFlags, ctx: ComponentWithProviders) { - if (fs & RenderFlags.Create) { - ɵɵelementStart(0, 'div'); - { ɵɵcontainer(1); } - ɵɵelementEnd(); - } - if (fs & RenderFlags.Update) { - ɵɵcontainerRefreshStart(1); - { - for (let i = 0; i < 3; i++) { - let rf1 = ɵɵembeddedViewStart(1, 1, 0); + template: + function(fs: RenderFlags, ctx: ComponentWithProviders) { + if (fs & RenderFlags.Create) { + ɵɵelementStart(0, 'div'); + { ɵɵcontainer(1); } + ɵɵelementEnd(); + } + if (fs & RenderFlags.Update) { + ɵɵcontainerRefreshStart(1); { - if (rf1 & RenderFlags.Create) { - ɵɵelement(0, 'repeated'); + for (let i = 0; i < 3; i++) { + let rf1 = ɵɵembeddedViewStart(1, 1, 0); + { + if (rf1 & RenderFlags.Create) { + ɵɵelement(0, 'repeated'); + } + } + ɵɵembeddedViewEnd(); } } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } - } - ɵɵcontainerRefreshEnd(); - } - }, - features: [ - ɵɵProvidersFeature( - [{provide: Number, useValue: 1, multi: true}], - [{provide: String, useValue: 'foo'}, {provide: Number, useValue: 2, multi: true}]), - ], + }, + features: + [ + ɵɵProvidersFeature( + [{provide: Number, useValue: 1, multi: true}], + [ + {provide: String, useValue: 'foo'}, + {provide: Number, useValue: 2, multi: true} + ]), + ], directives: [Repeated] }); } @@ -954,29 +1086,36 @@ describe('providers', () => { constructor(private s: String, private n: Number) {} static ɵfac = - () => { return new Repeated(ɵɵdirectiveInject(String), ɵɵdirectiveInject(Number)); } + () => { + return new Repeated(ɵɵdirectiveInject(String), ɵɵdirectiveInject(Number)); + } static ɵcmp = ɵɵdefineComponent({ type: Repeated, selectors: [['repeated']], decls: 2, vars: 2, - template: function(fs: RenderFlags, ctx: Repeated) { - if (fs & RenderFlags.Create) { - ɵɵtext(0); - ɵɵtext(1); - } - if (fs & RenderFlags.Update) { - ɵɵtextInterpolate(ctx.s); - ɵɵadvance(1); - ɵɵtextInterpolate(ctx.n); - } - }, - features: [ - ɵɵProvidersFeature( - [{provide: Number, useValue: 1, multi: true}], - [{provide: String, useValue: 'bar'}, {provide: Number, useValue: 2, multi: true}]), - ], + template: + function(fs: RenderFlags, ctx: Repeated) { + if (fs & RenderFlags.Create) { + ɵɵtext(0); + ɵɵtext(1); + } + if (fs & RenderFlags.Update) { + ɵɵtextInterpolate(ctx.s); + ɵɵadvance(1); + ɵɵtextInterpolate(ctx.n); + } + }, + features: + [ + ɵɵProvidersFeature( + [{provide: Number, useValue: 1, multi: true}], + [ + {provide: String, useValue: 'bar'}, + {provide: Number, useValue: 2, multi: true} + ]), + ], }); } @@ -995,28 +1134,29 @@ describe('providers', () => { selectors: [['component-with-providers']], decls: 2, vars: 0, - template: function(fs: RenderFlags, ctx: ComponentWithProviders) { - if (fs & RenderFlags.Create) { - ɵɵelementStart(0, 'div'); - { ɵɵcontainer(1); } - ɵɵelementEnd(); - } - if (fs & RenderFlags.Update) { - ɵɵcontainerRefreshStart(1); - { - for (let i = 0; i < 3; i++) { - let rf1 = ɵɵembeddedViewStart(1, 1, 0); + template: + function(fs: RenderFlags, ctx: ComponentWithProviders) { + if (fs & RenderFlags.Create) { + ɵɵelementStart(0, 'div'); + { ɵɵcontainer(1); } + ɵɵelementEnd(); + } + if (fs & RenderFlags.Update) { + ɵɵcontainerRefreshStart(1); { - if (rf1 & RenderFlags.Create) { - ɵɵelement(0, 'repeated'); + for (let i = 0; i < 3; i++) { + let rf1 = ɵɵembeddedViewStart(1, 1, 0); + { + if (rf1 & RenderFlags.Create) { + ɵɵelement(0, 'repeated'); + } + } + ɵɵembeddedViewEnd(); } } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } - } - ɵɵcontainerRefreshEnd(); - } - }, + }, features: [ɵɵProvidersFeature([], [{provide: String, useValue: 'foo'}])], directives: [Repeated] }); @@ -1044,14 +1184,15 @@ describe('providers', () => { selectors: [['embedded-cmp']], decls: 1, vars: 1, - template: (rf: RenderFlags, cmp: EmbeddedComponent) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0); - } - if (rf & RenderFlags.Update) { - ɵɵtextInterpolate1('', cmp.s, ''); - } - } + template: + (rf: RenderFlags, cmp: EmbeddedComponent) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0); + } + if (rf & RenderFlags.Update) { + ɵɵtextInterpolate1('', cmp.s, ''); + } + } }); } @@ -1067,14 +1208,16 @@ describe('providers', () => { selectors: [['host-cmp']], decls: 1, vars: 0, - template: (rf: RenderFlags, cmp: HostComponent) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0, 'foo'); - } - }, - features: [ - ɵɵProvidersFeature([{provide: String, useValue: 'From host component'}]), - ], + template: + (rf: RenderFlags, cmp: HostComponent) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0, 'foo'); + } + }, + features: + [ + ɵɵProvidersFeature([{provide: String, useValue: 'From host component'}]), + ], }); } @@ -1091,14 +1234,16 @@ describe('providers', () => { selectors: [['app-cmp']], decls: 1, vars: 0, - template: (rf: RenderFlags, cmp: AppComponent) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'host-cmp'); - } - }, - features: [ - ɵɵProvidersFeature([{provide: String, useValue: 'From app component'}]), - ], + template: + (rf: RenderFlags, cmp: AppComponent) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'host-cmp'); + } + }, + features: + [ + ɵɵProvidersFeature([{provide: String, useValue: 'From app component'}]), + ], directives: [HostComponent] }); } @@ -1107,8 +1252,8 @@ describe('providers', () => { const fixture = new ComponentFixture(AppComponent); expect(fixture.html).toEqual('<host-cmp>foo</host-cmp>'); - hostComponent !.vcref.createComponent( - hostComponent !.cfr.resolveComponentFactory(EmbeddedComponent), undefined, { + hostComponent!.vcref.createComponent( + hostComponent!.cfr.resolveComponentFactory(EmbeddedComponent), undefined, { get: (token: any, notFoundValue?: any) => { return token === String ? 'From custom root view injector' : notFoundValue; } @@ -1121,7 +1266,6 @@ describe('providers', () => { it('should not cross the root view boundary, and use the module injector if no root view injector', () => { - const fixture = new ComponentFixture(AppComponent); expect(fixture.html).toEqual('<host-cmp>foo</host-cmp>'); @@ -1129,18 +1273,19 @@ describe('providers', () => { static ɵinj = ɵɵdefineInjector({ factory: () => new MyAppModule(), imports: [], - providers: [ - {provide: RendererFactory2, useValue: getRendererFactory2(document)}, - {provide: String, useValue: 'From module injector'} - ] + providers: + [ + {provide: RendererFactory2, useValue: getRendererFactory2(document)}, + {provide: String, useValue: 'From module injector'} + ] }); - static ɵmod: NgModuleDef<any> = { bootstrap: [] } as any; + static ɵmod: NgModuleDef<any> = {bootstrap: []} as any; } const myAppModuleFactory = new NgModuleFactory(MyAppModule); const ngModuleRef = myAppModuleFactory.create(null); - hostComponent !.vcref.createComponent( - hostComponent !.cfr.resolveComponentFactory(EmbeddedComponent), undefined, + hostComponent!.vcref.createComponent( + hostComponent!.cfr.resolveComponentFactory(EmbeddedComponent), undefined, {get: (token: any, notFoundValue?: any) => notFoundValue}, undefined, ngModuleRef); fixture.update(); expect(fixture.html) @@ -1153,8 +1298,8 @@ describe('providers', () => { const fixture = new ComponentFixture(AppComponent); expect(fixture.html).toEqual('<host-cmp>foo</host-cmp>'); - hostComponent !.vcref.createComponent( - hostComponent !.cfr.resolveComponentFactory(EmbeddedComponent)); + hostComponent!.vcref.createComponent( + hostComponent!.cfr.resolveComponentFactory(EmbeddedComponent)); fixture.update(); expect(fixture.html) .toEqual('<host-cmp>foo</host-cmp><embedded-cmp>From app component</embedded-cmp>'); @@ -1201,8 +1346,9 @@ describe('providers', () => { }, viewChild: { providers: [{provide: String, useValue: 'view'}], - componentAssertion: - () => { expect(ɵɵdirectiveInject(Greeter).greeting).toEqual('parent'); }, + componentAssertion: () => { + expect(ɵɵdirectiveInject(Greeter).greeting).toEqual('parent'); + }, }, }); }); @@ -1228,7 +1374,9 @@ describe('providers', () => { }); describe('from a node without injector', () => { - abstract class Some { abstract location: String; } + abstract class Some { + abstract location: String; + } class SomeInj implements Some { constructor(public location: String) {} @@ -1253,16 +1401,18 @@ describe('providers', () => { selectors: [['my-cmp']], decls: 1, vars: 0, - template: (rf: RenderFlags, cmp: MyComponent) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'p'); - } - }, - features: [ - ɵɵProvidersFeature( - [{provide: String, useValue: 'From my component'}], - [{provide: Number, useValue: 123}]), - ], + template: + (rf: RenderFlags, cmp: MyComponent) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'p'); + } + }, + features: + [ + ɵɵProvidersFeature( + [{provide: String, useValue: 'From my component'}], + [{provide: Number, useValue: 123}]), + ], }); } @@ -1280,16 +1430,19 @@ describe('providers', () => { selectors: [['app-cmp']], decls: 1, vars: 0, - template: (rf: RenderFlags, cmp: AppComponent) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'my-cmp'); - } - }, - features: [ - ɵɵProvidersFeature([ - {provide: String, useValue: 'From app component'}, {provide: Some, useClass: SomeInj} - ]), - ], + template: + (rf: RenderFlags, cmp: AppComponent) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'my-cmp'); + } + }, + features: + [ + ɵɵProvidersFeature([ + {provide: String, useValue: 'From app component'}, + {provide: Some, useClass: SomeInj} + ]), + ], directives: [MyComponent] }); } @@ -1323,14 +1476,30 @@ describe('providers', () => { @Injectable() class InjectableWithLifeCycleHooks { - ngOnChanges() { logs.push('Injectable OnChanges'); } - ngOnInit() { logs.push('Injectable OnInit'); } - ngDoCheck() { logs.push('Injectable DoCheck'); } - ngAfterContentInit() { logs.push('Injectable AfterContentInit'); } - ngAfterContentChecked() { logs.push('Injectable AfterContentChecked'); } - ngAfterViewInit() { logs.push('Injectable AfterViewInit'); } - ngAfterViewChecked() { logs.push('Injectable gAfterViewChecked'); } - ngOnDestroy() { logs.push('Injectable OnDestroy'); } + ngOnChanges() { + logs.push('Injectable OnChanges'); + } + ngOnInit() { + logs.push('Injectable OnInit'); + } + ngDoCheck() { + logs.push('Injectable DoCheck'); + } + ngAfterContentInit() { + logs.push('Injectable AfterContentInit'); + } + ngAfterContentChecked() { + logs.push('Injectable AfterContentChecked'); + } + ngAfterViewInit() { + logs.push('Injectable AfterViewInit'); + } + ngAfterViewChecked() { + logs.push('Injectable gAfterViewChecked'); + } + ngOnDestroy() { + logs.push('Injectable OnDestroy'); + } } @Component({template: `<span></span>`, providers: [InjectableWithLifeCycleHooks]}) @@ -1338,18 +1507,21 @@ describe('providers', () => { constructor(foo: InjectableWithLifeCycleHooks) {} static ɵfac = - () => { return new MyComponent(ɵɵdirectiveInject(InjectableWithLifeCycleHooks)); } + () => { + return new MyComponent(ɵɵdirectiveInject(InjectableWithLifeCycleHooks)); + } static ɵcmp = ɵɵdefineComponent({ type: MyComponent, selectors: [['my-comp']], decls: 1, vars: 0, - template: (rf: RenderFlags, ctx: MyComponent) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'span'); - } - }, + template: + (rf: RenderFlags, ctx: MyComponent) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'span'); + } + }, features: [ɵɵProvidersFeature([InjectableWithLifeCycleHooks])] }); } @@ -1372,28 +1544,29 @@ describe('providers', () => { selectors: [['app-cmp']], decls: 2, vars: 0, - template: (rf: RenderFlags, ctx: App) => { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'div'); - { ɵɵcontainer(1); } - ɵɵelementEnd(); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(1); - { - if (ctx.condition) { - let rf1 = ɵɵembeddedViewStart(1, 2, 1); + template: + (rf: RenderFlags, ctx: App) => { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'div'); + { ɵɵcontainer(1); } + ɵɵelementEnd(); + } + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(1); { - if (rf1 & RenderFlags.Create) { - ɵɵelement(0, 'my-comp'); + if (ctx.condition) { + let rf1 = ɵɵembeddedViewStart(1, 2, 1); + { + if (rf1 & RenderFlags.Create) { + ɵɵelement(0, 'my-comp'); + } + } + ɵɵembeddedViewEnd(); } } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } - } - ɵɵcontainerRefreshEnd(); - } - }, + }, directives: [MyComponent] }); } @@ -1407,7 +1580,6 @@ describe('providers', () => { expect(fixture.html).toEqual('<div></div>'); expect(logs).toEqual(['Injectable OnDestroy']); }); - }); }); interface ComponentTest { @@ -1426,14 +1598,14 @@ function expectProvidersScenario(defs: { contentChild?: ComponentTest, ngModule?: InjectorType<any>, }): void { - function testComponentInjection<T>(def: ComponentTest | undefined, instance: T): T { + function testComponentInjection<T>(def: ComponentTest|undefined, instance: T): T { if (def) { def.componentAssertion && def.componentAssertion(); } return instance; } - function testDirectiveInjection<T>(def: ComponentTest | undefined, instance: T): T { + function testDirectiveInjection<T>(def: ComponentTest|undefined, instance: T): T { if (def) { def.directiveAssertion && def.directiveAssertion(); } @@ -1447,11 +1619,12 @@ function expectProvidersScenario(defs: { selectors: [['view-child']], decls: 1, vars: 0, - template: function(fs: RenderFlags, ctx: ViewChildComponent) { - if (fs & RenderFlags.Create) { - ɵɵtext(0, 'view-child'); - } - }, + template: + function(fs: RenderFlags, ctx: ViewChildComponent) { + if (fs & RenderFlags.Create) { + ɵɵtext(0, 'view-child'); + } + }, features: defs.viewChild && [ɵɵProvidersFeature(defs.viewChild.providers || [], defs.viewChild.viewProviders || [])] }); @@ -1468,18 +1641,21 @@ function expectProvidersScenario(defs: { class ContentChildComponent { static ɵfac = - () => { return testComponentInjection(defs.contentChild, new ContentChildComponent()); } + () => { + return testComponentInjection(defs.contentChild, new ContentChildComponent()); + } static ɵcmp = ɵɵdefineComponent({ type: ContentChildComponent, selectors: [['content-child']], decls: 1, vars: 0, - template: function(fs: RenderFlags, ctx: ParentComponent) { - if (fs & RenderFlags.Create) { - ɵɵtext(0, 'content-child'); - } - }, + template: + function(fs: RenderFlags, ctx: ParentComponent) { + if (fs & RenderFlags.Create) { + ɵɵtext(0, 'content-child'); + } + }, features: defs.contentChild && [ɵɵProvidersFeature( defs.contentChild.providers || [], defs.contentChild.viewProviders || [])], @@ -1488,13 +1664,15 @@ function expectProvidersScenario(defs: { class ContentChildDirective { static ɵfac = - () => { return testDirectiveInjection(defs.contentChild, new ContentChildDirective()); } + () => { + return testDirectiveInjection(defs.contentChild, new ContentChildDirective()); + } static ɵdir = ɵɵdefineDirective({ type: ContentChildDirective, selectors: [['content-child']], - features: - defs.contentChild && [ɵɵProvidersFeature(defs.contentChild.directiveProviders || [])], + features: defs.contentChild && + [ɵɵProvidersFeature(defs.contentChild.directiveProviders || [])], }); } @@ -1506,11 +1684,12 @@ function expectProvidersScenario(defs: { selectors: [['parent']], decls: 1, vars: 0, - template: function(fs: RenderFlags, ctx: ParentComponent) { - if (fs & RenderFlags.Create) { - ɵɵelement(0, 'view-child'); - } - }, + template: + function(fs: RenderFlags, ctx: ParentComponent) { + if (fs & RenderFlags.Create) { + ɵɵelement(0, 'view-child'); + } + }, features: defs.parent && [ɵɵProvidersFeature(defs.parent.providers || [], defs.parent.viewProviders || [])], directives: [ViewChildComponent, ViewChildDirective] @@ -1543,19 +1722,21 @@ function expectProvidersScenario(defs: { selectors: [['app']], decls: 2, vars: 0, - template: function(fs: RenderFlags, ctx: App) { - if (fs & RenderFlags.Create) { - ɵɵelementStart(0, 'parent'); - ɵɵelement(1, 'content-child'); - ɵɵelementEnd(); - } - }, - features: - defs.app && [ɵɵProvidersFeature(defs.app.providers || [], defs.app.viewProviders || [])], - directives: [ - ParentComponent, ParentDirective2, ParentDirective, ContentChildComponent, - ContentChildDirective - ] + template: + function(fs: RenderFlags, ctx: App) { + if (fs & RenderFlags.Create) { + ɵɵelementStart(0, 'parent'); + ɵɵelement(1, 'content-child'); + ɵɵelementEnd(); + } + }, + features: defs.app && + [ɵɵProvidersFeature(defs.app.providers || [], defs.app.viewProviders || [])], + directives: + [ + ParentComponent, ParentDirective2, ParentDirective, ContentChildComponent, + ContentChildDirective + ] }); } diff --git a/packages/core/test/render3/pure_function_spec.ts b/packages/core/test/render3/pure_function_spec.ts index be5837e3a4a7a..940b8f2a2db6a 100644 --- a/packages/core/test/render3/pure_function_spec.ts +++ b/packages/core/test/render3/pure_function_spec.ts @@ -17,9 +17,11 @@ describe('object literals', () => { class ObjectComp { // TODO(issue/24571): remove '!'. - config !: {[key: string]: any}; + config!: {[key: string]: any}; - static ɵfac = function ObjectComp_Factory() { return objectComp = new ObjectComp(); }; + static ɵfac = function ObjectComp_Factory() { + return objectComp = new ObjectComp(); + }; static ɵcmp = ɵɵdefineComponent({ type: ObjectComp, selectors: [['object-comp']], @@ -70,7 +72,9 @@ describe('object literals', () => { } } - const e0_ff = (v1: any, v2: any) => { return {opacity: v1, duration: v2}; }; + const e0_ff = (v1: any, v2: any) => { + return {opacity: v1, duration: v2}; + }; const configs = [{opacity: 0, duration: 500}, {opacity: 1, duration: 600}]; renderToHtml(Template, {configs}, 1, 0, defs); @@ -82,5 +86,4 @@ describe('object literals', () => { expect(objectComps[0].config).toEqual({opacity: 0, duration: 1000}); expect(objectComps[1].config).toEqual({opacity: 1, duration: 600}); }); - }); diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index bf4f79d5a7d7f..9a688ecec0e56 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -9,7 +9,7 @@ import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core'; import {EventEmitter} from '../..'; -import {AttributeMarker, ɵɵProvidersFeature, ɵɵdefineComponent, ɵɵdefineDirective} from '../../src/render3/index'; +import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵProvidersFeature} from '../../src/render3/index'; import {ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdirectiveInject, ɵɵelement, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵtemplate, ɵɵtext} from '../../src/render3/instructions/all'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {ɵɵcontentQuery, ɵɵloadQuery, ɵɵqueryRefresh, ɵɵviewQuery} from '../../src/render3/query'; @@ -17,7 +17,7 @@ import {getLView} from '../../src/render3/state'; import {getNativeByIndex, load} from '../../src/render3/util/view_utils'; import {ɵɵtemplateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound'; -import {ComponentFixture, TemplateFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent} from './render_util'; +import {ComponentFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent, TemplateFixture} from './render_util'; @@ -99,7 +99,6 @@ describe('query', () => { describe('predicate', () => { describe('types', () => { - it('should query using type predicate and read a specified token', () => { const Child = createDirective('child'); let elToQuery; @@ -211,7 +210,6 @@ describe('query', () => { }); describe('providers', () => { - class Service {} class Alias {} @@ -234,7 +232,6 @@ describe('query', () => { // https://stackblitz.com/edit/ng-viewengine-viewchild-providers?file=src%2Fapp%2Fapp.component.ts it('should query for providers that are present on a directive', () => { - /** * <div myDir></div> * class App { @@ -248,43 +245,48 @@ describe('query', () => { service?: Service; alias?: Alias; - static ɵfac = function App_Factory() { return new App(); }; + static ɵfac = function App_Factory() { + return new App(); + }; static ɵcmp = ɵɵdefineComponent({ type: App, selectors: [['app']], decls: 1, vars: 0, consts: [['myDir']], - template: function App_Template(rf: RenderFlags, ctx: App) { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div', 0); - } - }, - viewQuery: function(rf: RenderFlags, ctx: App) { - if (rf & RenderFlags.Create) { - ɵɵviewQuery(MyDirective, false); - ɵɵviewQuery(Service, false); - ɵɵviewQuery(Alias, false); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && (ctx.directive = tmp.first); - ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && (ctx.service = tmp.first); - ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && (ctx.alias = tmp.first); - } - }, + template: + function App_Template(rf: RenderFlags, ctx: App) { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div', 0); + } + }, + viewQuery: + function(rf: RenderFlags, ctx: App) { + if (rf & RenderFlags.Create) { + ɵɵviewQuery(MyDirective, false); + ɵɵviewQuery(Service, false); + ɵɵviewQuery(Alias, false); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && + (ctx.directive = tmp.first); + ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && + (ctx.service = tmp.first); + ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && (ctx.alias = tmp.first); + } + }, directives: [MyDirective] }); } const componentFixture = new ComponentFixture(App); - expect(componentFixture.component.directive).toBe(directive !); - expect(componentFixture.component.service).toBe(directive !.service); - expect(componentFixture.component.alias).toBe(directive !.service); + expect(componentFixture.component.directive).toBe(directive!); + expect(componentFixture.component.service).toBe(directive!.service); + expect(componentFixture.component.alias).toBe(directive!.service); }); it('should resolve a provider if given as read token', () => { - /** * <div myDir></div> * class App { @@ -294,41 +296,43 @@ describe('query', () => { class App { service?: Service; - static ɵfac = function App_Factory() { return new App(); }; + static ɵfac = function App_Factory() { + return new App(); + }; static ɵcmp = ɵɵdefineComponent({ type: App, selectors: [['app']], decls: 1, vars: 0, consts: [['myDir']], - template: function App_Template(rf: RenderFlags, ctx: App) { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div', 0); - } - }, - viewQuery: function(rf: RenderFlags, ctx: App) { - let tmp: any; - if (rf & RenderFlags.Create) { - ɵɵviewQuery(MyDirective, false, Alias); - } - if (rf & RenderFlags.Update) { - ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && (ctx.service = tmp.first); - } - }, + template: + function App_Template(rf: RenderFlags, ctx: App) { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div', 0); + } + }, + viewQuery: + function(rf: RenderFlags, ctx: App) { + let tmp: any; + if (rf & RenderFlags.Create) { + ɵɵviewQuery(MyDirective, false, Alias); + } + if (rf & RenderFlags.Update) { + ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && + (ctx.service = tmp.first); + } + }, directives: [MyDirective] }); } const componentFixture = new ComponentFixture(App); - expect(componentFixture.component.service).toBe(directive !.service); + expect(componentFixture.component.service).toBe(directive!.service); }); - }); describe('local names', () => { - it('should query for a single element and read ElementRef by default', () => { - let elToQuery; /** * <div #foo></div> @@ -413,7 +417,6 @@ describe('query', () => { }); it('should query for multiple elements and read ElementRef by default', () => { - let el1ToQuery; let el2ToQuery; /** @@ -456,7 +459,6 @@ describe('query', () => { }); it('should read ElementRef from an element when explicitly asked for', () => { - let elToQuery; /** * <div #foo></div> @@ -652,7 +654,6 @@ describe('query', () => { }, 2, 0, [], [], function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { ɵɵviewQuery(['foo'], false, ElementRef); } @@ -822,7 +823,7 @@ describe('query', () => { const cmptInstance = renderComponent(Cmpt); const qList = (cmptInstance.query as QueryList<any>); expect(qList.length).toBe(1); - expect(qList.first).toBe(childInstance !); + expect(qList.first).toBe(childInstance!); }); it('should read directive instance if element queried for has an exported directive with a matching name', @@ -1294,7 +1295,6 @@ describe('query', () => { expect(elemQList.length).toBe(3); expect(isElementRef(elemQList.first)).toBeTruthy(); }); - }); }); @@ -1322,7 +1322,9 @@ describe('query', () => { }, [], [], undefined, [['foo', '']]); - function createTemplate() { ɵɵcontainer(0); } + function createTemplate() { + ɵɵcontainer(0); + } function updateTemplate() { ɵɵcontainerRefreshStart(0); @@ -1349,12 +1351,12 @@ describe('query', () => { const t = new TemplateFixture(createTemplate, updateTemplate, 1, 0, [SimpleComponentWithQuery]); expect(t.html).toEqual('<some-component-with-query><div></div></some-component-with-query>'); - expect((queryInstance !.changes as EventEmitter<any>).closed).toBeFalsy(); + expect((queryInstance!.changes as EventEmitter<any>).closed).toBeFalsy(); condition = false; t.update(); expect(t.html).toEqual(''); - expect((queryInstance !.changes as EventEmitter<any>).closed).toBeTruthy(); + expect((queryInstance!.changes as EventEmitter<any>).closed).toBeTruthy(); }); }); @@ -1411,15 +1413,19 @@ describe('query', () => { describe('content', () => { let withContentInstance: WithContentDirective|null; - beforeEach(() => { withContentInstance = null; }); + beforeEach(() => { + withContentInstance = null; + }); class WithContentDirective { // @ContentChildren('foo') - foos !: QueryList<ElementRef>; + foos!: QueryList<ElementRef>; contentInitQuerySnapshot = 0; contentCheckedQuerySnapshot = 0; - ngAfterContentInit() { this.contentInitQuerySnapshot = this.foos ? this.foos.length : 0; } + ngAfterContentInit() { + this.contentInitQuerySnapshot = this.foos ? this.foos.length : 0; + } ngAfterContentChecked() { this.contentCheckedQuerySnapshot = this.foos ? this.foos.length : 0; @@ -1429,15 +1435,16 @@ describe('query', () => { static ɵdir = ɵɵdefineDirective({ type: WithContentDirective, selectors: [['', 'with-content', '']], - contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => { - if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], true); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.foos = tmp); - } - } + contentQueries: + (rf: RenderFlags, ctx: any, dirIndex: number) => { + if (rf & RenderFlags.Create) { + ɵɵcontentQuery(dirIndex, ['foo'], true); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.foos = tmp); + } + } }); } @@ -1460,15 +1467,15 @@ describe('query', () => { [[AttributeMarker.Bindings, 'with-content'], ['foo', '']]); const fixture = new ComponentFixture(AppComponent); - expect(withContentInstance !.foos.length) + expect(withContentInstance!.foos.length) .toBe(1, `Expected content query to match <span #foo>.`); - expect(withContentInstance !.contentInitQuerySnapshot) + expect(withContentInstance!.contentInitQuerySnapshot) .toBe( 1, `Expected content query results to be available when ngAfterContentInit was called.`); - expect(withContentInstance !.contentCheckedQuerySnapshot) + expect(withContentInstance!.contentCheckedQuerySnapshot) .toBe( 1, `Expected content query results to be available when ngAfterContentChecked was called.`); @@ -1509,15 +1516,15 @@ describe('query', () => { [[AttributeMarker.Bindings, 'with-content'], ['foo', '']]); const fixture = new ComponentFixture(AppComponent); - expect(withContentInstance !.foos.length) + expect(withContentInstance!.foos.length) .toBe(1, `Expected content query to match <span #foo>.`); - expect(withContentInstance !.contentInitQuerySnapshot) + expect(withContentInstance!.contentInitQuerySnapshot) .toBe( 1, `Expected content query results to be available when ngAfterContentInit was called.`); - expect(withContentInstance !.contentCheckedQuerySnapshot) + expect(withContentInstance!.contentCheckedQuerySnapshot) .toBe( 1, `Expected content query results to be available when ngAfterContentChecked was called.`); @@ -1539,7 +1546,7 @@ describe('query', () => { [['with-content', ''], ['foo', '']]); const fixture = new ComponentFixture(AppComponent); - expect(withContentInstance !.foos.length) + expect(withContentInstance!.foos.length) .toBe(0, `Expected content query not to match <div with-content #foo>.`); }); @@ -1581,8 +1588,8 @@ describe('query', () => { const viewQList = fixture.component.foos; expect(viewQList.length).toBe(2); - expect(withContentInstance !.foos.length).toBe(1); - expect(viewQList.first.nativeElement).toBe(withContentInstance !.foos.first.nativeElement); + expect(withContentInstance!.foos.length).toBe(1); + expect(viewQList.first.nativeElement).toBe(withContentInstance!.foos.first.nativeElement); expect(viewQList.last.nativeElement.id).toBe('after'); }); @@ -1620,8 +1627,8 @@ describe('query', () => { [], [], undefined, [['with-content', ''], ['id', 'yes'], ['foo', '']]); const fixture = new ComponentFixture(AppComponent); - expect(withContentInstance !.foos.length).toBe(1); - expect(withContentInstance !.foos.first.nativeElement.id).toEqual('yes'); + expect(withContentInstance!.foos.length).toBe(1); + expect(withContentInstance!.foos.first.nativeElement.id).toEqual('yes'); }); it('should report results to appropriate queries where deep content queries are nested', () => { @@ -1632,17 +1639,18 @@ describe('query', () => { type: QueryDirective, selectors: [['', 'query', '']], exportAs: ['query'], - contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => { - // @ContentChildren('foo, bar, baz', {descendants: true}) - // fooBars: QueryList<ElementRef>; - if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo', 'bar', 'baz'], true); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.fooBars = tmp); - } - } + contentQueries: + (rf: RenderFlags, ctx: any, dirIndex: number) => { + // @ContentChildren('foo, bar, baz', {descendants: true}) + // fooBars: QueryList<ElementRef>; + if (rf & RenderFlags.Create) { + ɵɵcontentQuery(dirIndex, ['foo', 'bar', 'baz'], true); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.fooBars = tmp); + } + } }); } @@ -1684,8 +1692,8 @@ describe('query', () => { ]); const fixture = new ComponentFixture(AppComponent); - expect(outInstance !.fooBars.length).toBe(3); - expect(inInstance !.fooBars.length).toBe(1); + expect(outInstance!.fooBars.length).toBe(3); + expect(inInstance!.fooBars.length).toBe(1); }); @@ -1700,17 +1708,18 @@ describe('query', () => { type: QueryDirective, selectors: [['', 'query', '']], exportAs: ['query'], - contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => { - // @ContentChildren('foo', {descendants: true}) - // fooBars: QueryList<ElementRef>; - if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], false); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.fooBars = tmp); - } - } + contentQueries: + (rf: RenderFlags, ctx: any, dirIndex: number) => { + // @ContentChildren('foo', {descendants: true}) + // fooBars: QueryList<ElementRef>; + if (rf & RenderFlags.Create) { + ɵɵcontentQuery(dirIndex, ['foo'], false); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.fooBars = tmp); + } + } }); } @@ -1740,13 +1749,12 @@ describe('query', () => { } }, 7, 0, [QueryDirective], [], null, [], [], undefined, [ - ['query', ''], ['id', 'bar'], ['out', 'query'], ['in', 'query', 'foo', ''], - ['foo', ''] + ['query', ''], ['id', 'bar'], ['out', 'query'], ['in', 'query', 'foo', ''], ['foo', ''] ]); const fixture = new ComponentFixture(AppComponent); - expect(outInstance !.fooBars.length).toBe(1); - expect(inInstance !.fooBars.length).toBe(1); + expect(outInstance!.fooBars.length).toBe(1); + expect(inInstance!.fooBars.length).toBe(1); }); it('should support nested shallow content queries across multiple component instances', () => { @@ -1760,17 +1768,18 @@ describe('query', () => { type: QueryDirective, selectors: [['', 'query', '']], exportAs: ['query'], - contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => { - // @ContentChildren('foo', {descendants: true}) - // fooBars: QueryList<ElementRef>; - if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], false); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.fooBars = tmp); - } - } + contentQueries: + (rf: RenderFlags, ctx: any, dirIndex: number) => { + // @ContentChildren('foo', {descendants: true}) + // fooBars: QueryList<ElementRef>; + if (rf & RenderFlags.Create) { + ɵɵcontentQuery(dirIndex, ['foo'], false); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.fooBars = tmp); + } + } }); } @@ -1800,19 +1809,18 @@ describe('query', () => { } }, 7, 0, [QueryDirective], [], null, [], [], undefined, [ - ['query', ''], ['id', 'bar'], ['out', 'query'], ['in', 'query', 'foo', ''], - ['foo', ''] + ['query', ''], ['id', 'bar'], ['out', 'query'], ['in', 'query', 'foo', ''], ['foo', ''] ]); const fixture1 = new ComponentFixture(AppComponent); - expect(outInstance !.fooBars.length).toBe(1); - expect(inInstance !.fooBars.length).toBe(1); + expect(outInstance!.fooBars.length).toBe(1); + expect(inInstance!.fooBars.length).toBe(1); - outInstance = inInstance = null !; + outInstance = inInstance = null!; const fixture2 = new ComponentFixture(AppComponent); - expect(outInstance !.fooBars.length).toBe(1); - expect(inInstance !.fooBars.length).toBe(1); + expect(outInstance!.fooBars.length).toBe(1); + expect(inInstance!.fooBars.length).toBe(1); }); it('should respect shallow flag on content queries when mixing deep and shallow queries', @@ -1824,17 +1832,18 @@ describe('query', () => { type: ShallowQueryDirective, selectors: [['', 'shallow-query', '']], exportAs: ['shallow-query'], - contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => { - // @ContentChildren('foo', {descendants: false}) - // foos: QueryList<ElementRef>; - if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], false); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.foos = tmp); - } - } + contentQueries: + (rf: RenderFlags, ctx: any, dirIndex: number) => { + // @ContentChildren('foo', {descendants: false}) + // foos: QueryList<ElementRef>; + if (rf & RenderFlags.Create) { + ɵɵcontentQuery(dirIndex, ['foo'], false); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.foos = tmp); + } + } }); } @@ -1845,17 +1854,18 @@ describe('query', () => { type: DeepQueryDirective, selectors: [['', 'deep-query', '']], exportAs: ['deep-query'], - contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => { - // @ContentChildren('foo', {descendants: true}) - // foos: QueryList<ElementRef>; - if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, ['foo'], true); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.foos = tmp); - } - } + contentQueries: + (rf: RenderFlags, ctx: any, dirIndex: number) => { + // @ContentChildren('foo', {descendants: true}) + // foos: QueryList<ElementRef>; + if (rf & RenderFlags.Create) { + ɵɵcontentQuery(dirIndex, ['foo'], true); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<ElementRef>()) && (ctx.foos = tmp); + } + } }); } @@ -1866,7 +1876,7 @@ describe('query', () => { 'app-component', /** * <div shallow-query #shallow="shallow-query" deep-query #deep="deep-query"> - * <span #foo></span> + * <span #foo></span> * <div> * <span #foo></span> * </div> @@ -1895,14 +1905,14 @@ describe('query', () => { ]); const fixture = new ComponentFixture(AppComponent); - expect(shallowInstance !.foos.length).toBe(1); - expect(deepInstance !.foos.length).toBe(2); + expect(shallowInstance!.foos.length).toBe(1); + expect(deepInstance!.foos.length).toBe(2); }); }); describe('order', () => { class TextDirective { - value !: string; + value!: string; static ɵfac = () => new TextDirective(); static ɵdir = ɵɵdefineDirective( @@ -1914,39 +1924,40 @@ describe('query', () => { class ContentQueryDirective { // @ContentChildren(TextDirective) - texts !: QueryList<TextDirective>; + texts!: QueryList<TextDirective>; static ɵfac = () => contentQueryDirective = new ContentQueryDirective(); static ɵcmp = ɵɵdefineDirective({ type: ContentQueryDirective, selectors: [['', 'content-query', '']], - contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => { - // @ContentChildren(TextDirective, {descendants: true}) - // texts: QueryList<TextDirective>; - if (rf & RenderFlags.Create) { - ɵɵcontentQuery(dirIndex, TextDirective, true); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<TextDirective>()) && (ctx.texts = tmp); - } - } + contentQueries: + (rf: RenderFlags, ctx: any, dirIndex: number) => { + // @ContentChildren(TextDirective, {descendants: true}) + // texts: QueryList<TextDirective>; + if (rf & RenderFlags.Create) { + ɵɵcontentQuery(dirIndex, TextDirective, true); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<TextDirective>()) && (ctx.texts = tmp); + } + } }); } const AppComponent = createComponent( 'app-component', /** - * <div content-query> - * <span text="A"></span> - * <div text="B"> - * <span text="C"> - * <span text="D"></span> - * </span> - * </div> - * <span text="E"></span> - * </div> - */ + * <div content-query> + * <span text="A"></span> + * <div text="B"> + * <span text="C"> + * <span text="D"></span> + * </span> + * </div> + * <span text="E"></span> + * </div> + */ function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { ɵɵelementStart(0, 'div', 0); @@ -1968,53 +1979,55 @@ describe('query', () => { ]); new ComponentFixture(AppComponent); - expect(contentQueryDirective !.texts.map(item => item.value)).toEqual([ + expect(contentQueryDirective!.texts.map(item => item.value)).toEqual([ 'A', 'B', 'C', 'D', 'E' ]); }); it('should register view matches from top to bottom', () => { /** - * <span text="A"></span> - * <div text="B"> - * <span text="C"> - * <span text="D"></span> - * </span> - * </div> - * <span text="E"></span> - */ + * <span text="A"></span> + * <div text="B"> + * <span text="C"> + * <span text="D"></span> + * </span> + * </div> + * <span text="E"></span> + */ class ViewQueryComponent { // @ViewChildren(TextDirective) - texts !: QueryList<TextDirective>; + texts!: QueryList<TextDirective>; static ɵfac = () => new ViewQueryComponent(); static ɵcmp = ɵɵdefineComponent({ type: ViewQueryComponent, selectors: [['view-query']], consts: [['text', 'A'], ['text', 'B'], ['text', 'C'], ['text', 'D'], ['text', 'E']], - template: function(rf: RenderFlags, ctx: ViewQueryComponent) { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'span', 0); - ɵɵelementStart(1, 'div', 1); - ɵɵelementStart(2, 'span', 2); - { ɵɵelement(3, 'span', 3); } - ɵɵelementEnd(); - ɵɵelementEnd(); - ɵɵelement(4, 'span', 4); - } - }, + template: + function(rf: RenderFlags, ctx: ViewQueryComponent) { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'span', 0); + ɵɵelementStart(1, 'div', 1); + ɵɵelementStart(2, 'span', 2); + { ɵɵelement(3, 'span', 3); } + ɵɵelementEnd(); + ɵɵelementEnd(); + ɵɵelement(4, 'span', 4); + } + }, decls: 5, vars: 0, - viewQuery: function(rf: RenderFlags, ctx: ViewQueryComponent) { - let tmp: any; - if (rf & RenderFlags.Create) { - ɵɵviewQuery(TextDirective, true); - } - if (rf & RenderFlags.Update) { - ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<TextDirective>>()) && - (ctx.texts = tmp as QueryList<TextDirective>); - } - }, + viewQuery: + function(rf: RenderFlags, ctx: ViewQueryComponent) { + let tmp: any; + if (rf & RenderFlags.Create) { + ɵɵviewQuery(TextDirective, true); + } + if (rf & RenderFlags.Update) { + ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<TextDirective>>()) && + (ctx.texts = tmp as QueryList<TextDirective>); + } + }, directives: [TextDirective] }); } diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index d4c84c4a0acfa..0dfd00ef6b16d 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -28,11 +28,11 @@ import {CreateComponentOptions} from '../../src/render3/component'; import {getDirectivesAtNodeIndex, getLContext, isComponentInstance} from '../../src/render3/context_discovery'; import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition'; import {NG_ELEMENT_ID} from '../../src/render3/fields'; -import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, RenderFlags, renderComponent as _renderComponent, tick, ɵɵProvidersFeature, ɵɵdefineComponent, ɵɵdefineDirective} from '../../src/render3/index'; +import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, renderComponent as _renderComponent, RenderFlags, tick, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵProvidersFeature} from '../../src/render3/index'; import {DirectiveDefList, DirectiveDefListOrFactory, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeDefList, PipeDefListOrFactory, PipeTypesOrFactory} from '../../src/render3/interfaces/definition'; import {PlayerHandler} from '../../src/render3/interfaces/player'; -import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; -import {HEADER_OFFSET, LView, LViewFlags, TVIEW, TViewType, T_HOST} from '../../src/render3/interfaces/view'; +import {domRendererFactory3, ProceduralRenderer3, RComment, RElement, Renderer3, RendererFactory3, RendererStyleFlags3, RNode, RText} from '../../src/render3/interfaces/renderer'; +import {HEADER_OFFSET, LView, LViewFlags, T_HOST, TVIEW, TViewType} from '../../src/render3/interfaces/view'; import {destroyLView} from '../../src/render3/node_manipulation'; import {getRootView} from '../../src/render3/util/view_traversal_utils'; import {Sanitizer} from '../../src/sanitization/sanitizer'; @@ -69,13 +69,17 @@ export abstract class BaseFixture { /** * Current state of HTML rendered by the bootstrapped component. */ - get html(): string { return toHtml(this.hostElement as any as Element); } + get html(): string { + return toHtml(this.hostElement as any as Element); + } /** * Current state of HTML rendered by the fixture (will include HTML rendered by the bootstrapped * component as well as any elements outside of the component's host). */ - get outerHtml(): string { return toHtml(this.containerElement as any as Element); } + get outerHtml(): string { + return toHtml(this.containerElement as any as Element); + } } function noop() {} @@ -121,7 +125,7 @@ export class TemplateFixture extends BaseFixture { this.updateBlock(); } }, - decls, vars, null !, this._rendererFactory, null, this._directiveDefs, this._pipeDefs, + decls, vars, null!, this._rendererFactory, null, this._directiveDefs, this._pipeDefs, sanitizer, this._consts); } @@ -132,7 +136,7 @@ export class TemplateFixture extends BaseFixture { */ update(updateBlock?: () => void): void { renderTemplate( - this.hostElement, updateBlock || this.updateBlock, 0, this.vars, null !, + this.hostElement, updateBlock || this.updateBlock, 0, this.vars, null!, this._rendererFactory, this.hostView, this._directiveDefs, this._pipeDefs, this._sanitizer, this._consts); } @@ -164,7 +168,7 @@ export class ComponentFixture<T> extends BaseFixture { this.requestAnimationFrame.queue = []; this.requestAnimationFrame.flush = function() { while (requestAnimationFrame.queue.length) { - requestAnimationFrame.queue.shift() !(); + requestAnimationFrame.queue.shift()!(); } }; @@ -201,7 +205,7 @@ export class ComponentFixture<T> extends BaseFixture { /////////////////////////////////////////////////////////////////////////////////// export const document = ((typeof global == 'object' && global || window) as any).document; -export let containerEl: HTMLElement = null !; +export let containerEl: HTMLElement = null!; let hostView: LView|null; const isRenderer2 = typeof process == 'object' && process.argv[3] && process.argv[3] === '--r=renderer2'; @@ -216,7 +220,7 @@ export const requestAnimationFrame: } as any; requestAnimationFrame.flush = function() { while (requestAnimationFrame.queue.length) { - requestAnimationFrame.queue.shift() !(); + requestAnimationFrame.queue.shift()!(); } }; @@ -250,9 +254,9 @@ export function resetDOM() { */ export function renderTemplate<T>( hostNode: RElement, templateFn: ComponentTemplate<T>, decls: number, vars: number, context: T, - providedRendererFactory: RendererFactory3, componentView: LView | null, - directives?: DirectiveDefListOrFactory | null, pipes?: PipeDefListOrFactory | null, - sanitizer?: Sanitizer | null, consts?: TConstants): LView { + providedRendererFactory: RendererFactory3, componentView: LView|null, + directives?: DirectiveDefListOrFactory|null, pipes?: PipeDefListOrFactory|null, + sanitizer?: Sanitizer|null, consts?: TConstants): LView { if (componentView === null) { const renderer = providedRendererFactory.createRenderer(null, null); @@ -290,8 +294,8 @@ export function renderTemplate<T>( */ export function renderToHtml( template: ComponentTemplate<any>, ctx: any, decls: number = 0, vars: number = 0, - directives?: DirectiveTypesOrFactory | null, pipes?: PipeTypesOrFactory | null, - providedRendererFactory?: RendererFactory3 | null, keepNgReflect = false, consts?: TConstants) { + directives?: DirectiveTypesOrFactory|null, pipes?: PipeTypesOrFactory|null, + providedRendererFactory?: RendererFactory3|null, keepNgReflect = false, consts?: TConstants) { hostView = renderTemplate( containerEl, template, decls, vars, ctx, providedRendererFactory || testRendererFactory, hostView, toDefs(directives, extractDirectiveDef), toDefs(pipes, extractPipeDef), null, @@ -300,13 +304,12 @@ export function renderToHtml( } function toDefs( - types: DirectiveTypesOrFactory | undefined | null, + types: DirectiveTypesOrFactory|undefined|null, mapFn: (type: Type<any>) => DirectiveDef<any>): DirectiveDefList|null; +function toDefs(types: PipeTypesOrFactory|undefined|null, mapFn: (type: Type<any>) => PipeDef<any>): + PipeDefList|null; function toDefs( - types: PipeTypesOrFactory | undefined | null, - mapFn: (type: Type<any>) => PipeDef<any>): PipeDefList|null; -function toDefs( - types: Type<any>[] | (() => Type<any>[]) | undefined | null, + types: Type<any>[]|(() => Type<any>[])|undefined|null, mapFn: (type: Type<any>) => PipeDef<any>| DirectiveDef<any>): any { if (!types) return null; if (typeof types == 'function') { @@ -337,7 +340,7 @@ export function renderComponent<T>(type: ComponentType<T>, opts?: CreateComponen /** * @deprecated use `TemplateFixture` or `ComponentFixture` */ -export function toHtml<T>(componentOrElement: T | RElement, keepNgReflect = false): string { +export function toHtml<T>(componentOrElement: T|RElement, keepNgReflect = false): string { let element: any; if (isComponentInstance(componentOrElement)) { const context = getLContext(componentOrElement); @@ -369,7 +372,7 @@ export function toHtml<T>(componentOrElement: T | RElement, keepNgReflect = fals export function createComponent( name: string, template: ComponentTemplate<any>, decls: number = 0, vars: number = 0, directives: DirectiveTypesOrFactory = [], pipes: PipeTypesOrFactory = [], - viewQuery: ComponentTemplate<any>| null = null, providers: Provider[] = [], + viewQuery: ComponentTemplate<any>|null = null, providers: Provider[] = [], viewProviders: Provider[] = [], hostBindings?: HostBindingsFunction<any>, consts: TConstants = []): ComponentType<any> { return class Component { @@ -382,7 +385,8 @@ export function createComponent( vars: vars, template: template, viewQuery: viewQuery, - directives: directives, hostBindings, + directives: directives, + hostBindings, pipes: pipes, features: (providers.length > 0 || viewProviders.length > 0)? [ɵɵProvidersFeature(providers || [], viewProviders || [])]: [], @@ -437,7 +441,9 @@ export class MockRendererFactory implements RendererFactory3 { lastRenderer: any; private _spyOnMethods: string[]; - constructor(spyOnMethods?: string[]) { this._spyOnMethods = spyOnMethods || []; } + constructor(spyOnMethods?: string[]) { + this._spyOnMethods = spyOnMethods || []; + } createRenderer(hostElement: RElement|null, rendererType: RendererType2|null): Renderer3 { const renderer = this.lastRenderer = new MockRenderer(this._spyOnMethods); @@ -455,22 +461,34 @@ class MockRenderer implements ProceduralRenderer3 { } destroy(): void {} - createComment(value: string): RComment { return document.createComment(value); } + createComment(value: string): RComment { + return document.createComment(value); + } createElement(name: string, namespace?: string|null): RElement { return namespace ? document.createElementNS(namespace, name) : document.createElement(name); } - createText(value: string): RText { return document.createTextNode(value); } - appendChild(parent: RElement, newChild: RNode): void { parent.appendChild(newChild); } + createText(value: string): RText { + return document.createTextNode(value); + } + appendChild(parent: RElement, newChild: RNode): void { + parent.appendChild(newChild); + } insertBefore(parent: RNode, newChild: RNode, refChild: RNode|null): void { parent.insertBefore(newChild, refChild, false); } - removeChild(parent: RElement, oldChild: RNode): void { parent.removeChild(oldChild); } + removeChild(parent: RElement, oldChild: RNode): void { + parent.removeChild(oldChild); + } selectRootElement(selectorOrNode: string|any): RElement { return typeof selectorOrNode === 'string' ? document.querySelector(selectorOrNode) : selectorOrNode; } - parentNode(node: RNode): RElement|null { return node.parentNode as RElement; } - nextSibling(node: RNode): RNode|null { return node.nextSibling; } + parentNode(node: RNode): RElement|null { + return node.parentNode as RElement; + } + nextSibling(node: RNode): RNode|null { + return node.nextSibling; + } setAttribute(el: RElement, name: string, value: string, namespace?: string|null): void { // set all synthetic attributes as properties if (name[0] === '@') { @@ -486,8 +504,12 @@ class MockRenderer implements ProceduralRenderer3 { el: RElement, style: string, value: any, flags?: RendererStyleFlags2|RendererStyleFlags3): void {} removeStyle(el: RElement, style: string, flags?: RendererStyleFlags2|RendererStyleFlags3): void {} - setProperty(el: RElement, name: string, value: any): void { (el as any)[name] = value; } - setValue(node: RText, value: string): void { node.textContent = value; } + setProperty(el: RElement, name: string, value: any): void { + (el as any)[name] = value; + } + setValue(node: RText, value: string): void { + node.textContent = value; + } // TODO(misko): Deprecate in favor of addEventListener/removeEventListener listen(target: RNode, eventName: string, callback: (event: any) => boolean | void): () => void { diff --git a/packages/core/test/render3/renderer_factory_spec.ts b/packages/core/test/render3/renderer_factory_spec.ts index 7b3990a7afe1d..829fa1c15ffad 100644 --- a/packages/core/test/render3/renderer_factory_spec.ts +++ b/packages/core/test/render3/renderer_factory_spec.ts @@ -12,13 +12,13 @@ import {ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵe import {RenderFlags} from '../../src/render3/interfaces/definition'; import {getRendererFactory2} from './imported_renderer2'; -import {TemplateFixture, document, renderToHtml} from './render_util'; +import {document, renderToHtml, TemplateFixture} from './render_util'; describe('renderer factory lifecycle', () => { let logs: string[] = []; let rendererFactory = getRendererFactory2(document); const createRender = rendererFactory.createRenderer; - rendererFactory.createRenderer = (hostElement: any, type: RendererType2 | null) => { + rendererFactory.createRenderer = (hostElement: any, type: RendererType2|null) => { logs.push('create'); return createRender.apply(rendererFactory, [hostElement, type]); }; @@ -33,15 +33,16 @@ describe('renderer factory lifecycle', () => { selectors: [['some-component']], decls: 1, vars: 0, - template: function(rf: RenderFlags, ctx: SomeComponent) { - if (rf & RenderFlags.Create) { - logs.push('component create'); - ɵɵtext(0, 'foo'); - } - if (rf & RenderFlags.Update) { - logs.push('component update'); - } - } + template: + function(rf: RenderFlags, ctx: SomeComponent) { + if (rf & RenderFlags.Create) { + logs.push('component create'); + ɵɵtext(0, 'foo'); + } + if (rf & RenderFlags.Update) { + logs.push('component update'); + } + } }); } @@ -53,9 +54,10 @@ describe('renderer factory lifecycle', () => { selectors: [['some-component-with-Error']], decls: 0, vars: 0, - template: function(rf: RenderFlags, ctx: SomeComponentWhichThrows) { - throw(new Error('SomeComponentWhichThrows threw')); - } + template: + function(rf: RenderFlags, ctx: SomeComponentWhichThrows) { + throw (new Error('SomeComponentWhichThrows threw')); + } }); } @@ -82,7 +84,9 @@ describe('renderer factory lifecycle', () => { } } - beforeEach(() => { logs = []; }); + beforeEach(() => { + logs = []; + }); it('should work with a template', () => { renderToHtml(Template, {}, 1, 0, null, null, rendererFactory); @@ -104,7 +108,6 @@ describe('renderer factory lifecycle', () => { renderToHtml(TemplateWithComponent, {}, 2, 0, directives); expect(logs).toEqual(['begin', 'function_with_component update', 'component update', 'end']); }); - }); describe('Renderer2 destruction hooks', () => { @@ -157,11 +160,12 @@ describe('Renderer2 destruction hooks', () => { selectors: [['simple']], decls: 1, vars: 0, - template: function(rf: RenderFlags, ctx: SimpleComponent) { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'span'); - } - }, + template: + function(rf: RenderFlags, ctx: SimpleComponent) { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'span'); + } + }, }); } diff --git a/packages/core/test/render3/styling_next/static_styling_spec.ts b/packages/core/test/render3/styling_next/static_styling_spec.ts index d9e5d8f2fae9c..5df0dc2bae1db 100644 --- a/packages/core/test/render3/styling_next/static_styling_spec.ts +++ b/packages/core/test/render3/styling_next/static_styling_spec.ts @@ -14,10 +14,10 @@ import {computeStaticStyling} from '@angular/core/src/render3/styling/static_sty describe('static styling', () => { const mockFirstCreatePassLView: LView = [null, {firstCreatePass: true}] as any; - let tNode !: TNode; + let tNode!: TNode; beforeEach(() => { enterView(mockFirstCreatePassLView, null); - tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); }); it('should initialize when no attrs', () => { computeStaticStyling(tNode, []); diff --git a/packages/core/test/render3/styling_next/style_binding_list_spec.ts b/packages/core/test/render3/styling_next/style_binding_list_spec.ts index d5e66cc0a5ec7..e42a2f50bfd86 100644 --- a/packages/core/test/render3/styling_next/style_binding_list_spec.ts +++ b/packages/core/test/render3/styling_next/style_binding_list_spec.ts @@ -8,7 +8,7 @@ import {createTNode} from '@angular/core/src/render3/instructions/shared'; import {TNode, TNodeType} from '@angular/core/src/render3/interfaces/node'; -import {TStylingKey, TStylingRange, getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate} from '@angular/core/src/render3/interfaces/styling'; +import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '@angular/core/src/render3/interfaces/styling'; import {LView, TData} from '@angular/core/src/render3/interfaces/view'; import {enterView, leaveView} from '@angular/core/src/render3/state'; import {insertTStylingBinding} from '@angular/core/src/render3/styling/style_binding_list'; @@ -20,7 +20,7 @@ describe('TNode styling linked list', () => { afterEach(() => leaveView()); describe('insertTStylingBinding', () => { it('should append template only', () => { - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); const tData: TData = [null, null]; insertTStylingBinding(tData, tNode, 'tmpl1', 2, false, true); @@ -60,7 +60,7 @@ describe('TNode styling linked list', () => { it('should append host only', () => { const tData: TData = [null, null]; - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); insertTStylingBinding(tData, tNode, 'host1', 2, true, true); expectRange(tNode.classBindings).toEqual([2, 0 /* no template binding */]); @@ -79,7 +79,7 @@ describe('TNode styling linked list', () => { }); it('should append template and host', () => { - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); const tData: TData = [null, null]; insertTStylingBinding(tData, tNode, 'tmpl1', 2, false, true); @@ -113,7 +113,7 @@ describe('TNode styling linked list', () => { // ɵɵstyleMap({color: '#007'}); // Binding index: 28 // ɵɵstyleProp('color', '#008'); // Binding index: 30 - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); tNode.styles = ''; const tData: TData = newArray(32, null); @@ -291,12 +291,11 @@ describe('TNode styling linked list', () => { [12, 'color', true, false], // 12 - Template: ɵɵstyleProp('color', '#002'}); ]); }); - }); describe('markDuplicates', () => { it('should not mark items as duplicate if names don\'t match', () => { - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); const tData: TData = [null, null]; insertTStylingBinding(tData, tNode, 'color', 2, false, false); expectPriorityOrder(tData, tNode, false).toEqual([ @@ -321,7 +320,7 @@ describe('TNode styling linked list', () => { }); it('should mark items as duplicate if names match', () => { - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); const tData: TData = [null, null]; insertTStylingBinding(tData, tNode, 'color', 2, false, false); expectPriorityOrder(tData, tNode, false).toEqual([ @@ -345,7 +344,7 @@ describe('TNode styling linked list', () => { }); it('should treat maps as matching all', () => { - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); const tData: TData = [null, null]; insertTStylingBinding(tData, tNode, 'color', 2, false, false); insertTStylingBinding(tData, tNode, 'height', 4, true, false); @@ -365,7 +364,7 @@ describe('TNode styling linked list', () => { }); it('should mark all things after map as duplicate', () => { - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); const tData: TData = [null, null]; insertTStylingBinding(tData, tNode, null, 2, false, false); insertTStylingBinding(tData, tNode, 'height', 4, false, false); @@ -379,7 +378,7 @@ describe('TNode styling linked list', () => { }); it('should mark duplicate on complex objects like width.px', () => { - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); const tData: TData = [null, null]; insertTStylingBinding(tData, tNode, 'width', 2, false, false); insertTStylingBinding(tData, tNode, 'height', 4, false, false); @@ -406,7 +405,7 @@ describe('TNode styling linked list', () => { }); it('should mark duplicate on static fields', () => { - const tNode = createTNode(null !, null !, TNodeType.Element, 0, '', null); + const tNode = createTNode(null!, null!, TNodeType.Element, 0, '', null); tNode.residualStyles = ['color', 'blue'] as any; const tData: TData = [null, null]; insertTStylingBinding(tData, tNode, 'width', 2, false, false); @@ -431,7 +430,6 @@ describe('TNode styling linked list', () => { ]); }); }); - }); const empty_0_through_9 = [null, null, null, null, null, null, null, null, null, null]; @@ -460,7 +458,7 @@ function expectTData(tData: TData) { function expectPriorityOrder(tData: TData, tNode: TNode, isClassBinding: boolean) { // first find head. let index = getStylingBindingHead(tData, tNode, isClassBinding); - const indexes: [number, string | null, boolean, boolean][] = []; + const indexes: [number, string|null, boolean, boolean][] = []; while (index !== 0) { let key = tData[index] as TStylingKey | null; const tStylingRange = tData[index + 1] as TStylingRange; diff --git a/packages/core/test/render3/testing_spec.ts b/packages/core/test/render3/testing_spec.ts index 850534020345f..cd1e2844d7d87 100644 --- a/packages/core/test/render3/testing_spec.ts +++ b/packages/core/test/render3/testing_spec.ts @@ -24,7 +24,7 @@ describe('testing', () => { return Promise.resolve(true).then(() => passed = true); })); - it('should support async and await', withBody('<span>works!</span>', async() => { + it('should support async and await', withBody('<span>works!</span>', async () => { await Promise.resolve(true); passed = true; })); diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index 27ab15e59ab66..5195c67e5ed49 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -39,7 +39,7 @@ describe('ViewContainerRef', () => { }); // TODO(issue/24571): remove '!'. - tplRef !: TemplateRef<{}>; + tplRef!: TemplateRef<{}>; name: string = ''; @@ -49,9 +49,7 @@ describe('ViewContainerRef', () => { } describe('API', () => { - describe('createEmbeddedView (incl. insert)', () => { - it('should add embedded views at the right position in the DOM tree (ng-template next to other ng-template)', () => { let directiveInstances: TestDirective[] = []; @@ -75,9 +73,13 @@ describe('ViewContainerRef', () => { constructor(private _vcRef: ViewContainerRef, private _tplRef: TemplateRef<{}>) {} - insertTpl(ctx: {}) { this._vcRef.createEmbeddedView(this._tplRef, ctx); } + insertTpl(ctx: {}) { + this._vcRef.createEmbeddedView(this._tplRef, ctx); + } - remove(index?: number) { this._vcRef.remove(index); } + remove(index?: number) { + this._vcRef.remove(index); + } } function EmbeddedTemplateA(rf: RenderFlags, ctx: any) { @@ -100,7 +102,7 @@ describe('ViewContainerRef', () => { */ class TestComponent { // TODO(issue/24571): remove '!'. - testDir !: TestDirective; + testDir!: TestDirective; static ɵfac = () => new TestComponent(); static ɵcmp = ɵɵdefineComponent({ type: TestComponent, @@ -109,14 +111,15 @@ describe('ViewContainerRef', () => { decls: 4, vars: 0, consts: [['testdir', '']], - template: (rf: RenderFlags, cmp: TestComponent) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0, 'before|'); - ɵɵtemplate(1, EmbeddedTemplateA, 1, 0, 'ng-template', 0); - ɵɵtemplate(2, EmbeddedTemplateB, 1, 0, 'ng-template', 0); - ɵɵtext(3, '|after'); - } - }, + template: + (rf: RenderFlags, cmp: TestComponent) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0, 'before|'); + ɵɵtemplate(1, EmbeddedTemplateA, 1, 0, 'ng-template', 0); + ɵɵtemplate(2, EmbeddedTemplateB, 1, 0, 'ng-template', 0); + ɵɵtext(3, '|after'); + } + }, directives: [TestDirective] }); } @@ -124,10 +127,10 @@ describe('ViewContainerRef', () => { const fixture = new ComponentFixture(TestComponent); expect(fixture.html).toEqual('before||after'); - directiveInstances ![1].insertTpl({}); + directiveInstances![1].insertTpl({}); expect(fixture.html).toEqual('before|B|after'); - directiveInstances ![0].insertTpl({}); + directiveInstances![0].insertTpl({}); expect(fixture.html).toEqual('before|AB|after'); }); @@ -145,14 +148,18 @@ describe('ViewContainerRef', () => { constructor(private _vcRef: ViewContainerRef, private _tplRef: TemplateRef<{}>) {} - insertTpl(ctx: {}) { this._vcRef.createEmbeddedView(this._tplRef, ctx); } + insertTpl(ctx: {}) { + this._vcRef.createEmbeddedView(this._tplRef, ctx); + } insertTpl2(ctx: {}) { const viewRef = this._tplRef.createEmbeddedView(ctx); this._vcRef.insert(viewRef); } - remove(index?: number) { this._vcRef.remove(index); } + remove(index?: number) { + this._vcRef.remove(index); + } } function EmbeddedTemplateA(rf: RenderFlags, ctx: any) { @@ -172,7 +179,7 @@ describe('ViewContainerRef', () => { class TestComponent { condition = false; // TODO(issue/24571): remove '!'. - testDir !: TestDirective; + testDir!: TestDirective; static ɵfac = () => new TestComponent(); static ɵcmp = ɵɵdefineComponent({ type: TestComponent, @@ -181,29 +188,30 @@ describe('ViewContainerRef', () => { decls: 4, vars: 0, consts: [['testdir', '']], - template: (rf: RenderFlags, cmp: TestComponent) => { - if (rf & RenderFlags.Create) { - ɵɵtext(0, 'before|'); - ɵɵtemplate(1, EmbeddedTemplateA, 1, 0, 'ng-template', 0); - ɵɵcontainer(2); - ɵɵtext(3, '|after'); - } - if (rf & RenderFlags.Update) { - ɵɵcontainerRefreshStart(2); - { - if (cmp.condition) { - let rf1 = ɵɵembeddedViewStart(0, 1, 0); + template: + (rf: RenderFlags, cmp: TestComponent) => { + if (rf & RenderFlags.Create) { + ɵɵtext(0, 'before|'); + ɵɵtemplate(1, EmbeddedTemplateA, 1, 0, 'ng-template', 0); + ɵɵcontainer(2); + ɵɵtext(3, '|after'); + } + if (rf & RenderFlags.Update) { + ɵɵcontainerRefreshStart(2); { - if (rf1 & RenderFlags.Create) { - ɵɵtext(0, 'B'); + if (cmp.condition) { + let rf1 = ɵɵembeddedViewStart(0, 1, 0); + { + if (rf1 & RenderFlags.Create) { + ɵɵtext(0, 'B'); + } + } + ɵɵembeddedViewEnd(); } } - ɵɵembeddedViewEnd(); + ɵɵcontainerRefreshEnd(); } - } - ɵɵcontainerRefreshEnd(); - } - }, + }, directives: [TestDirective] }); } @@ -215,28 +223,27 @@ describe('ViewContainerRef', () => { fixture.update(); expect(fixture.html).toEqual('before|B|after'); - directiveInstance !.insertTpl({}); + directiveInstance!.insertTpl({}); expect(fixture.html).toEqual('before|AB|after'); fixture.component.condition = false; fixture.update(); expect(fixture.html).toEqual('before|A|after'); - directiveInstance !.insertTpl2({}); + directiveInstance!.insertTpl2({}); expect(fixture.html).toEqual('before|AA|after'); fixture.component.condition = true; fixture.update(); expect(fixture.html).toEqual('before|AAB|after'); }); - }); describe('createComponent', () => { let templateExecutionCounter = 0; describe('ComponentRef', () => { - let dynamicComp !: DynamicComp; + let dynamicComp!: DynamicComp; class AppComp { constructor(public vcr: ViewContainerRef, public cfr: ComponentFactoryResolver) {} @@ -259,7 +266,9 @@ describe('ViewContainerRef', () => { class DynamicComp { doCheckCount = 0; - ngDoCheck() { this.doCheckCount++; } + ngDoCheck() { + this.doCheckCount++; + } static ɵfac = () => dynamicComp = new DynamicComp(); @@ -313,8 +322,9 @@ describe('ViewContainerRef', () => { fixture.component.vcr.detach(fixture.component.vcr.indexOf(ref.hostView)); - expect(() => { ref.destroy(); }).not.toThrow(); - + expect(() => { + ref.destroy(); + }).not.toThrow(); }); }); }); @@ -330,12 +340,12 @@ describe('ViewContainerRef', () => { createTemplate, undefined, 2, 0, [DirectiveWithVCRef], null, null, undefined, [['vcref', '']]); - expect(directiveInstance !.vcref.element.nativeElement.tagName.toLowerCase()) + expect(directiveInstance!.vcref.element.nativeElement.tagName.toLowerCase()) .toEqual('header'); expect( - directiveInstance !.vcref.injector.get(ElementRef).nativeElement.tagName.toLowerCase()) + directiveInstance!.vcref.injector.get(ElementRef).nativeElement.tagName.toLowerCase()) .toEqual('header'); - expect(() => directiveInstance !.vcref.parentInjector.get(ElementRef)).toThrow(); + expect(() => directiveInstance!.vcref.parentInjector.get(ElementRef)).toThrow(); }); it('should work on components', () => { @@ -351,18 +361,17 @@ describe('ViewContainerRef', () => { createTemplate, undefined, 2, 0, [HeaderComponent, DirectiveWithVCRef], null, null, undefined, [['vcref', '']]); - expect(directiveInstance !.vcref.element.nativeElement.tagName.toLowerCase()) + expect(directiveInstance!.vcref.element.nativeElement.tagName.toLowerCase()) .toEqual('header-cmp'); expect( - directiveInstance !.vcref.injector.get(ElementRef).nativeElement.tagName.toLowerCase()) + directiveInstance!.vcref.injector.get(ElementRef).nativeElement.tagName.toLowerCase()) .toEqual('header-cmp'); - expect(() => directiveInstance !.vcref.parentInjector.get(ElementRef)).toThrow(); + expect(() => directiveInstance!.vcref.parentInjector.get(ElementRef)).toThrow(); }); }); }); describe('view engine compatibility', () => { - @Component({selector: 'app', template: ''}) class AppCmpt { static ɵfac = () => @@ -383,14 +392,17 @@ describe('ViewContainerRef', () => { this._vcRef.createComponent(this._cfResolver.resolveComponentFactory(comp)); } - clear() { this._vcRef.clear(); } + clear() { + this._vcRef.clear(); + } - getVCRefParentInjector() { return this._vcRef.parentInjector; } + getVCRefParentInjector() { + return this._vcRef.parentInjector; + } } // https://stackblitz.com/edit/angular-xxpffd?file=src%2Findex.html it('should allow injecting VCRef into the root (bootstrapped) component', () => { - const DynamicComponent = createComponent('dynamic-cmpt', function(rf: RenderFlags, parent: any) { if (rf & RenderFlags.Create) { @@ -428,12 +440,12 @@ describe('ViewContainerRef', () => { }); it('should support view queries for dynamically created components', () => { - let dynamicComp !: DynamicCompWithViewQueries; - let fooEl !: RElement; + let dynamicComp!: DynamicCompWithViewQueries; + let fooEl!: RElement; class DynamicCompWithViewQueries { // @ViewChildren('foo') - foo !: QueryList<any>; + foo!: QueryList<any>; static ɵfac = () => dynamicComp = new DynamicCompWithViewQueries(); static ɵcmp = ɵɵdefineComponent({ @@ -442,23 +454,25 @@ describe('ViewContainerRef', () => { decls: 2, vars: 0, consts: [['foo', ''], ['bar', '']], - template: (rf: RenderFlags, ctx: DynamicCompWithViewQueries) => { - if (rf & RenderFlags.Create) { - ɵɵelement(0, 'div', 1, 0); - } - // testing only - fooEl = getNativeByIndex(0, getLView()) as RElement; - }, - viewQuery: function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵviewQuery(['foo'], true); - } - if (rf & RenderFlags.Update) { - let tmp: any; - ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && - (ctx.foo = tmp as QueryList<any>); - } - } + template: + (rf: RenderFlags, ctx: DynamicCompWithViewQueries) => { + if (rf & RenderFlags.Create) { + ɵɵelement(0, 'div', 1, 0); + } + // testing only + fooEl = getNativeByIndex(0, getLView()) as RElement; + }, + viewQuery: + function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵviewQuery(['foo'], true); + } + if (rf & RenderFlags.Update) { + let tmp: any; + ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) && + (ctx.foo = tmp as QueryList<any>); + } + } }); } @@ -469,7 +483,6 @@ describe('ViewContainerRef', () => { expect(dynamicComp.foo.first.nativeElement).toEqual(fooEl as any); }); - }); describe('view destruction', () => { @@ -478,7 +491,9 @@ describe('ViewContainerRef', () => { onClick() {} - ngOnDestroy() { this.viewRef.destroy(); } + ngOnDestroy() { + this.viewRef.destroy(); + } // We want the ViewRef, so we rely on the knowledge that `ViewRef` is actually given // when injecting `ChangeDetectorRef`. @@ -491,16 +506,19 @@ describe('ViewContainerRef', () => { decls: 2, vars: 0, /** <button (click)="onClick()"> Click me </button> */ - template: function CompTemplate(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ɵɵelementStart(0, 'button'); - { - ɵɵlistener('click', function() { return ctx.onClick(); }); - ɵɵtext(1, 'Click me'); - } - ɵɵelementEnd(); - } - }, + template: + function CompTemplate(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + ɵɵelementStart(0, 'button'); + { + ɵɵlistener('click', function() { + return ctx.onClick(); + }); + ɵɵtext(1, 'Click me'); + } + ɵɵelementEnd(); + } + }, }); } diff --git a/packages/core/test/render3/view_utils_spec.ts b/packages/core/test/render3/view_utils_spec.ts index caedd809674c1..944921fcfacf1 100644 --- a/packages/core/test/render3/view_utils_spec.ts +++ b/packages/core/test/render3/view_utils_spec.ts @@ -15,7 +15,7 @@ describe('view_utils', () => { const div = document.createElement('div'); const tView = createTView(TViewType.Root, 0, null, 0, 0, null, null, null, null, null); const lView = createLView(null, tView, {}, 0, div, null, {} as any, {} as any, null, null); - const tNode = createTNode(null !, null, 3, 0, 'div', []); + const tNode = createTNode(null!, null, 3, 0, 'div', []); const lContainer = createLContainer(lView, lView, div, tNode); expect(isLView(lView)).toBe(true); diff --git a/packages/core/test/sanitization/html_sanitizer_spec.ts b/packages/core/test/sanitization/html_sanitizer_spec.ts index df917b55df3fa..8e99a670e45d9 100644 --- a/packages/core/test/sanitization/html_sanitizer_spec.ts +++ b/packages/core/test/sanitization/html_sanitizer_spec.ts @@ -13,7 +13,7 @@ import {_sanitizeHtml} from '../../src/sanitization/html_sanitizer'; { describe('HTML sanitizer', () => { let defaultDoc: any; - let originalLog: (msg: any) => any = null !; + let originalLog: (msg: any) => any = null!; let logMsgs: string[]; beforeEach(() => { @@ -23,7 +23,9 @@ import {_sanitizeHtml} from '../../src/sanitization/html_sanitizer'; console.warn = (msg: any) => logMsgs.push(msg); }); - afterEach(() => { console.warn = originalLog; }); + afterEach(() => { + console.warn = originalLog; + }); it('serializes nested structures', () => { expect(_sanitizeHtml(defaultDoc, '<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>')) @@ -36,8 +38,9 @@ import {_sanitizeHtml} from '../../src/sanitization/html_sanitizer'; .toEqual('<p>Hello <br> World</p>'); }); - it('supports namespaced elements', - () => { expect(_sanitizeHtml(defaultDoc, 'a<my:hr/><my:div>b</my:div>c')).toEqual('abc'); }); + it('supports namespaced elements', () => { + expect(_sanitizeHtml(defaultDoc, 'a<my:hr/><my:div>b</my:div>c')).toEqual('abc'); + }); it('supports namespaced attributes', () => { expect(_sanitizeHtml(defaultDoc, '<a xlink:href="something">t</a>')) @@ -66,8 +69,9 @@ import {_sanitizeHtml} from '../../src/sanitization/html_sanitizer'; .toEqual('<img srcset="/foo.png 400px, unsafe:javascript:evil() 23px">'); }); - it('supports sanitizing plain text', - () => { expect(_sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World'); }); + it('supports sanitizing plain text', () => { + expect(_sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World'); + }); it('ignores non-element, non-attribute nodes', () => { expect(_sanitizeHtml(defaultDoc, '<!-- comments? -->no.')).toEqual('no.'); @@ -104,8 +108,9 @@ import {_sanitizeHtml} from '../../src/sanitization/html_sanitizer'; 'select', ]; for (const tag of dangerousTags) { - it(tag, - () => { expect(_sanitizeHtml(defaultDoc, `<${tag}>evil!</${tag}>`)).toEqual('evil!'); }); + it(tag, () => { + expect(_sanitizeHtml(defaultDoc, `<${tag}>evil!</${tag}>`)).toEqual('evil!'); + }); } const dangerousSelfClosingTags = [ @@ -129,7 +134,9 @@ import {_sanitizeHtml} from '../../src/sanitization/html_sanitizer'; 'template', ]; for (const tag of dangerousSkipContentTags) { - it(tag, () => { expect(_sanitizeHtml(defaultDoc, `<${tag}>evil!</${tag}>`)).toEqual(''); }); + it(tag, () => { + expect(_sanitizeHtml(defaultDoc, `<${tag}>evil!</${tag}>`)).toEqual(''); + }); } it(`frame`, () => { diff --git a/packages/core/test/sanitization/sanitization_spec.ts b/packages/core/test/sanitization/sanitization_spec.ts index 2c7a8d2650c61..d9dcc1bae16ae 100644 --- a/packages/core/test/sanitization/sanitization_spec.ts +++ b/packages/core/test/sanitization/sanitization_spec.ts @@ -24,7 +24,9 @@ describe('sanitization', () => { afterEach(() => leaveView()); class Wrap { constructor(private value: string) {} - toString() { return this.value; } + toString() { + return this.value; + } } it('should sanitize html', () => { expect(ɵɵsanitizeHtml('<div></div>')).toEqual('<div></div>'); @@ -96,7 +98,7 @@ describe('sanitization', () => { contextsByProp.set(prop, contexts); // check only in case a prop can be a part of both URL contexts if (contexts.size === 2) { - expect(getUrlSanitizer(tag, prop)).toEqual(sanitizerNameByContext.get(context) !); + expect(getUrlSanitizer(tag, prop)).toEqual(sanitizerNameByContext.get(context)!); } } }); diff --git a/packages/core/test/sanitization/style_sanitizer_spec.ts b/packages/core/test/sanitization/style_sanitizer_spec.ts index d96251b482bed..0a776483f9bb9 100644 --- a/packages/core/test/sanitization/style_sanitizer_spec.ts +++ b/packages/core/test/sanitization/style_sanitizer_spec.ts @@ -19,9 +19,13 @@ describe('Style sanitizer', () => { console.warn = (msg: any) => logMsgs.push(msg); }); - afterEach(() => { console.warn = originalLog; }); + afterEach(() => { + console.warn = originalLog; + }); - function expectSanitize(v: string) { return expect(_sanitizeStyle(v)); } + function expectSanitize(v: string) { + return expect(_sanitizeStyle(v)); + } it('sanitizes values', () => { expectSanitize('').toEqual(''); @@ -31,7 +35,9 @@ describe('Style sanitizer', () => { expectSanitize('expression(haha)').toEqual('unsafe'); }); - it('rejects unblanaced quotes', () => { expectSanitize('"value" "').toEqual('unsafe'); }); + it('rejects unblanaced quotes', () => { + expectSanitize('"value" "').toEqual('unsafe'); + }); it('accepts transform functions', () => { expectSanitize('rotate(90deg)').toEqual('rotate(90deg)'); @@ -47,12 +53,17 @@ describe('Style sanitizer', () => { .toEqual('repeating-radial-gradient(ellipse cover, black, red, black, red)'); }); - it('accepts attr', () => { expectSanitize('attr(value string)').toEqual('attr(value string)'); }); + it('accepts attr', () => { + expectSanitize('attr(value string)').toEqual('attr(value string)'); + }); - it('accepts calc', () => { expectSanitize('calc(90%-123px)').toEqual('calc(90%-123px)'); }); + it('accepts calc', () => { + expectSanitize('calc(90%-123px)').toEqual('calc(90%-123px)'); + }); - it('accepts var', - () => { expectSanitize('var(--my-custom-var)').toEqual('var(--my-custom-var)'); }); + it('accepts var', () => { + expectSanitize('var(--my-custom-var)').toEqual('var(--my-custom-var)'); + }); it('sanitizes URLs', () => { expectSanitize('url(foo/bar.png)').toEqual('url(foo/bar.png)'); diff --git a/packages/core/test/sanitization/url_sanitizer_spec.ts b/packages/core/test/sanitization/url_sanitizer_spec.ts index 69c1f705a483a..6c53c9328adbe 100644 --- a/packages/core/test/sanitization/url_sanitizer_spec.ts +++ b/packages/core/test/sanitization/url_sanitizer_spec.ts @@ -21,7 +21,9 @@ import {_sanitizeUrl, sanitizeSrcset} from '../../src/sanitization/url_sanitizer console.warn = (msg: any) => logMsgs.push(msg); }); - afterEach(() => { console.warn = originalLog; }); + afterEach(() => { + console.warn = originalLog; + }); t.it('reports unsafe URLs', () => { t.expect(_sanitizeUrl('javascript:evil()')).toBe('unsafe:javascript:evil()'); @@ -113,6 +115,5 @@ import {_sanitizeUrl, sanitizeSrcset} from '../../src/sanitization/url_sanitizer t.it(`valid ${srcset}`, () => t.expect(sanitizeSrcset(srcset)).toMatch(/unsafe:/)); } }); - }); } diff --git a/packages/core/test/spies.ts b/packages/core/test/spies.ts index 08acdd7c9601e..ff2638cbe4c59 100644 --- a/packages/core/test/spies.ts +++ b/packages/core/test/spies.ts @@ -22,5 +22,7 @@ export class SpyChangeDetectorRef extends SpyObject { export class SpyIterableDifferFactory extends SpyObject {} export class SpyElementRef extends SpyObject { - constructor() { super(ElementRef); } + constructor() { + super(ElementRef); + } } diff --git a/packages/core/test/strict_types/inheritance_spec.ts b/packages/core/test/strict_types/inheritance_spec.ts index 29edf027953fc..28e6b3aa66247 100644 --- a/packages/core/test/strict_types/inheritance_spec.ts +++ b/packages/core/test/strict_types/inheritance_spec.ts @@ -9,7 +9,7 @@ import {ɵɵComponentDefWithMeta, ɵɵPipeDefWithMeta as PipeDefWithMeta} from '@angular/core'; declare class SuperComponent { - static ɵcmp: ɵɵComponentDefWithMeta<SuperComponent, '[super]', never, {}, {}, never>; + static ɵcmp: ɵɵComponentDefWithMeta<SuperComponent, '[super]', never, {}, {}, never, never>; } declare class SubComponent extends SuperComponent { @@ -18,10 +18,12 @@ declare class SubComponent extends SuperComponent { // would produce type errors when the "strictFunctionTypes" option is enabled. onlyInSubtype: string; - static ɵcmp: ɵɵComponentDefWithMeta<SubComponent, '[sub]', never, {}, {}, never>; + static ɵcmp: ɵɵComponentDefWithMeta<SubComponent, '[sub]', never, {}, {}, never, never>; } -declare class SuperPipe { static ɵpipe: PipeDefWithMeta<SuperPipe, 'super'>; } +declare class SuperPipe { + static ɵpipe: PipeDefWithMeta<SuperPipe, 'super'>; +} declare class SubPipe extends SuperPipe { onlyInSubtype: string; diff --git a/packages/core/test/test_bed_async_spec.ts b/packages/core/test/test_bed_async_spec.ts index 56c3ed092bb78..4f158c5e8bd4f 100644 --- a/packages/core/test/test_bed_async_spec.ts +++ b/packages/core/test/test_bed_async_spec.ts @@ -10,9 +10,12 @@ import {TestBed} from '@angular/core/testing/src/test_bed'; import {AsyncTestCompleter, ddescribe, describe, inject, it} from '@angular/core/testing/src/testing_internal'; describe('TestBed with async processing', () => { - - beforeEach(() => { TestBed.resetTestingModule(); }); + beforeEach(() => { + TestBed.resetTestingModule(); + }); it('should allow injecting AsyncTestCompleter', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { async.done(); })); + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + async.done(); + })); }); diff --git a/packages/core/test/test_bed_spec.ts b/packages/core/test/test_bed_spec.ts index 5ec2d3f55671c..ff5235974c813 100644 --- a/packages/core/test/test_bed_spec.ts +++ b/packages/core/test/test_bed_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectorRef, Compiler, Component, Directive, ErrorHandler, Inject, Injectable, InjectionToken, Injector, Input, ModuleWithProviders, NgModule, Optional, Pipe, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵsetNgModuleScope as setNgModuleScope, ɵɵtext as text} from '@angular/core'; -import {TestBed, getTestBed} from '@angular/core/testing/src/test_bed'; +import {APP_INITIALIZER, ChangeDetectorRef, Compiler, Component, Directive, ErrorHandler, Inject, Injectable, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, Optional, Pipe, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵsetNgModuleScope as setNgModuleScope, ɵɵtext as text} from '@angular/core'; +import {getTestBed, TestBed} from '@angular/core/testing/src/test_bed'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {onlyInIvy} from '@angular/private/testing'; @@ -18,7 +18,9 @@ const NAME = new InjectionToken<string>('name'); class SimpleService { static ngOnDestroyCalls: number = 0; id: number = 1; - ngOnDestroy() { SimpleService.ngOnDestroyCalls++; } + ngOnDestroy() { + SimpleService.ngOnDestroyCalls++; + } } // -- module: HWModule @@ -245,6 +247,21 @@ describe('TestBed', () => { expect(hello.nativeElement).toHaveText('Hello World!'); }); + it('should run `APP_INITIALIZER` before accessing `LOCALE_ID` provider', () => { + let locale: string = ''; + @NgModule({ + providers: [ + {provide: APP_INITIALIZER, useValue: () => locale = 'fr-FR', multi: true}, + {provide: LOCALE_ID, useFactory: () => locale} + ] + }) + class TestModule { + } + + TestBed.configureTestingModule({imports: [TestModule]}); + expect(TestBed.inject(LOCALE_ID)).toBe('fr-FR'); + }); + it('allow to override a provider', () => { TestBed.overrideProvider(NAME, {useValue: 'injected World!'}); const hello = TestBed.createComponent(HelloWorld); @@ -310,7 +327,7 @@ describe('TestBed', () => { template: `<test-cmp #testCmpCtrl></test-cmp>`, }) class AppComponent { - @ViewChild('testCmpCtrl', {static: true}) testCmpCtrl !: TestComponent; + @ViewChild('testCmpCtrl', {static: true}) testCmpCtrl!: TestComponent; } @NgModule({ @@ -342,6 +359,126 @@ describe('TestBed', () => { }); }); + describe('nested module overrides using TestBed.overrideModule', () => { + // Set up an NgModule hierarchy with two modules, A and B, each with their own component. + // Module B additionally re-exports module A. Also declare two mock components which can be + // used in tests to verify that overrides within this hierarchy are working correctly. + + // ModuleA content: + + @Component({ + selector: 'comp-a', + template: 'comp-a content', + }) + class CompA { + } + + @Component({ + selector: 'comp-a', + template: 'comp-a mock content', + }) + class MockCompA { + } + + @NgModule({ + declarations: [CompA], + exports: [CompA], + }) + class ModuleA { + } + + // ModuleB content: + + @Component({ + selector: 'comp-b', + template: 'comp-b content', + }) + class CompB { + } + + @Component({ + selector: 'comp-b', + template: 'comp-b mock content', + }) + class MockCompB { + } + + @NgModule({ + imports: [ModuleA], + declarations: [CompB], + exports: [CompB, ModuleA], + }) + class ModuleB { + } + + // AppModule content: + + @Component({ + selector: 'app', + template: ` + <comp-a></comp-a> + <comp-b></comp-b> + `, + }) + class App { + } + + @NgModule({ + imports: [ModuleB], + exports: [ModuleB], + }) + class AppModule { + } + + it('should detect nested module override', () => { + TestBed + .configureTestingModule({ + declarations: [App], + // AppModule -> ModuleB -> ModuleA (to be overridden) + imports: [AppModule], + }) + .overrideModule(ModuleA, { + remove: {declarations: [CompA], exports: [CompA]}, + add: {declarations: [MockCompA], exports: [MockCompA]} + }) + .compileComponents(); + + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + // CompA is overridden, expect mock content. + expect(fixture.nativeElement.textContent).toContain('comp-a mock content'); + + // CompB is not overridden, expect original content. + expect(fixture.nativeElement.textContent).toContain('comp-b content'); + }); + + it('should detect chained modules override', () => { + TestBed + .configureTestingModule({ + declarations: [App], + // AppModule -> ModuleB (to be overridden) -> ModuleA (to be overridden) + imports: [AppModule], + }) + .overrideModule(ModuleA, { + remove: {declarations: [CompA], exports: [CompA]}, + add: {declarations: [MockCompA], exports: [MockCompA]} + }) + .overrideModule(ModuleB, { + remove: {declarations: [CompB], exports: [CompB]}, + add: {declarations: [MockCompB], exports: [MockCompB]} + }) + .compileComponents(); + + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + // Both CompA and CompB are overridden, expect mock content for both. + expect(fixture.nativeElement.textContent).toContain('comp-a mock content'); + expect(fixture.nativeElement.textContent).toContain('comp-b mock content'); + }); + }); + describe('multi providers', () => { const multiToken = new InjectionToken<string[]>('multiToken'); const singleToken = new InjectionToken<string>('singleToken'); @@ -388,7 +525,7 @@ describe('TestBed', () => { it('overridden with an array', () => { const overrideValue = ['override']; - TestBed.overrideProvider(multiToken, { useValue: overrideValue, multi: true } as any); + TestBed.overrideProvider(multiToken, {useValue: overrideValue, multi: true} as any); const value = TestBed.inject(multiToken); expect(value.length).toEqual(overrideValue.length); @@ -399,7 +536,7 @@ describe('TestBed', () => { // This is actually invalid because multi providers return arrays. We have this here so we can // ensure Ivy behaves the same as VE does currently. const overrideValue = 'override'; - TestBed.overrideProvider(multiToken, { useValue: overrideValue, multi: true } as any); + TestBed.overrideProvider(multiToken, {useValue: overrideValue, multi: true} as any); const value = TestBed.inject(multiToken); expect(value.length).toEqual(overrideValue.length); @@ -459,12 +596,16 @@ describe('TestBed', () => { it('should allow overriding a provider defined via ModuleWithProviders (using TestBed.overrideProvider)', () => { const serviceOverride = { - get() { return 'override'; }, + get() { + return 'override'; + }, }; @Injectable({providedIn: 'root'}) class MyService { - get() { return 'original'; } + get() { + return 'original'; + } } @NgModule({}) @@ -508,12 +649,16 @@ describe('TestBed', () => { it('should allow overriding a provider defined via ModuleWithProviders (using TestBed.configureTestingModule)', () => { const serviceOverride = { - get() { return 'override'; }, + get() { + return 'override'; + }, }; @Injectable({providedIn: 'root'}) class MyService { - get() { return 'original'; } + get() { + return 'original'; + } } @NgModule({}) @@ -589,7 +734,9 @@ describe('TestBed', () => { }) class CompA { @Input() inputA: string = ''; - ngOnInit() { log.push('CompA:ngOnInit', this.inputA); } + ngOnInit() { + log.push('CompA:ngOnInit', this.inputA); + } } @Component({ @@ -598,7 +745,9 @@ describe('TestBed', () => { }) class CompB { @Input() inputB: string = ''; - ngOnInit() { log.push('CompB:ngOnInit', this.inputB); } + ngOnInit() { + log.push('CompB:ngOnInit', this.inputB); + } } TestBed.configureTestingModule({declarations: [CompA, CompB]}); @@ -647,13 +796,12 @@ describe('TestBed', () => { TestBed.configureTestingModule({imports: [ProvidesErrorHandler, HelloWorldModule]}); expect(TestBed.inject(ErrorHandler)).toEqual(jasmine.any(CustomErrorHandler)); - }); it('should throw errors in CD', () => { @Component({selector: 'my-comp', template: ''}) class MyComp { - name !: {hello: string}; + name!: {hello: string}; ngOnInit() { // this should throw because this.name is undefined @@ -673,10 +821,9 @@ describe('TestBed', () => { // tests to fail. This is an issue in both View Engine and Ivy, and may require a breaking // change to completely fix (since simple re-throwing breaks handlers in ngrx, etc). xit('should throw errors in listeners', () => { - @Component({selector: 'my-comp', template: '<button (click)="onClick()">Click me</button>'}) class MyComp { - name !: {hello: string}; + name!: {hello: string}; onClick() { // this should throw because this.name is undefined @@ -804,11 +951,12 @@ describe('TestBed', () => { selectors: [['comp']], decls: 1, vars: 0, - template: (rf: any, ctx: any) => { - if (rf & 1) { - text(0, 'Some template'); - } - }, + template: + (rf: any, ctx: any) => { + if (rf & 1) { + text(0, 'Some template'); + } + }, styles: ['body { margin: 0; }'] }); } @@ -883,7 +1031,9 @@ describe('TestBed', () => { it('should restore ng defs to their initial states', () => { @Pipe({name: 'somePipe', pure: true}) class SomePipe { - transform(value: string): string { return `transformed ${value}`; } + transform(value: string): string { + return `transformed ${value}`; + } } @Directive({selector: 'someDirective'}) @@ -949,9 +1099,8 @@ describe('TestBed', () => { class PipeWithNoAnnotations extends SomePipe {} TestBed.configureTestingModule({ - declarations: [ - ComponentWithNoAnnotations, DirectiveWithNoAnnotations, PipeWithNoAnnotations - ] + declarations: + [ComponentWithNoAnnotations, DirectiveWithNoAnnotations, PipeWithNoAnnotations] }); TestBed.createComponent(ComponentWithNoAnnotations); @@ -979,7 +1128,6 @@ describe('TestBed', () => { it('should clean up overridden providers for modules that are imported more than once', () => { - @Injectable() class Token { name: string = 'real'; @@ -1004,7 +1152,7 @@ describe('TestBed', () => { }); it('should clean up overridden providers on components whose modules are compiled more than once', - async() => { + async () => { @Injectable() class SomeInjectable { id: string|undefined; @@ -1083,6 +1231,6 @@ describe('TestBed', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); - expect(fixture !.nativeElement.textContent).toContain('changed'); + expect(fixture!.nativeElement.textContent).toContain('changed'); }); }); diff --git a/packages/core/test/testability/testability_spec.ts b/packages/core/test/testability/testability_spec.ts index ad604b8cf1ee7..bdca80a38c984 100644 --- a/packages/core/test/testability/testability_spec.ts +++ b/packages/core/test/testability/testability_spec.ts @@ -11,7 +11,7 @@ import {Injectable} from '@angular/core/src/di'; import {PendingMacrotask, Testability, TestabilityRegistry} from '@angular/core/src/testability/testability'; import {NgZone} from '@angular/core/src/zone/ng_zone'; import {async, fakeAsync, flush, tick} from '@angular/core/testing'; -import {SpyObject, beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; +import {beforeEach, describe, expect, it, SpyObject} from '@angular/core/testing/src/testing_internal'; import {scheduleMicroTask} from '../../src/util/microtask'; @@ -38,9 +38,13 @@ class MockNgZone extends NgZone { this.onStable = new EventEmitter(false); } - unstable(): void { this.onUnstable.emit(null); } + unstable(): void { + this.onUnstable.emit(null); + } - stable(): void { this.onStable.emit(null); } + stable(): void { + this.onStable.emit(null); + } } { @@ -60,13 +64,16 @@ class MockNgZone extends NgZone { })); describe('Pending count logic', () => { - it('should start with a pending count of 0', - () => { expect(testability.getPendingRequestCount()).toEqual(0); }); + it('should start with a pending count of 0', () => { + expect(testability.getPendingRequestCount()).toEqual(0); + }); it('should fire whenstable callbacks if pending count is 0', async(() => { testability.whenStable(execute); - microTask(() => { expect(execute).toHaveBeenCalled(); }); + microTask(() => { + expect(execute).toHaveBeenCalled(); + }); })); it('should not fire whenstable callbacks synchronously if pending count is 0', () => { @@ -83,7 +90,9 @@ class MockNgZone extends NgZone { expect(execute).not.toHaveBeenCalled(); testability.decreasePendingRequestCount(); - microTask(() => { expect(execute).not.toHaveBeenCalled(); }); + microTask(() => { + expect(execute).not.toHaveBeenCalled(); + }); }); })); @@ -95,7 +104,9 @@ class MockNgZone extends NgZone { expect(execute).not.toHaveBeenCalled(); testability.decreasePendingRequestCount(); - microTask(() => { expect(execute).toHaveBeenCalled(); }); + microTask(() => { + expect(execute).toHaveBeenCalled(); + }); }); })); @@ -111,7 +122,9 @@ class MockNgZone extends NgZone { microTask(() => { testability.whenStable(execute); - microTask(() => { expect(execute).toHaveBeenCalledWith(false); }); + microTask(() => { + expect(execute).toHaveBeenCalledWith(false); + }); }); })); @@ -125,10 +138,11 @@ class MockNgZone extends NgZone { expect(execute).toHaveBeenCalledWith(true); testability.whenStable(execute2); - microTask(() => { expect(execute2).toHaveBeenCalledWith(false); }); + microTask(() => { + expect(execute2).toHaveBeenCalledWith(false); + }); }); })); - }); describe('NgZone callback logic', () => { @@ -144,9 +158,9 @@ class MockNgZone extends NgZone { expect(tasks.length).toEqual(1); expect(tasks[0].data).toBeTruthy(); - expect(tasks[0].data !.delay).toEqual(1000); + expect(tasks[0].data!.delay).toEqual(1000); expect(tasks[0].source).toEqual('setTimeout'); - expect(tasks[0].data !.isPeriodic).toEqual(false); + expect(tasks[0].data!.isPeriodic).toEqual(false); clearTimeout(id); })); @@ -154,7 +168,9 @@ class MockNgZone extends NgZone { it('should fire if Angular is already stable', async(() => { testability.whenStable(execute, 200); - microTask(() => { expect(execute).toHaveBeenCalled(); }); + microTask(() => { + expect(execute).toHaveBeenCalled(); + }); })); it('should fire when macroTasks are cancelled', fakeAsync(() => { @@ -208,11 +224,11 @@ class MockNgZone extends NgZone { expect(execute).toHaveBeenCalled(); const update1 = updateCallback.calls.all()[0].args[0] as PendingMacrotask[]; - expect(update1[0].data !.delay).toEqual(500); + expect(update1[0].data!.delay).toEqual(500); const update2 = updateCallback.calls.all()[1].args[0] as PendingMacrotask[]; - expect(update2[0].data !.delay).toEqual(500); - expect(update2[1].data !.delay).toEqual(300); + expect(update2[0].data!.delay).toEqual(500); + expect(update2[1].data!.delay).toEqual(300); })); it('cancels the done callback if the update callback returns true', fakeAsync(() => { diff --git a/packages/core/test/testing_internal_spec.ts b/packages/core/test/testing_internal_spec.ts index fc29424e97e01..584e93ebf9a0f 100644 --- a/packages/core/test/testing_internal_spec.ts +++ b/packages/core/test/testing_internal_spec.ts @@ -10,23 +10,34 @@ import {SpyObject} from '@angular/core/testing/src/testing_internal'; class TestObj { prop: any; - constructor(prop: any) { this.prop = prop; } - someFunc(): number { return -1; } - someComplexFunc(a: any) { return a; } + constructor(prop: any) { + this.prop = prop; + } + someFunc(): number { + return -1; + } + someComplexFunc(a: any) { + return a; + } } { describe('testing', () => { describe('should respect custom equality tester', () => { beforeEach(() => { - const equalIfMarried = - (first: any, second: any) => { return first === 'kevin' && second === 'patricia'; }; + const equalIfMarried = (first: any, second: any) => { + return first === 'kevin' && second === 'patricia'; + }; jasmine.addCustomEqualityTester(equalIfMarried); }); - it('for positive test', () => { expect('kevin').toEqual('patricia'); }); + it('for positive test', () => { + expect('kevin').toEqual('patricia'); + }); - it('for negative test', () => { expect('kevin').not.toEqual('kevin'); }); + it('for negative test', () => { + expect('kevin').not.toEqual('kevin'); + }); }); describe('equality', () => { @@ -83,10 +94,13 @@ class TestObj { describe('spy objects', () => { let spyObj: any; - beforeEach(() => { spyObj = new SpyObject(TestObj); }); + beforeEach(() => { + spyObj = new SpyObject(TestObj); + }); - it('should return a new spy func with no calls', - () => { expect(spyObj.spy('someFunc')).not.toHaveBeenCalled(); }); + it('should return a new spy func with no calls', () => { + expect(spyObj.spy('someFunc')).not.toHaveBeenCalled(); + }); it('should record function calls', () => { spyObj.spy('someFunc').and.callFake((a: any, b: any) => a + b); diff --git a/packages/core/test/util/array_utils_spec.ts b/packages/core/test/util/array_utils_spec.ts index 5d2fdd6cef815..48ef711673ff6 100644 --- a/packages/core/test/util/array_utils_spec.ts +++ b/packages/core/test/util/array_utils_spec.ts @@ -6,15 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {KeyValueArray, arrayIndexOfSorted, arrayInsert, arrayInsert2, arrayInsertSorted, arrayRemoveSorted, arraySplice, flatten, keyValueArrayDelete, keyValueArrayGet, keyValueArrayIndexOf, keyValueArraySet} from '../../src/util/array_utils'; +import {arrayIndexOfSorted, arrayInsert, arrayInsert2, arrayInsertSorted, arrayRemoveSorted, arraySplice, flatten, KeyValueArray, keyValueArrayDelete, keyValueArrayGet, keyValueArrayIndexOf, keyValueArraySet} from '../../src/util/array_utils'; describe('array_utils', () => { - describe('flatten', () => { + it('should flatten an empty array', () => { + expect(flatten([])).toEqual([]); + }); - it('should flatten an empty array', () => { expect(flatten([])).toEqual([]); }); - - it('should flatten a flat array', () => { expect(flatten([1, 2, 3])).toEqual([1, 2, 3]); }); + it('should flatten a flat array', () => { + expect(flatten([1, 2, 3])).toEqual([1, 2, 3]); + }); it('should flatten a nested array depth-first', () => { expect(flatten([1, [2], 3])).toEqual([1, 2, 3]); @@ -75,7 +77,6 @@ describe('array_utils', () => { }); describe('arrayInsertSorted', () => { - it('should insert items don\'t allow duplicates', () => { let a; a = ['a', 'c', 'e', 'g', 'i']; @@ -103,7 +104,6 @@ describe('array_utils', () => { describe('arrayRemoveSorted', () => { - it('should remove items', () => { let a; a = ['a', 'b', 'c', 'd', 'e']; diff --git a/packages/core/test/util/decorators_spec.ts b/packages/core/test/util/decorators_spec.ts index f657f96f254e8..d418f01a6309f 100644 --- a/packages/core/test/util/decorators_spec.ts +++ b/packages/core/test/util/decorators_spec.ts @@ -24,8 +24,7 @@ class DecoratedChild extends DecoratedParent {} const Prop = makePropDecorator('Prop', (value: any) => ({value})); class TestClass { - @Prop('firefox!') - watch: any; + @Prop('firefox!') watch: any; } const p = reflector.propMetadata(TestClass); diff --git a/packages/core/test/util/global_spec.ts b/packages/core/test/util/global_spec.ts index c74e33ea69ca6..7aff2fcd21627 100644 --- a/packages/core/test/util/global_spec.ts +++ b/packages/core/test/util/global_spec.ts @@ -19,7 +19,9 @@ declare var globalThis: any /** TODO #9100 */; }); if (typeof globalThis !== 'undefined') { - it('should use globalThis as global reference', () => { expect(global).toBe(globalThis); }); + it('should use globalThis as global reference', () => { + expect(global).toBe(globalThis); + }); } }); } diff --git a/packages/core/test/util/lang_spec.ts b/packages/core/test/util/lang_spec.ts index 73086b09cf494..d734463171e4c 100644 --- a/packages/core/test/util/lang_spec.ts +++ b/packages/core/test/util/lang_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {isObservable, isPromise} from '@angular/core/src/util/lang'; -import {of } from 'rxjs'; +import {of} from 'rxjs'; { describe('isPromise', () => { @@ -28,7 +28,7 @@ import {of } from 'rxjs'; }); describe('isObservable', () => { - it('should be true for an Observable', () => expect(isObservable(of (true))).toEqual(true)); + it('should be true for an Observable', () => expect(isObservable(of(true))).toEqual(true)); it('should be true if the argument is the object with subscribe function', () => expect(isObservable({subscribe: () => {}})).toEqual(true)); diff --git a/packages/core/test/util/stringify_spec.ts b/packages/core/test/util/stringify_spec.ts index e5ce710f6c0ec..d2e15e2f049d5 100644 --- a/packages/core/test/util/stringify_spec.ts +++ b/packages/core/test/util/stringify_spec.ts @@ -21,7 +21,8 @@ describe('stringify', () => { expect(concatStringsWithSpace('', 'b')).toEqual('b'); }); - it('should concat when not empty', - () => { expect(concatStringsWithSpace('before', 'after')).toEqual('before after'); }); + it('should concat when not empty', () => { + expect(concatStringsWithSpace('before', 'after')).toEqual('before after'); + }); }); }); diff --git a/packages/core/test/view/anchor_spec.ts b/packages/core/test/view/anchor_spec.ts index d19a5a3d3f411..d5e5e9535acf6 100644 --- a/packages/core/test/view/anchor_spec.ts +++ b/packages/core/test/view/anchor_spec.ts @@ -8,13 +8,12 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {getDebugNode} from '@angular/core'; -import {NodeFlags, anchorDef, asElementData, elementDef} from '@angular/core/src/view/index'; +import {anchorDef, asElementData, elementDef, NodeFlags} from '@angular/core/src/view/index'; import {compViewDef, createAndGetRootNodes} from './helper'; { describe(`View Anchor`, () => { - describe('create', () => { it('should create anchor nodes without parents', () => { const rootNodes = createAndGetRootNodes(compViewDef([ @@ -43,7 +42,7 @@ import {compViewDef, createAndGetRootNodes} from './helper'; const someContext = {}; const {view, rootNodes} = createAndGetRootNodes( compViewDef([anchorDef(NodeFlags.None, null, null, 0)]), someContext); - expect(getDebugNode(rootNodes[0]) !.nativeNode).toBe(asElementData(view, 0).renderElement); + expect(getDebugNode(rootNodes[0])!.nativeNode).toBe(asElementData(view, 0).renderElement); }); }); }); diff --git a/packages/core/test/view/component_view_spec.ts b/packages/core/test/view/component_view_spec.ts index 1588cd750604e..cb53b9791b82c 100644 --- a/packages/core/test/view/component_view_spec.ts +++ b/packages/core/test/view/component_view_spec.ts @@ -8,7 +8,7 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {SecurityContext} from '@angular/core'; -import {ArgumentType, BindingFlags, NodeCheckFn, NodeFlags, Services, ViewData, ViewFlags, ViewState, asElementData, directiveDef, elementDef, rootRenderNodes} from '@angular/core/src/view/index'; +import {ArgumentType, asElementData, BindingFlags, directiveDef, elementDef, NodeCheckFn, NodeFlags, rootRenderNodes, Services, ViewData, ViewFlags, ViewState} from '@angular/core/src/view/index'; import {callMostRecentEventListenerHandler, compViewDef, createAndGetRootNodes, createRootView, isBrowser, recordNodeToRemove} from './helper'; @@ -23,9 +23,11 @@ const addEventListener = 'addEventListener'; { describe(`Component Views`, () => { it('should create and attach component views', () => { - let instance: AComp = undefined !; + let instance: AComp = undefined!; class AComp { - constructor() { instance = this; } + constructor() { + instance = this; + } } const {view, rootNodes} = createAndGetRootNodes(compViewDef([ @@ -108,15 +110,18 @@ const addEventListener = 'addEventListener'; check(view, 0, ArgumentType.Inline, value); }); - const {view, rootNodes} = createAndGetRootNodes( - compViewDef([ - elementDef(0, NodeFlags.None, null, null, 1, 'div', null, null, null, null, () => compViewDef( - [ - elementDef(0, NodeFlags.None, null, null, 0, 'span', null, [[BindingFlags.TypeElementAttribute, 'a', SecurityContext.NONE]]), - ], null, update - )), - directiveDef(1, NodeFlags.Component, null, 0, AComp, []), - ])); + const {view, rootNodes} = createAndGetRootNodes(compViewDef([ + elementDef( + 0, NodeFlags.None, null, null, 1, 'div', null, null, null, null, + () => compViewDef( + [ + elementDef( + 0, NodeFlags.None, null, null, 0, 'span', null, + [[BindingFlags.TypeElementAttribute, 'a', SecurityContext.NONE]]), + ], + null, update)), + directiveDef(1, NodeFlags.Component, null, 0, AComp, []), + ])); const compView = asElementData(view, 0).componentView; value = 'v1'; @@ -218,13 +223,15 @@ const addEventListener = 'addEventListener'; [ elementDef( 0, NodeFlags.None, null, null, 0, 'span', null, null, - [[null !, 'click']]), + [[null!, 'click']]), ], update, null, ViewFlags.OnPush); }), directiveDef(1, NodeFlags.Component, null, 0, AComp, [], {a: [0, 'a']}), ], - (check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); })); + (check, view) => { + check(view, 1, ArgumentType.Inline, compInputValue); + })); Services.checkAndUpdateView(view); @@ -272,10 +279,21 @@ const addEventListener = 'addEventListener'; 0, NodeFlags.None, null, null, 0, 'span', null, [[BindingFlags.TypeElementAttribute, 'a', SecurityContext.NONE]])], null, update)), - directiveDef(1, NodeFlags.Component, null, 0, AComp, [], null, null, ), + directiveDef( + 1, + NodeFlags.Component, + null, + 0, + AComp, + [], + null, + null, + ), ])); - update.and.callFake((check: NodeCheckFn, view: ViewData) => { throw new Error('Test'); }); + update.and.callFake((check: NodeCheckFn, view: ViewData) => { + throw new Error('Test'); + }); expect(() => Services.checkAndUpdateView(view)).toThrowError('Test'); expect(update).toHaveBeenCalled(); @@ -283,7 +301,6 @@ const addEventListener = 'addEventListener'; expect(() => Services.checkAndUpdateView(view)).toThrowError('Test'); expect(update).toHaveBeenCalled(); }); - }); describe('destroy', () => { @@ -293,7 +310,9 @@ const addEventListener = 'addEventListener'; class AComp {} class ChildProvider { - ngOnDestroy() { log.push('ngOnDestroy'); } + ngOnDestroy() { + log.push('ngOnDestroy'); + } } const {view, rootNodes} = createAndGetRootNodes(compViewDef([ @@ -303,7 +322,16 @@ const addEventListener = 'addEventListener'; elementDef(0, NodeFlags.None, null, null, 1, 'span'), directiveDef(1, NodeFlags.OnDestroy, null, 0, ChildProvider, []) ])), - directiveDef(1, NodeFlags.Component, null, 0, AComp, [], null, null, ), + directiveDef( + 1, + NodeFlags.Component, + null, + 0, + AComp, + [], + null, + null, + ), ])); Services.destroyView(view); @@ -321,6 +349,5 @@ const addEventListener = 'addEventListener'; .toThrowError('ViewDestroyedError: Attempt to use a destroyed view: detectChanges'); }); }); - }); } diff --git a/packages/core/test/view/element_spec.ts b/packages/core/test/view/element_spec.ts index 967cf83884137..551a533adaddb 100644 --- a/packages/core/test/view/element_spec.ts +++ b/packages/core/test/view/element_spec.ts @@ -7,9 +7,9 @@ */ import {ɵgetDOM as getDOM} from '@angular/common'; -import {ErrorHandler, SecurityContext, getDebugNode} from '@angular/core'; +import {ErrorHandler, getDebugNode, SecurityContext} from '@angular/core'; import {getDebugContext} from '@angular/core/src/errors'; -import {BindingFlags, NodeFlags, Services, ViewData, ViewDefinition, asElementData, elementDef} from '@angular/core/src/view/index'; +import {asElementData, BindingFlags, elementDef, NodeFlags, Services, ViewData, ViewDefinition} from '@angular/core/src/view/index'; import {TestBed} from '@angular/core/testing'; import {ARG_TYPE_VALUES, callMostRecentEventListenerHandler, checkNodeInlineOrDynamic, compViewDef, createAndGetRootNodes, isBrowser, recordNodeToRemove} from './helper'; @@ -25,7 +25,6 @@ const removeEventListener = 'removeEventListener'; { describe(`View Elements`, () => { - describe('create', () => { it('should create elements without parents', () => { const rootNodes = createAndGetRootNodes(compViewDef([ @@ -65,14 +64,13 @@ const removeEventListener = 'removeEventListener'; const someContext = {}; const {view, rootNodes} = createAndGetRootNodes( compViewDef([elementDef(0, NodeFlags.None, null, null, 0, 'div')]), someContext); - expect(getDebugNode(rootNodes[0]) !.nativeNode).toBe(asElementData(view, 0).renderElement); + expect(getDebugNode(rootNodes[0])!.nativeNode).toBe(asElementData(view, 0).renderElement); }); }); describe('change properties', () => { ARG_TYPE_VALUES.forEach((inlineDynamic) => { it(`should update via strategy ${inlineDynamic}`, () => { - const {view, rootNodes} = createAndGetRootNodes(compViewDef( [ elementDef( @@ -189,7 +187,7 @@ const removeEventListener = 'removeEventListener'; const removeListenerSpy = spyOn(HTMLElement.prototype, removeEventListener).and.callThrough(); const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef( - 0, NodeFlags.None, null, null, 0, 'button', null, null, [[null !, 'click']], + 0, NodeFlags.None, null, null, 0, 'button', null, null, [[null!, 'click']], handleEventSpy)])); rootNodes[0].click(); @@ -253,10 +251,10 @@ const removeEventListener = 'removeEventListener'; it('should preventDefault only if the handler returns false', () => { let eventHandlerResult: any; - let preventDefaultSpy: jasmine.Spy = undefined !; + let preventDefaultSpy: jasmine.Spy = undefined!; const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef( - 0, NodeFlags.None, null, null, 0, 'button', null, null, [[null !, 'click']], + 0, NodeFlags.None, null, null, 0, 'button', null, null, [[null!, 'click']], (view, eventName, event) => { preventDefaultSpy = spyOn(event, 'preventDefault').and.callThrough(); return eventHandlerResult; @@ -283,8 +281,9 @@ const removeEventListener = 'removeEventListener'; const handleErrorSpy = spyOn(TestBed.inject(ErrorHandler), 'handleError'); const addListenerSpy = spyOn(HTMLElement.prototype, addEventListener).and.callThrough(); const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef( - 0, NodeFlags.None, null, null, 0, 'button', null, null, [[null !, 'click']], - () => { throw new Error('Test'); })])); + 0, NodeFlags.None, null, null, 0, 'button', null, null, [[null!, 'click']], () => { + throw new Error('Test'); + })])); callMostRecentEventListenerHandler(addListenerSpy, 'SomeEvent'); const err = handleErrorSpy.calls.mostRecent().args[0]; diff --git a/packages/core/test/view/embedded_view_spec.ts b/packages/core/test/view/embedded_view_spec.ts index f327981582346..d1c3d5f0b2a50 100644 --- a/packages/core/test/view/embedded_view_spec.ts +++ b/packages/core/test/view/embedded_view_spec.ts @@ -8,13 +8,12 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {SecurityContext} from '@angular/core'; -import {ArgumentType, BindingFlags, NodeCheckFn, NodeFlags, Services, ViewData, anchorDef, asElementData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, moveEmbeddedView, rootRenderNodes} from '@angular/core/src/view/index'; +import {anchorDef, ArgumentType, asElementData, attachEmbeddedView, BindingFlags, detachEmbeddedView, directiveDef, elementDef, moveEmbeddedView, NodeCheckFn, NodeFlags, rootRenderNodes, Services, ViewData} from '@angular/core/src/view/index'; import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedView} from './helper'; { describe(`Embedded Views`, () => { - it('should create embedded views with the right context', () => { const parentContext = {}; const childContext = {}; @@ -39,9 +38,9 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi anchorDef(NodeFlags.EmbeddedViews, null, null, 0, null, compViewDefFactory([ elementDef(0, NodeFlags.None, null, null, 0, 'span', [['name', 'child0']]) ])), - anchorDef(NodeFlags.None, null, null, 0, null, compViewDefFactory([elementDef( - 0, NodeFlags.None, null, null, 0, 'span', - [['name', 'child1']])])) + anchorDef(NodeFlags.None, null, null, 0, null, compViewDefFactory([ + elementDef(0, NodeFlags.None, null, null, 0, 'span', [['name', 'child1']]) + ])) ])); const viewContainerData = asElementData(parentView, 1); const rf = parentView.root.rendererFactory; @@ -58,10 +57,10 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi expect(rootChildren[1].getAttribute('name')).toBe('child0'); expect(rootChildren[2].getAttribute('name')).toBe('child1'); - rf.begin !(); + rf.begin!(); detachEmbeddedView(viewContainerData, 1); detachEmbeddedView(viewContainerData, 0); - rf.end !(); + rf.end!(); expect(rootNodes[0].childNodes.length).toBe(2); }); @@ -72,9 +71,9 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi anchorDef(NodeFlags.EmbeddedViews, null, null, 0, null, compViewDefFactory([ elementDef(0, NodeFlags.None, null, null, 0, 'span', [['name', 'child0']]) ])), - anchorDef(NodeFlags.None, null, null, 0, null, compViewDefFactory([elementDef( - 0, NodeFlags.None, null, null, 0, 'span', - [['name', 'child1']])])) + anchorDef(NodeFlags.None, null, null, 0, null, compViewDefFactory([ + elementDef(0, NodeFlags.None, null, null, 0, 'span', [['name', 'child1']]) + ])) ])); const viewContainerData = asElementData(parentView, 1); @@ -86,7 +85,7 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi moveEmbeddedView(viewContainerData, 0, 1); - expect(viewContainerData.viewContainer !._embeddedViews).toEqual([childView1, childView0]); + expect(viewContainerData.viewContainer!._embeddedViews).toEqual([childView1, childView0]); // 2 anchors + 2 elements const rootChildren = rootNodes[0].childNodes; expect(rootChildren.length).toBe(4); @@ -152,7 +151,9 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi const log: string[] = []; class ChildProvider { - ngOnDestroy() { log.push('ngOnDestroy'); } + ngOnDestroy() { + log.push('ngOnDestroy'); + } } const {view: parentView} = createAndGetRootNodes(compViewDef([ diff --git a/packages/core/test/view/helper.ts b/packages/core/test/view/helper.ts index 537355d7a0a09..3efd574c40ee1 100644 --- a/packages/core/test/view/helper.ts +++ b/packages/core/test/view/helper.ts @@ -8,7 +8,7 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {Injector, NgModuleRef} from '@angular/core'; -import {ArgumentType, NodeCheckFn, NodeDef, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewUpdateFn, initServicesIfNeeded, rootRenderNodes, viewDef} from '@angular/core/src/view/index'; +import {ArgumentType, initServicesIfNeeded, NodeCheckFn, NodeDef, rootRenderNodes, Services, ViewData, viewDef, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewUpdateFn} from '@angular/core/src/view/index'; import {TestBed} from '@angular/core/testing'; export function isBrowser() { @@ -38,11 +38,11 @@ export function createRootView( } export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData { - return Services.createEmbeddedView(parent, anchorDef, anchorDef.element !.template !, context); + return Services.createEmbeddedView(parent, anchorDef, anchorDef.element!.template !, context); } export function compViewDef( - nodes: NodeDef[], updateDirectives?: null | ViewUpdateFn, updateRenderer?: null | ViewUpdateFn, + nodes: NodeDef[], updateDirectives?: null|ViewUpdateFn, updateRenderer?: null|ViewUpdateFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition { const def = viewDef(viewFlags, nodes, updateDirectives, updateRenderer); @@ -53,8 +53,8 @@ export function compViewDef( // This check should be removed when we start reordering nodes at runtime if (node.checkIndex > -1 && node.checkIndex !== node.nodeIndex) { - throw new Error( - `nodeIndex and checkIndex should be the same, got ${node.nodeIndex} !== ${node.checkIndex}`); + throw new Error(`nodeIndex and checkIndex should be the same, got ${node.nodeIndex} !== ${ + node.checkIndex}`); } }); @@ -62,7 +62,7 @@ export function compViewDef( } export function compViewDefFactory( - nodes: NodeDef[], updateDirectives?: null | ViewUpdateFn, updateRenderer?: null | ViewUpdateFn, + nodes: NodeDef[], updateDirectives?: null|ViewUpdateFn, updateRenderer?: null|ViewUpdateFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinitionFactory { return () => compViewDef(nodes, updateDirectives, updateRenderer, viewFlags); } @@ -76,8 +76,12 @@ export function createAndGetRootNodes( let removeNodes: Node[]; -beforeEach(() => { removeNodes = []; }); -afterEach(() => { removeNodes.forEach((node) => getDOM().remove(node)); }); +beforeEach(() => { + removeNodes = []; +}); +afterEach(() => { + removeNodes.forEach((node) => getDOM().remove(node)); +}); export function recordNodeToRemove(node: Node) { removeNodes.push(node); diff --git a/packages/core/test/view/ng_content_spec.ts b/packages/core/test/view/ng_content_spec.ts index a2be95f301168..ae393ba29dec7 100644 --- a/packages/core/test/view/ng_content_spec.ts +++ b/packages/core/test/view/ng_content_spec.ts @@ -8,7 +8,7 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {TemplateRef, ViewContainerRef} from '@angular/core'; -import {NodeDef, NodeFlags, ViewData, ViewDefinition, anchorDef, asElementData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef} from '@angular/core/src/view/index'; +import {anchorDef, asElementData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, NodeDef, NodeFlags, rootRenderNodes, textDef, ViewData, ViewDefinition} from '@angular/core/src/view/index'; import {compViewDef, compViewDefFactory, createEmbeddedView, createRootView, isBrowser} from './helper'; @@ -74,27 +74,23 @@ import {compViewDef, compViewDefFactory, createEmbeddedView, createRootView, isB } } - const {view, rootNodes} = - createAndGetRootNodes( - compViewDef( - hostElDef(0, - [ - anchorDef( - NodeFlags.EmbeddedViews, null, 0, 1, null, - compViewDefFactory([textDef(0, null, ['a'])])), - directiveDef(3, - NodeFlags.None, null, 0, CreateViewService, - [TemplateRef, ViewContainerRef]), - ], - [ - elementDef(0, NodeFlags.None, null, null, 1, 'div'), - ngContentDef(null, 0), - ]))); + const {view, rootNodes} = createAndGetRootNodes(compViewDef(hostElDef( + 0, + [ + anchorDef(NodeFlags.EmbeddedViews, null, 0, 1, null, compViewDefFactory([textDef( + 0, null, ['a'])])), + directiveDef( + 3, NodeFlags.None, null, 0, CreateViewService, [TemplateRef, ViewContainerRef]), + ], + [ + elementDef(0, NodeFlags.None, null, null, 1, 'div'), + ngContentDef(null, 0), + ]))); const anchor = asElementData(view, 2); const child = rootNodes[0].firstChild; expect(child.childNodes[0]).toBe(anchor.renderElement); - const embeddedView = anchor.viewContainer !._embeddedViews[0]; + const embeddedView = anchor.viewContainer!._embeddedViews[0]; expect(child.childNodes[1]).toBe(asTextData(embeddedView, 0).renderText); }); @@ -118,9 +114,9 @@ import {compViewDef, compViewDefFactory, createEmbeddedView, createRootView, isB expect(child.childNodes.length).toBe(3); expect(child.childNodes[1]).toBe(asTextData(view, 2).renderText); - rf.begin !(); + rf.begin!(); detachEmbeddedView(asElementData(componentView, 1), 0); - rf.end !(); + rf.end!(); child = rootNodes[0].firstChild; expect(child.childNodes.length).toBe(1); }); diff --git a/packages/core/test/view/ng_module_spec.ts b/packages/core/test/view/ng_module_spec.ts index 249ecf5bbcf04..0d8d313e1b168 100644 --- a/packages/core/test/view/ng_module_spec.ts +++ b/packages/core/test/view/ng_module_spec.ts @@ -7,10 +7,10 @@ */ import {NgModuleRef, ɵINJECTOR_SCOPE as INJECTOR_SCOPE} from '@angular/core'; -import {InjectFlags, inject} from '@angular/core/src/di'; +import {inject, InjectFlags} from '@angular/core/src/di'; import {Injector} from '@angular/core/src/di/injector'; import {INJECTOR} from '@angular/core/src/di/injector_compatibility'; -import {ɵɵInjectableDef, ɵɵdefineInjectable} from '@angular/core/src/di/interface/defs'; +import {ɵɵdefineInjectable, ɵɵInjectableDef} from '@angular/core/src/di/interface/defs'; import {NgModuleDefinition, NgModuleProviderDef, NodeFlags} from '@angular/core/src/view'; import {moduleDef} from '@angular/core/src/view/ng_module'; import {createNgModuleRef} from '@angular/core/src/view/refs'; @@ -94,25 +94,28 @@ class FromChildWithSkipSelfDep { static ɵprov: ɵɵInjectableDef<FromChildWithSkipSelfDep> = ɵɵdefineInjectable({ token: FromChildWithSkipSelfDep, factory: () => new FromChildWithSkipSelfDep( - inject(ChildDep, InjectFlags.SkipSelf|InjectFlags.Optional), - inject(ChildDep, InjectFlags.Self), - inject(Bar, InjectFlags.Self|InjectFlags.Optional), ), + inject(ChildDep, InjectFlags.SkipSelf|InjectFlags.Optional), + inject(ChildDep, InjectFlags.Self), + inject(Bar, InjectFlags.Self|InjectFlags.Optional), + ), providedIn: MyChildModule, }); } class UsesInject { - constructor() { inject(INJECTOR); } + constructor() { + inject(INJECTOR); + } } function makeProviders(classes: any[], modules: any[]): NgModuleDefinition { - const providers = - classes.map((token, index) => ({ - index, - deps: [], - flags: NodeFlags.TypeClassProvider | NodeFlags.LazyProvider, token, - value: token, - })); + const providers = classes.map((token, index) => ({ + index, + deps: [], + flags: NodeFlags.TypeClassProvider | NodeFlags.LazyProvider, + token, + value: token, + })); return makeModule(modules, providers); } @@ -144,14 +147,17 @@ describe('NgModuleRef_ injector', () => { MyChildModule, ref.injector, [], makeProviders([MyChildModule], [MyChildModule])); }); - it('injects a provided value', - () => { expect(ref.injector.get(Foo) instanceof Foo).toBeTruthy(); }); + it('injects a provided value', () => { + expect(ref.injector.get(Foo) instanceof Foo).toBeTruthy(); + }); - it('injects an InjectableDef value', - () => { expect(ref.injector.get(Bar) instanceof Bar).toBeTruthy(); }); + it('injects an InjectableDef value', () => { + expect(ref.injector.get(Bar) instanceof Bar).toBeTruthy(); + }); - it('caches InjectableDef values', - () => { expect(ref.injector.get(Bar)).toBe(ref.injector.get(Bar)); }); + it('caches InjectableDef values', () => { + expect(ref.injector.get(Bar)).toBe(ref.injector.get(Bar)); + }); it('injects provided deps properly', () => { const instance = ref.injector.get(HasNormalDep); @@ -179,27 +185,32 @@ describe('NgModuleRef_ injector', () => { expect(instance.optionalSelfBar).toBeNull(); }); - it('does not inject something not scoped to the module', - () => { expect(ref.injector.get(Baz, null)).toBeNull(); }); + it('does not inject something not scoped to the module', () => { + expect(ref.injector.get(Baz, null)).toBeNull(); + }); - it('injects with the current injector always set', - () => { expect(() => ref.injector.get(UsesInject)).not.toThrow(); }); + it('injects with the current injector always set', () => { + expect(() => ref.injector.get(UsesInject)).not.toThrow(); + }); it('calls ngOnDestroy on services created via factory', () => { class Module {} class Service { static destroyed = 0; - ngOnDestroy(): void { Service.destroyed++; } + ngOnDestroy(): void { + Service.destroyed++; + } } const ref = createNgModuleRef( - Module, Injector.NULL, [], makeFactoryProviders( - [{ - token: Service, - factory: () => new Service(), - }], - [Module])); + Module, Injector.NULL, [], + makeFactoryProviders( + [{ + token: Service, + factory: () => new Service(), + }], + [Module])); expect(ref.injector.get(Service)).toBeDefined(); expect(Service.destroyed).toBe(0); @@ -213,7 +224,9 @@ describe('NgModuleRef_ injector', () => { class Service { static destroyed = 0; - ngOnDestroy(): void { Service.destroyed++; } + ngOnDestroy(): void { + Service.destroyed++; + } static ɵprov: ɵɵInjectableDef<Service> = ɵɵdefineInjectable({ token: Service, @@ -235,25 +248,28 @@ describe('NgModuleRef_ injector', () => { class Service { static destroyed = 0; - ngOnDestroy(): void { Service.destroyed++; } + ngOnDestroy(): void { + Service.destroyed++; + } } class OtherToken {} const instance = new Service(); const ref = createNgModuleRef( - Module, Injector.NULL, [], makeFactoryProviders( - [ - { - token: Service, - factory: () => instance, - }, - { - token: OtherToken, - factory: () => instance, - } - ], - [Module])); + Module, Injector.NULL, [], + makeFactoryProviders( + [ + { + token: Service, + factory: () => instance, + }, + { + token: OtherToken, + factory: () => instance, + } + ], + [Module])); expect(ref.injector.get(Service)).toBe(instance); expect(ref.injector.get(OtherToken)).toBe(instance); @@ -267,7 +283,9 @@ describe('NgModuleRef_ injector', () => { return { index: 0, flags: NodeFlags.TypeValueProvider | NodeFlags.LazyProvider, - deps: [], token, value + deps: [], + token, + value }; } diff --git a/packages/core/test/view/provider_spec.ts b/packages/core/test/view/provider_spec.ts index cab5b26bc83f3..041b237b12679 100644 --- a/packages/core/test/view/provider_spec.ts +++ b/packages/core/test/view/provider_spec.ts @@ -6,26 +6,29 @@ * found in the LICENSE file at https://angular.io/license */ +import {ɵgetDOM as getDOM} from '@angular/common'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, ErrorHandler, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, Renderer2, SimpleChange, TemplateRef, ViewContainerRef,} from '@angular/core'; import {getDebugContext} from '@angular/core/src/errors'; -import {ArgumentType, DepFlags, NodeFlags, Services, anchorDef, asElementData, directiveDef, elementDef, providerDef, textDef} from '@angular/core/src/view/index'; +import {anchorDef, ArgumentType, asElementData, DepFlags, directiveDef, elementDef, NodeFlags, providerDef, Services, textDef} from '@angular/core/src/view/index'; import {TestBed, withModule} from '@angular/core/testing'; -import {ɵgetDOM as getDOM} from '@angular/common'; import {ivyEnabled} from '@angular/private/testing'; -import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetRootNodes, compViewDef, compViewDefFactory} from './helper'; +import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, compViewDefFactory, createAndGetRootNodes, createRootView} from './helper'; { describe(`View Providers`, () => { - describe('create', () => { let instance: SomeService; class SomeService { - constructor(public dep: any) { instance = this; } + constructor(public dep: any) { + instance = this; + } } - beforeEach(() => { instance = null !; }); + beforeEach(() => { + instance = null!; + }); it('should create providers eagerly', () => { createAndGetRootNodes(compViewDef([ @@ -37,9 +40,11 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR }); it('should create providers lazily', () => { - let lazy: LazyService = undefined !; + let lazy: LazyService = undefined!; class LazyService { - constructor() { lazy = this; } + constructor() { + lazy = this; + } } createAndGetRootNodes(compViewDef([ @@ -66,7 +71,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR }); it('should create factory providers', () => { - function someFactory() { return 'someValue'; } + function someFactory() { + return 'someValue'; + } createAndGetRootNodes(compViewDef([ elementDef(0, NodeFlags.None, null, null, 2, 'span'), @@ -91,7 +98,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR it('should add a DebugContext to errors in provider factories', () => { class SomeService { - constructor() { throw new Error('Test'); } + constructor() { + throw new Error('Test'); + } } let err: any; @@ -148,7 +157,10 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR expect(() => createAndGetRootNodes(compViewDef(rootElNodes))) .toThrowError( - `${ivyEnabled ? 'R3InjectorError' : 'StaticInjectorError'}(DynamicTestModule)[SomeService -> Dep]: \n` + + `${ + ivyEnabled ? + 'R3InjectorError' : + 'StaticInjectorError'}(DynamicTestModule)[SomeService -> Dep]: \n` + ' StaticInjectorError(Platform: core)[SomeService -> Dep]: \n' + ' NullInjectorError: No provider for Dep!'); @@ -162,7 +174,10 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR expect(() => createAndGetRootNodes(compViewDef(nonRootElNodes))) .toThrowError( - `${ivyEnabled ? 'R3InjectorError' : 'StaticInjectorError'}(DynamicTestModule)[SomeService -> Dep]: \n` + + `${ + ivyEnabled ? + 'R3InjectorError' : + 'StaticInjectorError'}(DynamicTestModule)[SomeService -> Dep]: \n` + ' StaticInjectorError(Platform: core)[SomeService -> Dep]: \n' + ' NullInjectorError: No provider for Dep!'); }); @@ -187,7 +202,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR directiveDef(1, NodeFlags.None, null, 0, SomeService, ['nonExistingDep']) ]))) .toThrowError( - `${ivyEnabled ? 'R3InjectorError' : 'StaticInjectorError'}(DynamicTestModule)[nonExistingDep]: \n` + + `${ + ivyEnabled ? 'R3InjectorError' : + 'StaticInjectorError'}(DynamicTestModule)[nonExistingDep]: \n` + ' StaticInjectorError(Platform: core)[nonExistingDep]: \n' + ' NullInjectorError: No provider for nonExistingDep!'); }); @@ -196,8 +213,7 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR createAndGetRootNodes(compViewDef([ elementDef(0, NodeFlags.None, null, null, 1, 'span'), directiveDef( - 1, NodeFlags.None, null, 0, SomeService, - [[DepFlags.Optional, 'nonExistingDep']]) + 1, NodeFlags.None, null, 0, SomeService, [[DepFlags.Optional, 'nonExistingDep']]) ])); expect(instance.dep).toBe(null); }); @@ -295,22 +311,21 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR expect(instance.dep.createElement).toBeTruthy(); }); - }); - }); }); describe('data binding', () => { - ARG_TYPE_VALUES.forEach((inlineDynamic) => { it(`should update via strategy ${inlineDynamic}`, () => { - let instance: SomeService = undefined !; + let instance: SomeService = undefined!; class SomeService { a: any; b: any; - constructor() { instance = this; } + constructor() { + instance = this; + } } const {view, rootNodes} = createAndGetRootNodes(compViewDef( @@ -331,7 +346,6 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR const el = rootNodes[0]; expect(el.getAttribute('ng-reflect-a')).toBe('v1'); }); - }); }); @@ -376,7 +390,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR const {view, rootNodes} = createAndGetRootNodes(compViewDef([ elementDef( 0, NodeFlags.None, null, null, 1, 'span', null, null, null, - () => { throw new Error('Test'); }), + () => { + throw new Error('Test'); + }), directiveDef( 1, NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'}) ])); @@ -397,18 +413,37 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR let log: string[] = []; class SomeService implements OnInit, DoCheck, OnChanges, AfterContentInit, - AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy { + AfterContentChecked, AfterViewInit, AfterViewChecked, + OnDestroy { id: number; a: any; - ngOnInit() { log.push(`${this.id}_ngOnInit`); } - ngDoCheck() { log.push(`${this.id}_ngDoCheck`); } - ngOnChanges() { log.push(`${this.id}_ngOnChanges`); } - ngAfterContentInit() { log.push(`${this.id}_ngAfterContentInit`); } - ngAfterContentChecked() { log.push(`${this.id}_ngAfterContentChecked`); } - ngAfterViewInit() { log.push(`${this.id}_ngAfterViewInit`); } - ngAfterViewChecked() { log.push(`${this.id}_ngAfterViewChecked`); } - ngOnDestroy() { log.push(`${this.id}_ngOnDestroy`); } - constructor() { this.id = instanceCount++; } + ngOnInit() { + log.push(`${this.id}_ngOnInit`); + } + ngDoCheck() { + log.push(`${this.id}_ngDoCheck`); + } + ngOnChanges() { + log.push(`${this.id}_ngOnChanges`); + } + ngAfterContentInit() { + log.push(`${this.id}_ngAfterContentInit`); + } + ngAfterContentChecked() { + log.push(`${this.id}_ngAfterContentChecked`); + } + ngAfterViewInit() { + log.push(`${this.id}_ngAfterViewInit`); + } + ngAfterViewChecked() { + log.push(`${this.id}_ngAfterViewChecked`); + } + ngOnDestroy() { + log.push(`${this.id}_ngOnDestroy`); + } + constructor() { + this.id = instanceCount++; + } } const allFlags = NodeFlags.OnInit | NodeFlags.DoCheck | NodeFlags.OnChanges | @@ -479,7 +514,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR directiveDef( 1, NodeFlags.OnChanges, null, 0, SomeService, [], {a: [0, 'nonMinifiedA']}) ], - (check, view) => { check(view, 1, ArgumentType.Inline, currValue); })); + (check, view) => { + check(view, 1, ArgumentType.Inline, currValue); + })); Services.checkAndUpdateView(view); expect(changesLog).toEqual([new SimpleChange(undefined, 'v1', true)]); @@ -492,7 +529,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR it('should add a DebugContext to errors in provider afterXXX lifecycles', () => { class SomeService implements AfterContentChecked { - ngAfterContentChecked() { throw new Error('Test'); } + ngAfterContentChecked() { + throw new Error('Test'); + } } const {view, rootNodes} = createAndGetRootNodes(compViewDef([ @@ -515,7 +554,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetR it('should add a DebugContext to errors inServices.destroyView', () => { class SomeService implements OnDestroy { - ngOnDestroy() { throw new Error('Test'); } + ngOnDestroy() { + throw new Error('Test'); + } } const {view, rootNodes} = createAndGetRootNodes(compViewDef([ diff --git a/packages/core/test/view/pure_expression_spec.ts b/packages/core/test/view/pure_expression_spec.ts index 03e42cd2ceba7..373522c75228e 100644 --- a/packages/core/test/view/pure_expression_spec.ts +++ b/packages/core/test/view/pure_expression_spec.ts @@ -7,19 +7,17 @@ */ import {PipeTransform} from '@angular/core'; -import {NodeFlags, Services, asProviderData, directiveDef, elementDef, nodeValue, pipeDef, pureArrayDef, pureObjectDef, purePipeDef} from '@angular/core/src/view/index'; +import {asProviderData, directiveDef, elementDef, NodeFlags, nodeValue, pipeDef, pureArrayDef, pureObjectDef, purePipeDef, Services} from '@angular/core/src/view/index'; import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRootNodes} from './helper'; { describe(`View Pure Expressions`, () => { - class Service { data: any; } describe('pure arrays', () => { - ARG_TYPE_VALUES.forEach((inlineDynamic) => { it(`should update via strategy ${inlineDynamic}`, () => { let values: any[]; @@ -52,9 +50,7 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot expect(arr1).not.toBe(arr0); expect(arr1).toEqual([3, 2]); }); - }); - }); describe('pure objects', () => { @@ -90,7 +86,6 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot expect(obj1).not.toBe(obj0); expect(obj1).toEqual({a: 3, b: 2}); }); - }); }); @@ -98,14 +93,16 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot ARG_TYPE_VALUES.forEach((inlineDynamic) => { it(`should update via strategy ${inlineDynamic}`, () => { class SomePipe implements PipeTransform { - transform(v1: any, v2: any) { return [v1 + 10, v2 + 20]; } + transform(v1: any, v2: any) { + return [v1 + 10, v2 + 20]; + } } let values: any[]; const {view, rootNodes} = createAndGetRootNodes(compViewDef( [ - elementDef(0, NodeFlags.None, null !, null !, 3, 'span'), + elementDef(0, NodeFlags.None, null!, null!, 3, 'span'), pipeDef(NodeFlags.None, SomePipe, []), purePipeDef(2, 2), directiveDef(3, NodeFlags.None, null, 0, Service, [], {data: [0, 'data']}), @@ -133,7 +130,6 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot expect(obj1).not.toBe(obj0); expect(obj1).toEqual([13, 22]); }); - }); }); }); diff --git a/packages/core/test/view/query_spec.ts b/packages/core/test/view/query_spec.ts index 1eba518ca9865..a5063d8a74403 100644 --- a/packages/core/test/view/query_spec.ts +++ b/packages/core/test/view/query_spec.ts @@ -8,20 +8,19 @@ import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core'; import {getDebugContext} from '@angular/core/src/errors'; -import {NodeDef, NodeFlags, QueryBindingType, QueryValueType, Services, anchorDef, asElementData, asProviderData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, queryDef} from '@angular/core/src/view/index'; +import {anchorDef, asElementData, asProviderData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, NodeDef, NodeFlags, QueryBindingType, queryDef, QueryValueType, Services} from '@angular/core/src/view/index'; import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedView} from './helper'; { describe(`Query Views`, () => { - const someQueryId = 1; class AService {} class QueryService { // TODO(issue/24571): remove '!'. - a !: QueryList<AService>; + a!: QueryList<AService>; } function contentQueryProviders(checkIndex: number) { @@ -47,7 +46,15 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi ...nodes ])), directiveDef( - checkIndex + 1, NodeFlags.Component, null !, 0, QueryService, [], null !, null !, ), + checkIndex + 1, + NodeFlags.Component, + null!, + 0, + QueryService, + [], + null!, + null!, + ), ]; } @@ -60,7 +67,6 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi } describe('content queries', () => { - it('should query providers on the same element and child elements', () => { const {view} = createAndGetRootNodes(compViewDef([ elementDef(0, NodeFlags.None, null, null, 5, 'div'), @@ -77,12 +83,11 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi const as = qs.a.toArray(); expect(as.length).toBe(2); - expect(as[0]).toBe(asProviderData(view, 3).instance); - expect(as[1]).toBe(asProviderData(view, 5).instance); + expect(as [0]).toBe(asProviderData(view, 3).instance); + expect(as [1]).toBe(asProviderData(view, 5).instance); }); it('should not query providers on sibling or parent elements', () => { - const {view} = createAndGetRootNodes(compViewDef([ elementDef(0, NodeFlags.None, null, null, 6, 'div'), aServiceProvider(1), @@ -249,7 +254,7 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi it('should query all matches', () => { class QueryService { // TODO(issue/24571): remove '!'. - a !: QueryList<AService>; + a!: QueryList<AService>; } const {view} = createAndGetRootNodes(compViewDef([ @@ -275,7 +280,7 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi it('should query the first match', () => { class QueryService { // TODO(issue/24571): remove '!'. - a !: AService; + a!: AService; } const {view} = createAndGetRootNodes(compViewDef([ @@ -299,7 +304,7 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi it('should query ElementRef', () => { class QueryService { // TODO(issue/24571): remove '!'. - a !: ElementRef; + a!: ElementRef; } const {view} = createAndGetRootNodes(compViewDef([ @@ -319,7 +324,7 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi it('should query TemplateRef', () => { class QueryService { // TODO(issue/24571): remove '!'. - a !: TemplateRef<any>; + a!: TemplateRef<any>; } const {view} = createAndGetRootNodes(compViewDef([ @@ -341,7 +346,7 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi it('should query ViewContainerRef', () => { class QueryService { // TODO(issue/24571): remove '!'. - a !: ViewContainerRef; + a!: ViewContainerRef; } const {view} = createAndGetRootNodes(compViewDef([ @@ -363,7 +368,9 @@ import {compViewDef, compViewDefFactory, createAndGetRootNodes, createEmbeddedVi describe('general binding behavior', () => { it('should report debug info on binding errors', () => { class QueryService { - set a(value: any) { throw new Error('Test'); } + set a(value: any) { + throw new Error('Test'); + } } const {view} = createAndGetRootNodes(compViewDef([ diff --git a/packages/core/test/view/services_spec.ts b/packages/core/test/view/services_spec.ts index c97ff6c7b5cad..7ce90cf40bda0 100644 --- a/packages/core/test/view/services_spec.ts +++ b/packages/core/test/view/services_spec.ts @@ -6,13 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {DebugContext, NodeFlags, QueryValueType, Services, asElementData, asTextData, directiveDef, elementDef, textDef} from '@angular/core/src/view/index'; +import {asElementData, asTextData, DebugContext, directiveDef, elementDef, NodeFlags, QueryValueType, Services, textDef} from '@angular/core/src/view/index'; import {compViewDef, createAndGetRootNodes} from './helper'; { describe('View Services', () => { - describe('DebugContext', () => { class AComp {} diff --git a/packages/core/test/view/text_spec.ts b/packages/core/test/view/text_spec.ts index ad169872e10fd..e5e907721df4d 100644 --- a/packages/core/test/view/text_spec.ts +++ b/packages/core/test/view/text_spec.ts @@ -8,13 +8,12 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {getDebugNode} from '@angular/core'; -import {NodeFlags, Services, asTextData, elementDef, textDef} from '@angular/core/src/view/index'; +import {asTextData, elementDef, NodeFlags, Services, textDef} from '@angular/core/src/view/index'; import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRootNodes} from './helper'; { describe(`View Text`, () => { - describe('create', () => { it('should create text nodes without parents', () => { const rootNodes = createAndGetRootNodes(compViewDef([textDef(0, null, ['a'])])).rootNodes; @@ -44,7 +43,7 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot const someContext = {}; const {view, rootNodes} = createAndGetRootNodes(compViewDef([textDef(0, null, ['a'])]), someContext); - expect(getDebugNode(rootNodes[0]) !.nativeNode).toBe(asTextData(view, 0).renderText); + expect(getDebugNode(rootNodes[0])!.nativeNode).toBe(asTextData(view, 0).renderText); }); }); @@ -55,7 +54,7 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot [ textDef(0, null, ['0', '1', '2']), ], - null !, (check, view) => { + null!, (check, view) => { checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['a', 'b']); })); @@ -63,9 +62,7 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot expect(rootNodes[0].textContent).toBe('0a1b2'); }); - }); }); - }); } diff --git a/packages/core/test/view/view_def_spec.ts b/packages/core/test/view/view_def_spec.ts index 99fc389ce9f25..b7c3b7afcf97d 100644 --- a/packages/core/test/view/view_def_spec.ts +++ b/packages/core/test/view/view_def_spec.ts @@ -6,14 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {NodeFlags, QueryValueType, ViewDefinition, ViewFlags, anchorDef, directiveDef, elementDef, textDef, viewDef} from '@angular/core/src/view/index'; +import {anchorDef, directiveDef, elementDef, NodeFlags, QueryValueType, textDef, viewDef, ViewDefinition, ViewFlags} from '@angular/core/src/view/index'; import {filterQueryId} from '@angular/core/src/view/util'; { describe('viewDef', () => { - describe('parent', () => { - function parents(viewDef: ViewDefinition): (number | null)[] { + function parents(viewDef: ViewDefinition): (number|null)[] { return viewDef.nodes.map(node => node.parent ? node.parent.nodeIndex : null); } @@ -54,7 +53,6 @@ import {filterQueryId} from '@angular/core/src/view/util'; }); describe('childFlags', () => { - function childFlags(viewDef: ViewDefinition): number[] { return viewDef.nodes.map(node => node.childFlags); } @@ -122,7 +120,7 @@ import {filterQueryId} from '@angular/core/src/view/util'; const vd = viewDef(ViewFlags.None, [ elementDef(0, NodeFlags.None, null, null, 2, 'span'), elementDef(1, NodeFlags.None, null, null, 1, 'span'), - directiveDef(2, NodeFlags.AfterContentChecked, null !, 0, AService, []), + directiveDef(2, NodeFlags.AfterContentChecked, null!, 0, AService, []), elementDef(3, NodeFlags.None, null, null, 2, 'span'), directiveDef(4, NodeFlags.AfterContentInit, null, 0, AService, []), directiveDef(5, NodeFlags.AfterViewInit, null, 0, AService, []), diff --git a/packages/core/test/zone/ng_zone_spec.ts b/packages/core/test/zone/ng_zone_spec.ts index ba8e79572717e..13d0d5389bf2d 100644 --- a/packages/core/test/zone/ng_zone_spec.ts +++ b/packages/core/test/zone/ng_zone_spec.ts @@ -8,8 +8,9 @@ import {EventEmitter, NgZone} from '@angular/core'; import {async, fakeAsync, flushMicrotasks} from '@angular/core/testing'; -import {AsyncTestCompleter, Log, beforeEach, describe, expect, inject, it, xit} from '@angular/core/testing/src/testing_internal'; +import {AsyncTestCompleter, beforeEach, describe, expect, inject, it, Log, xit} from '@angular/core/testing/src/testing_internal'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; + import {scheduleMicroTask} from '../../src/util/microtask'; import {NoopNgZone} from '../../src/zone/ng_zone'; @@ -89,7 +90,9 @@ function runNgZoneNoLog(fn: () => any) { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { macroTask(() => { let resolve: (result: any) => void; - const promise: Promise<any> = new Promise((res) => { resolve = res; }); + const promise: Promise<any> = new Promise((res) => { + resolve = res; + }); _zone.run(() => { setTimeout(() => { @@ -112,7 +115,9 @@ function runNgZoneNoLog(fn: () => any) { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { macroTask(() => { let resolve: (result: any) => void; - const promise: Promise<any> = new Promise((res) => { resolve = res; }); + const promise: Promise<any> = new Promise((res) => { + resolve = res; + }); _zone.run(() => { scheduleMicroTask(() => { @@ -147,7 +152,9 @@ function runNgZoneNoLog(fn: () => any) { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { macroTask(() => { let resolve: (result: any) => void; - const promise: Promise<any> = new Promise((res) => { resolve = res; }); + const promise: Promise<any> = new Promise((res) => { + resolve = res; + }); _zone.run(() => { setTimeout(() => { @@ -177,7 +184,11 @@ function runNgZoneNoLog(fn: () => any) { it('should run', () => { let runs = false; ngZone.run(() => { - ngZone.runGuarded(() => { ngZone.runOutsideAngular(() => { runs = true; }); }); + ngZone.runGuarded(() => { + ngZone.runOutsideAngular(() => { + runs = true; + }); + }); }); expect(runs).toBe(true); }); @@ -223,33 +234,47 @@ function runNgZoneNoLog(fn: () => any) { function commonTests() { describe('hasPendingMicrotasks', () => { - it('should be false', () => { expect(_zone.hasPendingMicrotasks).toBe(false); }); + it('should be false', () => { + expect(_zone.hasPendingMicrotasks).toBe(false); + }); it('should be true', () => { - runNgZoneNoLog(() => { scheduleMicroTask(() => {}); }); + runNgZoneNoLog(() => { + scheduleMicroTask(() => {}); + }); expect(_zone.hasPendingMicrotasks).toBe(true); }); }); describe('hasPendingTimers', () => { - it('should be false', () => { expect(_zone.hasPendingMacrotasks).toBe(false); }); + it('should be false', () => { + expect(_zone.hasPendingMacrotasks).toBe(false); + }); it('should be true', () => { - runNgZoneNoLog(() => { setTimeout(() => {}, 0); }); + runNgZoneNoLog(() => { + setTimeout(() => {}, 0); + }); expect(_zone.hasPendingMacrotasks).toBe(true); }); }); describe('hasPendingAsyncTasks', () => { - it('should be false', () => { expect(_zone.hasPendingMicrotasks).toBe(false); }); + it('should be false', () => { + expect(_zone.hasPendingMicrotasks).toBe(false); + }); it('should be true when microtask is scheduled', () => { - runNgZoneNoLog(() => { scheduleMicroTask(() => {}); }); + runNgZoneNoLog(() => { + scheduleMicroTask(() => {}); + }); expect(_zone.hasPendingMicrotasks).toBe(true); }); it('should be true when timer is scheduled', () => { - runNgZoneNoLog(() => { setTimeout(() => {}, 0); }); + runNgZoneNoLog(() => { + setTimeout(() => {}, 0); + }); expect(_zone.hasPendingMacrotasks).toBe(true); }); }); @@ -257,23 +282,33 @@ function commonTests() { describe('isInInnerZone', () => { it('should return whether the code executes in the inner zone', () => { expect(NgZone.isInAngularZone()).toEqual(false); - runNgZoneNoLog(() => { expect(NgZone.isInAngularZone()).toEqual(true); }); + runNgZoneNoLog(() => { + expect(NgZone.isInAngularZone()).toEqual(true); + }); }, testTimeout); }); describe('run', () => { it('should return the body return value from run', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { expect(_zone.run(() => 6)).toEqual(6); }); + macroTask(() => { + expect(_zone.run(() => 6)).toEqual(6); + }); - macroTask(() => { async.done(); }); + macroTask(() => { + async.done(); + }); }), testTimeout); it('should return the body return value from runTask', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { expect(_zone.runTask(() => 6)).toEqual(6); }); + macroTask(() => { + expect(_zone.runTask(() => 6)).toEqual(6); + }); - macroTask(() => { async.done(); }); + macroTask(() => { + async.done(); + }); }), testTimeout); it('should call onUnstable and onMicrotaskEmpty', @@ -299,7 +334,9 @@ function commonTests() { _log.add(`onMicrotaskEmpty ${times}`); if (times < 2) { // Scheduling a microtask causes a second digest - runNgZoneNoLog(() => { scheduleMicroTask(() => {}); }); + runNgZoneNoLog(() => { + scheduleMicroTask(() => {}); + }); } } }); @@ -358,7 +395,9 @@ function commonTests() { } }); - macroTask(() => { _zone.run(_log.fn('run')); }); + macroTask(() => { + _zone.run(_log.fn('run')); + }); macroTask(() => { expect(_log.result()).toEqual('onUnstable; run; onMicrotaskEmpty; onStable'); @@ -377,7 +416,9 @@ function commonTests() { next: () => { _log.add('onMyMicrotaskEmpty'); if (turnDone) return; - _zone.run(() => { scheduleMicroTask(() => {}); }); + _zone.run(() => { + scheduleMicroTask(() => {}); + }); turnDone = true; } }); @@ -490,8 +531,12 @@ function commonTests() { runNgZoneNoLog(() => { macroTask(() => { - aPromise = new Promise(res => { aResolve = res; }); - bPromise = new Promise(res => { bResolve = res; }); + aPromise = new Promise(res => { + aResolve = res; + }); + bPromise = new Promise(res => { + bResolve = res; + }); _log.add('run start'); aPromise.then(_log.fn('a then')); @@ -516,7 +561,9 @@ function commonTests() { it('should run a function outside of the angular zone', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { _zone.runOutsideAngular(_log.fn('run')); }); + macroTask(() => { + _zone.runOutsideAngular(_log.fn('run')); + }); macroTask(() => { expect(_log.result()).toEqual('run'); @@ -526,12 +573,14 @@ function commonTests() { it('should call onUnstable and onMicrotaskEmpty when an inner microtask is scheduled from outside angular', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - let resolve: (result: string | null) => void; + let resolve: (result: string|null) => void; let promise: Promise<string|null>; macroTask(() => { NgZone.assertNotInAngularZone(); - promise = new Promise<string|null>(res => { resolve = res; }); + promise = new Promise<string|null>(res => { + resolve = res; + }); }); runNgZoneNoLog(() => { @@ -657,7 +706,9 @@ function commonTests() { _log.add('onUnstable(begin)'); if (!startPromiseRan) { _log.add('onUnstable(schedulePromise)'); - _zone.run(() => { scheduleMicroTask(_log.fn('onUnstable(executePromise)')); }); + _zone.run(() => { + scheduleMicroTask(_log.fn('onUnstable(executePromise)')); + }); startPromiseRan = true; } _log.add('onUnstable(end)'); @@ -669,7 +720,9 @@ function commonTests() { _log.add('onMicrotaskEmpty(begin)'); if (!donePromiseRan) { _log.add('onMicrotaskEmpty(schedulePromise)'); - _zone.run(() => { scheduleMicroTask(_log.fn('onMicrotaskEmpty(executePromise)')); }); + _zone.run(() => { + scheduleMicroTask(_log.fn('onMicrotaskEmpty(executePromise)')); + }); donePromiseRan = true; } _log.add('onMicrotaskEmpty(end)'); @@ -695,24 +748,36 @@ function commonTests() { it('should call onUnstable and onMicrotaskEmpty before and after each turn, respectively', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - let aResolve: (result: string | null) => void; + let aResolve: (result: string|null) => void; let aPromise: Promise<string|null>; - let bResolve: (result: string | null) => void; + let bResolve: (result: string|null) => void; let bPromise: Promise<string|null>; runNgZoneNoLog(() => { macroTask(() => { - aPromise = new Promise<string|null>(res => { aResolve = res; }); - bPromise = new Promise<string|null>(res => { bResolve = res; }); + aPromise = new Promise<string|null>(res => { + aResolve = res; + }); + bPromise = new Promise<string|null>(res => { + bResolve = res; + }); aPromise.then(_log.fn('a then')); bPromise.then(_log.fn('b then')); _log.add('run start'); }); }); - runNgZoneNoLog(() => { macroTask(() => { aResolve(null); }, 10); }); + runNgZoneNoLog(() => { + macroTask(() => { + aResolve(null); + }, 10); + }); - runNgZoneNoLog(() => { macroTask(() => { bResolve(null); }, 20); }); + runNgZoneNoLog(() => { + macroTask(() => { + bResolve(null); + }, 20); + }); macroTask(() => { expect(_log.result()) @@ -754,8 +819,9 @@ function commonTests() { runNgZoneNoLog(() => { macroTask(() => { - _zone.runOutsideAngular( - () => { promise = Promise.resolve(4).then((x) => Promise.resolve(x)); }); + _zone.runOutsideAngular(() => { + promise = Promise.resolve(4).then((x) => Promise.resolve(x)); + }); promise.then(_log.fn('promise then')); _log.add('zone run'); @@ -778,7 +844,9 @@ function commonTests() { macroTask(() => { const exception = new Error('sync'); - _zone.runGuarded(() => { throw exception; }); + _zone.runGuarded(() => { + throw exception; + }); expect(_errors.length).toBe(1); expect(_errors[0]).toBe(exception); @@ -790,7 +858,9 @@ function commonTests() { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { macroTask(() => { const exception = new Error('sync'); - expect(() => _zone.run(() => { throw exception; })).toThrowError('sync'); + expect(() => _zone.run(() => { + throw exception; + })).toThrowError('sync'); expect(_errors.length).toBe(0); async.done(); @@ -801,7 +871,13 @@ function commonTests() { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const exception = new Error('async'); - macroTask(() => { _zone.run(() => { scheduleMicroTask(() => { throw exception; }); }); }); + macroTask(() => { + _zone.run(() => { + scheduleMicroTask(() => { + throw exception; + }); + }); + }); macroTask(() => { expect(_errors.length).toBe(1); @@ -821,7 +897,7 @@ function commonTests() { })); it('should fakeAsync even if the NgZone was created outside.', fakeAsync(() => { - let result: string = null !; + let result: string = null!; // try to escape the current fakeAsync zone by using NgZone which was created outside. ngZone.run(() => { Promise.resolve('works').then((v) => result = v); @@ -834,7 +910,9 @@ function commonTests() { let asyncResult: string; const waitLongerThenTestFrameworkAsyncTimeout = 5; - beforeEach(() => { asyncResult = null !; }); + beforeEach(() => { + asyncResult = null!; + }); it('should async even if the NgZone was created outside.', async(() => { // try to escape the current async zone by using NgZone which was created outside. @@ -845,8 +923,9 @@ function commonTests() { }); })); - afterEach(() => { expect(asyncResult).toEqual('works'); }); - + afterEach(() => { + expect(asyncResult).toEqual('works'); + }); }); }); }); diff --git a/packages/core/testing/src/async_fallback.ts b/packages/core/testing/src/async_fallback.ts index 7fa0ad73a5b10..897da61d79f8d 100644 --- a/packages/core/testing/src/async_fallback.ts +++ b/packages/core/testing/src/async_fallback.ts @@ -41,7 +41,9 @@ export function asyncFallback(fn: Function): (done: any) => any { // if we run beforeEach in @angular/core/testing/testing_internal then we get no done // fake it here and assume sync. done = function() {}; - done.fail = function(e: any) { throw e; }; + done.fail = function(e: any) { + throw e; + }; } runInTestZone(fn, this, done, (err: any) => { if (typeof err === 'string') { @@ -87,7 +89,7 @@ function runInTestZone( // If we do it in ProxyZone then we will get to infinite recursion. const proxyZone = Zone.current.getZoneWith('ProxyZoneSpec'); const previousDelegate = proxyZoneSpec.getDelegate(); - proxyZone !.parent !.run(() => { + proxyZone!.parent!.run(() => { const testZoneSpec: ZoneSpec = new AsyncTestZoneSpec( () => { // Need to restore the original zone. diff --git a/packages/core/testing/src/async_test_completer.ts b/packages/core/testing/src/async_test_completer.ts index cfa94d650e2e7..064e9b6d4d55a 100644 --- a/packages/core/testing/src/async_test_completer.ts +++ b/packages/core/testing/src/async_test_completer.ts @@ -11,16 +11,22 @@ */ export class AsyncTestCompleter { // TODO(issue/24571): remove '!'. - private _resolve !: (result: any) => void; + private _resolve!: (result: any) => void; // TODO(issue/24571): remove '!'. - private _reject !: (err: any) => void; + private _reject!: (err: any) => void; private _promise: Promise<any> = new Promise((res, rej) => { this._resolve = res; this._reject = rej; }); - done(value?: any) { this._resolve(value); } + done(value?: any) { + this._resolve(value); + } - fail(error?: any, stackTrace?: string) { this._reject(error); } + fail(error?: any, stackTrace?: string) { + this._reject(error); + } - get promise(): Promise<any> { return this._promise; } + get promise(): Promise<any> { + return this._promise; + } } diff --git a/packages/core/testing/src/component_fixture.ts b/packages/core/testing/src/component_fixture.ts index bb15761f22245..38e2f102db8f8 100644 --- a/packages/core/testing/src/component_fixture.ts +++ b/packages/core/testing/src/component_fixture.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectorRef, ComponentRef, DebugElement, ElementRef, NgZone, RendererFactory2, getDebugNode} from '@angular/core'; +import {ChangeDetectorRef, ComponentRef, DebugElement, ElementRef, getDebugNode, NgZone, RendererFactory2} from '@angular/core'; /** @@ -65,8 +65,11 @@ export class ComponentFixture<T> { // Create subscriptions outside the NgZone so that the callbacks run oustide // of NgZone. ngZone.runOutsideAngular(() => { - this._onUnstableSubscription = - ngZone.onUnstable.subscribe({next: () => { this._isStable = false; }}); + this._onUnstableSubscription = ngZone.onUnstable.subscribe({ + next: () => { + this._isStable = false; + } + }); this._onMicrotaskEmptySubscription = ngZone.onMicrotaskEmpty.subscribe({ next: () => { if (this._autoDetect) { @@ -87,7 +90,7 @@ export class ComponentFixture<T> { scheduleMicroTask(() => { if (!ngZone.hasPendingMacrotasks) { if (this._promise !== null) { - this._resolve !(true); + this._resolve!(true); this._resolve = null; this._promise = null; } @@ -97,8 +100,11 @@ export class ComponentFixture<T> { } }); - this._onErrorSubscription = - ngZone.onError.subscribe({next: (error: any) => { throw error; }}); + this._onErrorSubscription = ngZone.onError.subscribe({ + next: (error: any) => { + throw error; + } + }); }); } } @@ -117,7 +123,9 @@ export class ComponentFixture<T> { if (this.ngZone != null) { // Run the change detection inside the NgZone so that any async tasks as part of the change // detection are captured by the zone and can be waited for in isStable. - this.ngZone.run(() => { this._tick(checkNoChanges); }); + this.ngZone.run(() => { + this._tick(checkNoChanges); + }); } else { // Running without zone. Just do the change detection. this._tick(checkNoChanges); @@ -127,7 +135,9 @@ export class ComponentFixture<T> { /** * Do a change detection run to make sure there were no changes. */ - checkNoChanges(): void { this.changeDetectorRef.checkNoChanges(); } + checkNoChanges(): void { + this.changeDetectorRef.checkNoChanges(); + } /** * Set whether the fixture should autodetect changes. @@ -146,7 +156,9 @@ export class ComponentFixture<T> { * Return whether the fixture is currently stable or has async tasks that have not been completed * yet. */ - isStable(): boolean { return this._isStable && !this.ngZone !.hasPendingMacrotasks; } + isStable(): boolean { + return this._isStable && !this.ngZone!.hasPendingMacrotasks; + } /** * Get a promise that resolves when the fixture is stable. @@ -160,7 +172,9 @@ export class ComponentFixture<T> { } else if (this._promise !== null) { return this._promise; } else { - this._promise = new Promise(res => { this._resolve = res; }); + this._promise = new Promise(res => { + this._resolve = res; + }); return this._promise; } } @@ -174,8 +188,8 @@ export class ComponentFixture<T> { } /** - * Get a promise that resolves when the ui state is stable following animations. - */ + * Get a promise that resolves when the ui state is stable following animations. + */ whenRenderingDone(): Promise<any> { const renderer = this._getRenderer(); if (renderer && renderer.whenRenderingDone) { diff --git a/packages/core/testing/src/logger.ts b/packages/core/testing/src/logger.ts index a0d3b9c58a5dd..38c074b8d0037 100644 --- a/packages/core/testing/src/logger.ts +++ b/packages/core/testing/src/logger.ts @@ -12,9 +12,13 @@ import {Injectable} from '@angular/core'; export class Log { logItems: any[]; - constructor() { this.logItems = []; } + constructor() { + this.logItems = []; + } - add(value: any /** TODO #9100 */): void { this.logItems.push(value); } + add(value: any /** TODO #9100 */): void { + this.logItems.push(value); + } fn(value: any /** TODO #9100 */) { return (a1: any = null, a2: any = null, a3: any = null, a4: any = null, a5: any = null) => { @@ -22,7 +26,11 @@ export class Log { }; } - clear(): void { this.logItems = []; } + clear(): void { + this.logItems = []; + } - result(): string { return this.logItems.join('; '); } + result(): string { + return this.logItems.join('; '); + } } diff --git a/packages/core/testing/src/metadata_overrider.ts b/packages/core/testing/src/metadata_overrider.ts index 4a5bf7846719d..81bfbf806472b 100644 --- a/packages/core/testing/src/metadata_overrider.ts +++ b/packages/core/testing/src/metadata_overrider.ts @@ -22,7 +22,7 @@ export class MetadataOverrider { * based on an old instance and overrides. */ overrideMetadata<C extends T, T>( - metadataClass: {new (options: T): C;}, oldMetadata: C, override: MetadataOverride<T>): C { + metadataClass: {new(options: T): C;}, oldMetadata: C, override: MetadataOverride<T>): C { const props: StringMap = {}; if (oldMetadata) { _valueProps(oldMetadata).forEach((prop) => props[prop] = (<any>oldMetadata)[prop]); @@ -49,8 +49,9 @@ function removeMetadata(metadata: StringMap, remove: any, references: Map<any, s for (const prop in remove) { const removeValue = remove[prop]; if (Array.isArray(removeValue)) { - removeValue.forEach( - (value: any) => { removeObjects.add(_propHashKey(prop, value, references)); }); + removeValue.forEach((value: any) => { + removeObjects.add(_propHashKey(prop, value, references)); + }); } else { removeObjects.add(_propHashKey(prop, removeValue, references)); } diff --git a/packages/core/testing/src/ng_zone_mock.ts b/packages/core/testing/src/ng_zone_mock.ts index cad1508c9bf5a..65373def5aeef 100644 --- a/packages/core/testing/src/ng_zone_mock.ts +++ b/packages/core/testing/src/ng_zone_mock.ts @@ -16,11 +16,19 @@ import {EventEmitter, Injectable, NgZone} from '@angular/core'; export class MockNgZone extends NgZone { onStable: EventEmitter<any> = new EventEmitter(false); - constructor() { super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: false}); } + constructor() { + super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: false}); + } - run(fn: Function): any { return fn(); } + run(fn: Function): any { + return fn(); + } - runOutsideAngular(fn: Function): any { return fn(); } + runOutsideAngular(fn: Function): any { + return fn(); + } - simulateZoneExit(): void { this.onStable.emit(null); } + simulateZoneExit(): void { + this.onStable.emit(null); + } } diff --git a/packages/core/testing/src/r3_test_bed.ts b/packages/core/testing/src/r3_test_bed.ts index 6a749280fec02..178b3ba5e40fd 100644 --- a/packages/core/testing/src/r3_test_bed.ts +++ b/packages/core/testing/src/r3_test_bed.ts @@ -9,7 +9,8 @@ // The formatter and CI disagree on how this import statement should be formatted. Both try to keep // it on one line, too, which has gotten very hard to read & manage. So disable the formatter for // this statement only. -// clang-format off + +/* clang-format off */ import { AbstractType, Component, @@ -22,19 +23,20 @@ import { Pipe, PlatformRef, Type, + ɵflushModuleScopingQueueAsMuchAsPossible as flushModuleScopingQueueAsMuchAsPossible, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, - ɵflushModuleScopingQueueAsMuchAsPossible as flushModuleScopingQueueAsMuchAsPossible, ɵresetCompiledComponents as resetCompiledComponents, ɵstringify as stringify, } from '@angular/core'; -// clang-format on + +/* clang-format on */ import {ComponentFixture} from './component_fixture'; import {MetadataOverride} from './metadata_override'; +import {R3TestBedCompiler} from './r3_test_bed_compiler'; import {TestBed} from './test_bed'; import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBedStatic, TestComponentRenderer, TestModuleMetadata} from './test_bed_common'; -import {R3TestBedCompiler} from './r3_test_bed_compiler'; let _nextRootElementId = 0; @@ -75,7 +77,9 @@ export class TestBedRender3 implements TestBed { * * @publicApi */ - static resetTestEnvironment(): void { _getTestBedRender3().resetTestEnvironment(); } + static resetTestEnvironment(): void { + _getTestBedRender3().resetTestEnvironment(); + } static configureCompiler(config: {providers?: any[]; useJit?: boolean;}): TestBedStatic { _getTestBedRender3().configureCompiler(config); @@ -96,7 +100,9 @@ export class TestBedRender3 implements TestBed { * It is necessary to call this function * as fetching urls is asynchronous. */ - static compileComponents(): Promise<any> { return _getTestBedRender3().compileComponents(); } + static compileComponents(): Promise<any> { + return _getTestBedRender3().compileComponents(); + } static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic { _getTestBedRender3().overrideModule(ngModule, override); @@ -121,7 +127,7 @@ export class TestBedRender3 implements TestBed { } static overrideTemplate(component: Type<any>, template: string): TestBedStatic { - _getTestBedRender3().overrideComponent(component, {set: {template, templateUrl: null !}}); + _getTestBedRender3().overrideComponent(component, {set: {template, templateUrl: null!}}); return TestBedRender3 as any as TestBedStatic; } @@ -183,8 +189,8 @@ export class TestBedRender3 implements TestBed { // Properties - platform: PlatformRef = null !; - ngModule: Type<any>|Type<any>[] = null !; + platform: PlatformRef = null!; + ngModule: Type<any>|Type<any>[] = null!; private _compiler: R3TestBedCompiler|null = null; private _testModuleRef: NgModuleRef<any>|null = null; @@ -223,8 +229,8 @@ export class TestBedRender3 implements TestBed { resetTestEnvironment(): void { this.resetTestingModule(); this._compiler = null; - this.platform = null !; - this.ngModule = null !; + this.platform = null!; + this.ngModule = null!; } resetTestingModule(): void { @@ -253,7 +259,9 @@ export class TestBedRender3 implements TestBed { this.compiler.configureTestingModule(moduleDef); } - compileComponents(): Promise<any> { return this.compiler.compileComponents(); } + compileComponents(): Promise<any> { + return this.compiler.compileComponents(); + } inject<T>( token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue?: T, flags?: InjectFlags): T; diff --git a/packages/core/testing/src/r3_test_bed_compiler.ts b/packages/core/testing/src/r3_test_bed_compiler.ts index 5a46e92403f9f..5fd7623f86f24 100644 --- a/packages/core/testing/src/r3_test_bed_compiler.ts +++ b/packages/core/testing/src/r3_test_bed_compiler.ts @@ -7,7 +7,7 @@ */ import {ResourceLoader} from '@angular/compiler'; -import {ApplicationInitStatus, COMPILER_OPTIONS, Compiler, Component, Directive, Injector, InjectorType, LOCALE_ID, ModuleWithComponentFactories, ModuleWithProviders, NgModule, NgModuleFactory, NgZone, Pipe, PlatformRef, Provider, Type, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵDirectiveDef as DirectiveDef, ɵNG_COMP_DEF as NG_COMP_DEF, ɵNG_DIR_DEF as NG_DIR_DEF, ɵNG_INJ_DEF as NG_INJ_DEF, ɵNG_MOD_DEF as NG_MOD_DEF, ɵNG_PIPE_DEF as NG_PIPE_DEF, ɵNgModuleFactory as R3NgModuleFactory, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵNgModuleType as NgModuleType, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵgetInjectableDef as getInjectableDef, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵsetLocaleId as setLocaleId, ɵtransitiveScopesFor as transitiveScopesFor, ɵɵInjectableDef as InjectableDef} from '@angular/core'; +import {ApplicationInitStatus, Compiler, COMPILER_OPTIONS, Component, Directive, Injector, InjectorType, LOCALE_ID, ModuleWithComponentFactories, ModuleWithProviders, NgModule, NgModuleFactory, NgZone, Pipe, PlatformRef, Provider, Type, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵDirectiveDef as DirectiveDef, ɵgetInjectableDef as getInjectableDef, ɵNG_COMP_DEF as NG_COMP_DEF, ɵNG_DIR_DEF as NG_DIR_DEF, ɵNG_INJ_DEF as NG_INJ_DEF, ɵNG_MOD_DEF as NG_MOD_DEF, ɵNG_PIPE_DEF as NG_PIPE_DEF, ɵNgModuleFactory as R3NgModuleFactory, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵNgModuleType as NgModuleType, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, ɵsetLocaleId as setLocaleId, ɵtransitiveScopesFor as transitiveScopesFor, ɵɵInjectableDef as InjectableDef} from '@angular/core'; import {clearResolutionOfComponentResourcesQueue, isComponentDefPendingResolution, resolveComponentResources, restoreComponentResolutionQueue} from '../../src/metadata/resource_loading'; @@ -57,6 +57,9 @@ export class R3TestBedCompiler { private seenComponents = new Set<Type<any>>(); private seenDirectives = new Set<Type<any>>(); + // Keep track of overridden modules, so that we can collect all affected ones in the module tree. + private overriddenModules = new Set<NgModuleType<any>>(); + // Store resolved styles for Components that have template overrides present and `styleUrls` // defined at the same time. private existingComponentStyles = new Map<Type<any>, string[]>(); @@ -88,7 +91,6 @@ export class R3TestBedCompiler { private testModuleType: NgModuleType<any>; private testModuleRef: NgModuleRef<any>|null = null; - private hasModuleOverrides: boolean = false; constructor(private platform: PlatformRef, private additionalModuleTypes: Type<any>|Type<any>[]) { class DynamicTestModule {} @@ -123,7 +125,7 @@ export class R3TestBedCompiler { } overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void { - this.hasModuleOverrides = true; + this.overriddenModules.add(ngModule as NgModuleType<any>); // Compile the module right away. this.resolvers.module.addOverride(ngModule, override); @@ -194,7 +196,7 @@ export class R3TestBedCompiler { overrideTemplateUsingTestingModule(type: Type<any>, template: string): void { const def = (type as any)[NG_COMP_DEF]; const hasStyleUrls = (): boolean => { - const metadata = this.resolvers.component.resolve(type) !as Component; + const metadata = this.resolvers.component.resolve(type)! as Component; return !!metadata.styleUrls && metadata.styleUrls.length > 0; }; const overrideStyleUrls = !!def && !isComponentDefPendingResolution(type) && hasStyleUrls(); @@ -257,14 +259,16 @@ export class R3TestBedCompiler { const parentInjector = this.platform.injector; this.testModuleRef = new NgModuleRef(this.testModuleType, parentInjector); - // Set the locale ID, it can be overridden for the tests - const localeId = this.testModuleRef.injector.get(LOCALE_ID, DEFAULT_LOCALE_ID); - setLocaleId(localeId); - // ApplicationInitStatus.runInitializers() is marked @internal to core. // Cast it to any before accessing it. (this.testModuleRef.injector.get(ApplicationInitStatus) as any).runInitializers(); + // Set locale ID after running app initializers, since locale information might be updated while + // running initializers. This is also consistent with the execution order while bootstrapping an + // app (see `packages/core/src/application_ref.ts` file). + const localeId = this.testModuleRef.injector.get(LOCALE_ID, DEFAULT_LOCALE_ID); + setLocaleId(localeId); + return this.testModuleRef; } @@ -293,7 +297,9 @@ export class R3TestBedCompiler { /** * @internal */ - _getModuleResolver(): Resolver<NgModule> { return this.resolvers.module; } + _getModuleResolver(): Resolver<NgModule> { + return this.resolvers.module; + } /** * @internal @@ -301,7 +307,7 @@ export class R3TestBedCompiler { _getComponentFactories(moduleType: NgModuleType): ComponentFactory<any>[] { return maybeUnwrapFn(moduleType.ɵmod.declarations).reduce((factories, declaration) => { const componentDef = (declaration as any).ɵcmp; - componentDef && factories.push(new ComponentFactory(componentDef, this.testModuleRef !)); + componentDef && factories.push(new ComponentFactory(componentDef, this.testModuleRef!)); return factories; }, [] as ComponentFactory<any>[]); } @@ -344,24 +350,29 @@ export class R3TestBedCompiler { } private applyTransitiveScopes(): void { + if (this.overriddenModules.size > 0) { + // Module overrides (via `TestBed.overrideModule`) might affect scopes that were previously + // calculated and stored in `transitiveCompileScopes`. If module overrides are present, + // collect all affected modules and reset scopes to force their re-calculatation. + const testingModuleDef = (this.testModuleType as any)[NG_MOD_DEF]; + const affectedModules = this.collectModulesAffectedByOverrides(testingModuleDef.imports); + if (affectedModules.size > 0) { + affectedModules.forEach(moduleType => { + this.storeFieldOfDefOnType(moduleType as any, NG_MOD_DEF, 'transitiveCompileScopes'); + (moduleType as any)[NG_MOD_DEF].transitiveCompileScopes = null; + }); + } + } + const moduleToScope = new Map<Type<any>|TestingModuleOverride, NgModuleTransitiveScopes>(); const getScopeOfModule = - (moduleType: Type<any>| TestingModuleOverride): NgModuleTransitiveScopes => { + (moduleType: Type<any>|TestingModuleOverride): NgModuleTransitiveScopes => { if (!moduleToScope.has(moduleType)) { const isTestingModule = isTestingModuleOverride(moduleType); const realType = isTestingModule ? this.testModuleType : moduleType as Type<any>; - // Module overrides (via TestBed.overrideModule) might affect scopes that were - // previously calculated and stored in `transitiveCompileScopes`. If module overrides - // are present, always re-calculate transitive scopes to have the most up-to-date - // information available. The `moduleToScope` map avoids repeated re-calculation of - // scopes for the same module. - if (!isTestingModule && this.hasModuleOverrides) { - this.storeFieldOfDefOnType(moduleType as any, NG_MOD_DEF, 'transitiveCompileScopes'); - (moduleType as any)[NG_MOD_DEF].transitiveCompileScopes = null; - } moduleToScope.set(moduleType, transitiveScopesFor(realType)); } - return moduleToScope.get(moduleType) !; + return moduleToScope.get(moduleType)!; }; this.componentToModuleScope.forEach((moduleType, componentType) => { @@ -377,7 +388,7 @@ export class R3TestBedCompiler { private applyProviderOverrides(): void { const maybeApplyOverrides = (field: string) => (type: Type<any>) => { const resolver = field === NG_COMP_DEF ? this.resolvers.component : this.resolvers.directive; - const metadata = resolver.resolve(type) !; + const metadata = resolver.resolve(type)!; if (this.hasProviderOverrides(metadata.providers)) { this.patchDefWithProviderOverrides(type, field); } @@ -528,6 +539,46 @@ export class R3TestBedCompiler { queueTypesFromModulesArrayRecur(arr); } + // When module overrides (via `TestBed.overrideModule`) are present, it might affect all modules + // that import (even transitively) an overridden one. For all affected modules we need to + // recalculate their scopes for a given test run and restore original scopes at the end. The goal + // of this function is to collect all affected modules in a set for further processing. Example: + // if we have the following module hierarchy: A -> B -> C (where `->` means `imports`) and module + // `C` is overridden, we consider `A` and `B` as affected, since their scopes might become + // invalidated with the override. + private collectModulesAffectedByOverrides(arr: any[]): Set<NgModuleType<any>> { + const seenModules = new Set<NgModuleType<any>>(); + const affectedModules = new Set<NgModuleType<any>>(); + const calcAffectedModulesRecur = (arr: any[], path: NgModuleType<any>[]): void => { + for (const value of arr) { + if (Array.isArray(value)) { + // If the value is an array, just flatten it (by invoking this function recursively), + // keeping "path" the same. + calcAffectedModulesRecur(value, path); + } else if (hasNgModuleDef(value)) { + if (seenModules.has(value)) { + // If we've seen this module before and it's included into "affected modules" list, mark + // the whole path that leads to that module as affected, but do not descend into its + // imports, since we already examined them before. + if (affectedModules.has(value)) { + path.forEach(item => affectedModules.add(item)); + } + continue; + } + seenModules.add(value); + if (this.overriddenModules.has(value)) { + path.forEach(item => affectedModules.add(item)); + } + // Examine module imports recursively to look for overridden modules. + const moduleDef = (value as any)[NG_MOD_DEF]; + calcAffectedModulesRecur(maybeUnwrapFn(moduleDef.imports), path.concat(value)); + } + } + }; + calcAffectedModulesRecur(arr, []); + return affectedModules; + } + private maybeStoreNgDef(prop: string, type: Type<any>) { if (!this.initialNgDefs.has(type)) { const currentDef = Object.getOwnPropertyDescriptor(type, prop); @@ -551,7 +602,7 @@ export class R3TestBedCompiler { this.originalComponentResolutionQueue = new Map(); } clearResolutionOfComponentResourcesQueue().forEach( - (value, key) => this.originalComponentResolutionQueue !.set(key, value)); + (value, key) => this.originalComponentResolutionQueue!.set(key, value)); } /* @@ -573,21 +624,20 @@ export class R3TestBedCompiler { op.object[op.fieldName] = op.originalValue; }); // Restore initial component/directive/pipe defs - this.initialNgDefs.forEach( - (value: [string, PropertyDescriptor | undefined], type: Type<any>) => { - const [prop, descriptor] = value; - if (!descriptor) { - // Delete operations are generally undesirable since they have performance implications - // on objects they were applied to. In this particular case, situations where this code - // is invoked should be quite rare to cause any noticeable impact, since it's applied - // only to some test cases (for example when class with no annotations extends some - // @Component) when we need to clear 'ɵcmp' field on a given class to restore - // its original state (before applying overrides and running tests). - delete (type as any)[prop]; - } else { - Object.defineProperty(type, prop, descriptor); - } - }); + this.initialNgDefs.forEach((value: [string, PropertyDescriptor|undefined], type: Type<any>) => { + const [prop, descriptor] = value; + if (!descriptor) { + // Delete operations are generally undesirable since they have performance implications + // on objects they were applied to. In this particular case, situations where this code + // is invoked should be quite rare to cause any noticeable impact, since it's applied + // only to some test cases (for example when class with no annotations extends some + // @Component) when we need to clear 'ɵcmp' field on a given class to restore + // its original state (before applying overrides and running tests). + delete (type as any)[prop]; + } else { + Object.defineProperty(type, prop, descriptor); + } + }); this.initialNgDefs.clear(); this.moduleProvidersOverridden.clear(); this.restoreComponentResolutionQueue(); @@ -724,7 +774,7 @@ function hasNgModuleDef<T>(value: Type<T>): value is NgModuleType<T> { return value.hasOwnProperty('ɵmod'); } -function maybeUnwrapFn<T>(maybeFn: (() => T) | T): T { +function maybeUnwrapFn<T>(maybeFn: (() => T)|T): T { return maybeFn instanceof Function ? maybeFn() : maybeFn; } diff --git a/packages/core/testing/src/resolvers.ts b/packages/core/testing/src/resolvers.ts index 6bf7456f92391..fa2f7b35317bc 100644 --- a/packages/core/testing/src/resolvers.ts +++ b/packages/core/testing/src/resolvers.ts @@ -40,7 +40,9 @@ abstract class OverrideResolver<T> implements Resolver<T> { setOverrides(overrides: Array<[Type<any>, MetadataOverride<T>]>) { this.overrides.clear(); - overrides.forEach(([type, override]) => { this.addOverride(type, override); }); + overrides.forEach(([type, override]) => { + this.addOverride(type, override); + }); } getAnnotation(type: Type<any>): T|null { @@ -71,7 +73,7 @@ abstract class OverrideResolver<T> implements Resolver<T> { if (overrides) { const overrider = new MetadataOverrider(); overrides.forEach(override => { - resolved = overrider.overrideMetadata(this.type, resolved !, override); + resolved = overrider.overrideMetadata(this.type, resolved!, override); }); } } @@ -84,17 +86,25 @@ abstract class OverrideResolver<T> implements Resolver<T> { export class DirectiveResolver extends OverrideResolver<Directive> { - get type() { return Directive; } + get type() { + return Directive; + } } export class ComponentResolver extends OverrideResolver<Component> { - get type() { return Component; } + get type() { + return Component; + } } export class PipeResolver extends OverrideResolver<Pipe> { - get type() { return Pipe; } + get type() { + return Pipe; + } } export class NgModuleResolver extends OverrideResolver<NgModule> { - get type() { return NgModule; } + get type() { + return NgModule; + } } diff --git a/packages/core/testing/src/styling.ts b/packages/core/testing/src/styling.ts index dcb4ba904fcf0..d843d6d1aae25 100644 --- a/packages/core/testing/src/styling.ts +++ b/packages/core/testing/src/styling.ts @@ -7,11 +7,11 @@ */ /** - * Returns element classes in form of a stable (sorted) string. - * - * @param element HTML Element. - * @returns Returns element classes in form of a stable (sorted) string. - */ + * Returns element classes in form of a stable (sorted) string. + * + * @param element HTML Element. + * @returns Returns element classes in form of a stable (sorted) string. + */ export function getSortedClassName(element: Element): string { const names: string[] = Object.keys(getElementClasses(element)); names.sort(); diff --git a/packages/core/testing/src/test_bed.ts b/packages/core/testing/src/test_bed.ts index 3f260aee63b89..38ebcfe41d775 100644 --- a/packages/core/testing/src/test_bed.ts +++ b/packages/core/testing/src/test_bed.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {AbstractType, ApplicationInitStatus, CompilerOptions, Component, Directive, InjectFlags, InjectionToken, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵDepFlags as DepFlags, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetInjectableDef as getInjectableDef, ɵivyEnabled as ivyEnabled, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify, ɵɵInjectableDef} from '@angular/core'; +import {AbstractType, ApplicationInitStatus, CompilerOptions, Component, Directive, InjectFlags, InjectionToken, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵclearOverrides as clearOverrides, ɵDepFlags as DepFlags, ɵgetInjectableDef as getInjectableDef, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵivyEnabled as ivyEnabled, ɵNodeFlags as NodeFlags, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify, ɵɵInjectableDef} from '@angular/core'; import {AsyncTestCompleter} from './async_test_completer'; import {ComponentFixture} from './component_fixture'; import {MetadataOverride} from './metadata_override'; -import {TestBedRender3, _getTestBedRender3} from './r3_test_bed'; +import {_getTestBedRender3, TestBedRender3} from './r3_test_bed'; import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBedStatic, TestComponentRenderer, TestModuleMetadata} from './test_bed_common'; import {TestingCompiler, TestingCompilerFactory} from './test_compiler'; @@ -123,7 +123,9 @@ export class TestBedViewEngine implements TestBed { /** * Reset the providers for the test injector. */ - static resetTestEnvironment(): void { _getTestBedViewEngine().resetTestEnvironment(); } + static resetTestEnvironment(): void { + _getTestBedViewEngine().resetTestEnvironment(); + } static resetTestingModule(): TestBedStatic { _getTestBedViewEngine().resetTestingModule(); @@ -153,7 +155,9 @@ export class TestBedViewEngine implements TestBed { * It is necessary to call this function * as fetching urls is asynchronous. */ - static compileComponents(): Promise<any> { return getTestBed().compileComponents(); } + static compileComponents(): Promise<any> { + return getTestBed().compileComponents(); + } static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic { _getTestBedViewEngine().overrideModule(ngModule, override); @@ -178,7 +182,7 @@ export class TestBedViewEngine implements TestBed { } static overrideTemplate(component: Type<any>, template: string): TestBedStatic { - _getTestBedViewEngine().overrideComponent(component, {set: {template, templateUrl: null !}}); + _getTestBedViewEngine().overrideComponent(component, {set: {template, templateUrl: null!}}); return TestBedViewEngine as any as TestBedStatic; } @@ -243,9 +247,9 @@ export class TestBedViewEngine implements TestBed { private _instantiated: boolean = false; - private _compiler: TestingCompiler = null !; - private _moduleRef: NgModuleRef<any> = null !; - private _moduleFactory: NgModuleFactory<any> = null !; + private _compiler: TestingCompiler = null!; + private _moduleRef: NgModuleRef<any> = null!; + private _moduleFactory: NgModuleFactory<any> = null!; private _compilerOptions: CompilerOptions[] = []; @@ -267,9 +271,9 @@ export class TestBedViewEngine implements TestBed { private _isRoot: boolean = true; private _rootProviderOverrides: Provider[] = []; - platform: PlatformRef = null !; + platform: PlatformRef = null!; - ngModule: Type<any>|Type<any>[] = null !; + ngModule: Type<any>|Type<any>[] = null!; /** * Initialize the environment for testing with a compiler factory, a PlatformRef, and an @@ -299,8 +303,8 @@ export class TestBedViewEngine implements TestBed { */ resetTestEnvironment(): void { this.resetTestingModule(); - this.platform = null !; - this.ngModule = null !; + this.platform = null!; + this.ngModule = null!; this._testEnvAotSummaries = () => []; } @@ -308,7 +312,7 @@ export class TestBedViewEngine implements TestBed { clearOverrides(); this._aotSummaries = []; this._templateOverrides = []; - this._compiler = null !; + this._compiler = null!; this._moduleOverrides = []; this._componentOverrides = []; this._directiveOverrides = []; @@ -317,8 +321,8 @@ export class TestBedViewEngine implements TestBed { this._isRoot = true; this._rootProviderOverrides = []; - this._moduleRef = null !; - this._moduleFactory = null !; + this._moduleRef = null!; + this._moduleFactory = null!; this._compilerOptions = []; this._providers = []; this._declarations = []; @@ -387,7 +391,9 @@ export class TestBedViewEngine implements TestBed { const errorCompType = this._compiler.getComponentFromError(e); if (errorCompType) { throw new Error( - `This test module uses the component ${stringify(errorCompType)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` + + `This test module uses the component ${ + stringify( + errorCompType)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` + `Please call "TestBed.compileComponents" before your test.`); } else { throw e; @@ -593,8 +599,8 @@ export class TestBedViewEngine implements TestBed { const componentFactory = this._compiler.getComponentFactory(component); if (!componentFactory) { - throw new Error( - `Cannot create the component ${stringify(component)} as it was not imported into the testing module!`); + throw new Error(`Cannot create the component ${ + stringify(component)} as it was not imported into the testing module!`); } // TODO: Don't cast as `InjectionToken<boolean>`, declared type is boolean[] @@ -688,7 +694,9 @@ export function inject(tokens: any[], fn: Function): () => any { }; } else { // Not using an arrow function to preserve context passed from call site - return function(this: unknown) { return testBed.execute(tokens, fn, this); }; + return function(this: unknown) { + return testBed.execute(tokens, fn, this); + }; } } @@ -720,7 +728,7 @@ export class InjectSetupWrapper { */ export function withModule(moduleDef: TestModuleMetadata): InjectSetupWrapper; export function withModule(moduleDef: TestModuleMetadata, fn: Function): () => any; -export function withModule(moduleDef: TestModuleMetadata, fn?: Function | null): (() => any)| +export function withModule(moduleDef: TestModuleMetadata, fn?: Function|null): (() => any)| InjectSetupWrapper { if (fn) { // Not using an arrow function to preserve context passed from call site diff --git a/packages/core/testing/src/test_bed_common.ts b/packages/core/testing/src/test_bed_common.ts index 071179a1b0e9d..93c2123bc118f 100644 --- a/packages/core/testing/src/test_bed_common.ts +++ b/packages/core/testing/src/test_bed_common.ts @@ -49,7 +49,7 @@ export type TestModuleMetadata = { * @publicApi */ export interface TestBedStatic { - new (...args: any[]): TestBed; + new(...args: any[]): TestBed; initTestEnvironment( ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed; diff --git a/packages/core/testing/src/test_compiler.ts b/packages/core/testing/src/test_compiler.ts index 518d7484e06f7..4b7337eb3793e 100644 --- a/packages/core/testing/src/test_compiler.ts +++ b/packages/core/testing/src/test_compiler.ts @@ -21,7 +21,9 @@ function unimplemented(): any { */ @Injectable() export class TestingCompiler extends Compiler { - get injector(): Injector { throw unimplemented(); } + get injector(): Injector { + throw unimplemented(); + } overrideModule(module: Type<any>, overrides: MetadataOverride<NgModule>): void { throw unimplemented(); } @@ -38,20 +40,26 @@ export class TestingCompiler extends Compiler { * Allows to pass the compile summary from AOT compilation to the JIT compiler, * so that it can use the code generated by AOT. */ - loadAotSummaries(summaries: () => any[]) { throw unimplemented(); } + loadAotSummaries(summaries: () => any[]) { + throw unimplemented(); + } /** * Gets the component factory for the given component. * This assumes that the component has been compiled before calling this call using * `compileModuleAndAllComponents*`. */ - getComponentFactory<T>(component: Type<T>): ComponentFactory<T> { throw unimplemented(); } + getComponentFactory<T>(component: Type<T>): ComponentFactory<T> { + throw unimplemented(); + } /** * Returns the component type that is stored in the given error. * This can be used for errors created by compileModule... */ - getComponentFromError(error: Error): Type<any>|null { throw unimplemented(); } + getComponentFromError(error: Error): Type<any>|null { + throw unimplemented(); + } } /** diff --git a/packages/core/testing/src/testing_internal.ts b/packages/core/testing/src/testing_internal.ts index 00ce5136ab06b..8e28a62432b44 100644 --- a/packages/core/testing/src/testing_internal.ts +++ b/packages/core/testing/src/testing_internal.ts @@ -39,7 +39,7 @@ const globalTimeOut = jasmine.DEFAULT_TIMEOUT_INTERVAL; const testBed = getTestBed(); -export type TestFn = ((done: DoneFn) => any) | (() => any); +export type TestFn = ((done: DoneFn) => any)|(() => any); /** * Mechanism to run `beforeEach()` functions of Angular tests. @@ -51,20 +51,26 @@ class BeforeEachRunner { constructor(private _parent: BeforeEachRunner) {} - beforeEach(fn: Function): void { this._fns.push(fn); } + beforeEach(fn: Function): void { + this._fns.push(fn); + } run(): void { if (this._parent) this._parent.run(); - this._fns.forEach((fn) => { fn(); }); + this._fns.forEach((fn) => { + fn(); + }); } } // Reset the test providers before each test -jsmBeforeEach(() => { testBed.resetTestingModule(); }); +jsmBeforeEach(() => { + testBed.resetTestingModule(); +}); function _describe(jsmFn: Function, ...args: any[]) { const parentRunner = runnerStack.length === 0 ? null : runnerStack[runnerStack.length - 1]; - const runner = new BeforeEachRunner(parentRunner !); + const runner = new BeforeEachRunner(parentRunner!); runnerStack.push(runner); const suite = jsmFn(...args); runnerStack.pop(); @@ -138,7 +144,7 @@ function _it(jsmFn: Function, testName: string, testFn: TestFn, testTimeout = 0) if (testFn.length === 0) { // TypeScript doesn't infer the TestFn type without parameters here, so we // need to manually cast it. - const retVal = (testFn as() => any)(); + const retVal = (testFn as () => any)(); if (isPromise(retVal)) { // Asynchronous test function that returns a Promise - wait for completion. retVal.then(done, done.fail); @@ -192,7 +198,9 @@ export class SpyObject { return (this as any)[name]; } - prop(name: string, value: any) { (this as any)[name] = value; } + prop(name: string, value: any) { + (this as any)[name] = value; + } static stub(object: any = null, config: any = null, overrides: any = null) { if (!(object instanceof SpyObject)) { @@ -202,7 +210,9 @@ export class SpyObject { } const m = {...config, ...overrides}; - Object.keys(m).forEach(key => { object.spy(key).and.returnValue(m[key]); }); + Object.keys(m).forEach(key => { + object.spy(key).and.returnValue(m[key]); + }); return object; } } diff --git a/packages/elements/public_api.ts b/packages/elements/public_api.ts index 0b501cc0d7d56..98a3d66c23cbb 100644 --- a/packages/elements/public_api.ts +++ b/packages/elements/public_api.ts @@ -11,7 +11,7 @@ * @description * Entry point for all public APIs of the `elements` package. */ -export {NgElement, NgElementConfig, NgElementConstructor, WithProperties, createCustomElement} from './src/create-custom-element'; +export {createCustomElement, NgElement, NgElementConfig, NgElementConstructor, WithProperties} from './src/create-custom-element'; export {NgElementStrategy, NgElementStrategyEvent, NgElementStrategyFactory} from './src/element-strategy'; export {VERSION} from './src/version'; diff --git a/packages/elements/schematics/ng-add/index.ts b/packages/elements/schematics/ng-add/index.ts index ad8c0afcd1134..4db7a43f95833 100644 --- a/packages/elements/schematics/ng-add/index.ts +++ b/packages/elements/schematics/ng-add/index.ts @@ -5,9 +5,9 @@ * 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 {Rule, SchematicContext, SchematicsException, Tree, chain, noop} from '@angular-devkit/schematics'; +import {chain, noop, Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks'; -import {NodeDependencyType, addPackageJsonDependency} from '@schematics/angular/utility/dependencies'; +import {addPackageJsonDependency, NodeDependencyType} from '@schematics/angular/utility/dependencies'; import {getWorkspace} from '@schematics/angular/utility/workspace'; import {Schema} from './schema'; @@ -36,7 +36,7 @@ function addPolyfillDependency(): Rule { /** Adds the document-register-element.js to the polyfills file. */ function addPolyfill(options: Schema): Rule { - return async(host: Tree, context: SchematicContext) => { + return async (host: Tree, context: SchematicContext) => { const projectName = options.project; if (!projectName) { diff --git a/packages/elements/schematics/ng-add/index_spec.ts b/packages/elements/schematics/ng-add/index_spec.ts index 4e4d8737f274c..0cb6ba047287a 100644 --- a/packages/elements/schematics/ng-add/index_spec.ts +++ b/packages/elements/schematics/ng-add/index_spec.ts @@ -13,7 +13,9 @@ import {Schema as ElementsOptions} from './schema'; // tslint:disable:max-line-length describe('Elements Schematics', () => { const schematicRunner = new SchematicTestRunner( - '@angular/elements', path.join(__dirname, '../test-collection.json'), ); + '@angular/elements', + path.join(__dirname, '../test-collection.json'), + ); const defaultOptions: ElementsOptions = {project: 'elements', skipPackageJson: false}; let appTree: UnitTestTree; @@ -35,7 +37,7 @@ describe('Elements Schematics', () => { skipTests: false, }; - beforeEach(async() => { + beforeEach(async () => { appTree = await schematicRunner .runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions) .toPromise(); @@ -45,14 +47,14 @@ describe('Elements Schematics', () => { .toPromise(); }); - it('should run the ng-add schematic', async() => { + it('should run the ng-add schematic', async () => { const tree = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, appTree).toPromise(); expect(tree.readContent('/projects/elements/src/polyfills.ts')) .toContain(`import 'document-register-element';`); }); - it('should add polyfill as a dependency in package.json', async() => { + it('should add polyfill as a dependency in package.json', async () => { const tree = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, appTree).toPromise(); const pkgJsonText = tree.readContent('/package.json'); diff --git a/packages/elements/src/component-factory-strategy.ts b/packages/elements/src/component-factory-strategy.ts index 8f27a721c9fa2..e5b691b680051 100644 --- a/packages/elements/src/component-factory-strategy.ts +++ b/packages/elements/src/component-factory-strategy.ts @@ -7,7 +7,7 @@ */ import {ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, EventEmitter, Injector, OnChanges, SimpleChange, SimpleChanges, Type} from '@angular/core'; -import {Observable, merge} from 'rxjs'; +import {merge, Observable} from 'rxjs'; import {map} from 'rxjs/operators'; import {NgElementStrategy, NgElementStrategyEvent, NgElementStrategyFactory} from './element-strategy'; @@ -45,11 +45,11 @@ export class ComponentNgElementStrategyFactory implements NgElementStrategyFacto export class ComponentNgElementStrategy implements NgElementStrategy { /** Merged stream of the component's output events. */ // TODO(issue/24571): remove '!'. - events !: Observable<NgElementStrategyEvent>; + events!: Observable<NgElementStrategyEvent>; /** Reference to the component that was created on connect. */ // TODO(issue/24571): remove '!'. - private componentRef !: ComponentRef<any>| null; + private componentRef!: ComponentRef<any>|null; /** Changes that have been made to the component ref since the last time onChanges was called. */ private inputChanges: SimpleChanges|null = null; @@ -66,8 +66,11 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** Initial input values that were set before the component was created. */ private readonly initialInputValues = new Map<string, any>(); - /** Set of inputs that were not initially set when the component was created. */ - private readonly uninitializedInputs = new Set<string>(); + /** + * Set of component inputs that have not yet changed, i.e. for which `ngOnChanges()` has not + * fired. (This is used to determine the value of `fistChange` in `SimpleChange` instances.) + */ + private readonly unchangedInputs = new Set<string>(); constructor(private componentFactory: ComponentFactory<any>, private injector: Injector) {} @@ -102,7 +105,7 @@ export class ComponentNgElementStrategy implements NgElementStrategy { // moved elsewhere in the DOM this.scheduledDestroyFn = scheduler.schedule(() => { if (this.componentRef) { - this.componentRef !.destroy(); + this.componentRef!.destroy(); this.componentRef = null; } }, DESTROY_DELAY); @@ -130,7 +133,11 @@ export class ComponentNgElementStrategy implements NgElementStrategy { return; } - if (strictEquals(value, this.getInputValue(property))) { + // Ignore the value if it is strictly equal to the current value, except if it is `undefined` + // and this is the first change to the value (because an explicit `undefined` _is_ strictly + // equal to not having a value set at all, but we still need to record this as a change). + if (strictEquals(value, this.getInputValue(property)) && + !((value === undefined) && this.unchangedInputs.has(property))) { return; } @@ -164,12 +171,16 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** Set any stored initial inputs on the component's properties. */ protected initializeInputs(): void { this.componentFactory.inputs.forEach(({propName}) => { + if (this.implementsOnChanges) { + // If the component implements `ngOnChanges()`, keep track of which inputs have never + // changed so far. + this.unchangedInputs.add(propName); + } + if (this.initialInputValues.has(propName)) { + // Call `setInputValue()` now that the component has been instantiated to update its + // properties and fire `ngOnChanges()`. this.setInputValue(propName, this.initialInputValues.get(propName)); - } else { - // Keep track of inputs that were not initialized in case we need to know this for - // calling ngOnChanges with SimpleChanges - this.uninitializedInputs.add(propName); } }); @@ -179,7 +190,7 @@ export class ComponentNgElementStrategy implements NgElementStrategy { /** Sets up listeners for the component's outputs so that the events stream emits the events. */ protected initializeOutputs(): void { const eventEmitters = this.componentFactory.outputs.map(({propName, templateName}) => { - const emitter = (this.componentRef !.instance as any)[propName] as EventEmitter<any>; + const emitter = (this.componentRef!.instance as any)[propName] as EventEmitter<any>; return emitter.pipe(map((value: any) => ({name: templateName, value}))); }); @@ -196,7 +207,7 @@ export class ComponentNgElementStrategy implements NgElementStrategy { // during ngOnChanges. const inputChanges = this.inputChanges; this.inputChanges = null; - (this.componentRef !.instance as any as OnChanges).ngOnChanges(inputChanges); + (this.componentRef!.instance as any as OnChanges).ngOnChanges(inputChanges); } /** @@ -235,8 +246,8 @@ export class ComponentNgElementStrategy implements NgElementStrategy { return; } - const isFirstChange = this.uninitializedInputs.has(property); - this.uninitializedInputs.delete(property); + const isFirstChange = this.unchangedInputs.has(property); + this.unchangedInputs.delete(property); const previousValue = isFirstChange ? undefined : this.getInputValue(property); this.inputChanges[property] = new SimpleChange(previousValue, currentValue, isFirstChange); @@ -249,6 +260,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { } this.callNgOnChanges(); - this.componentRef !.changeDetectorRef.detectChanges(); + this.componentRef!.changeDetectorRef.detectChanges(); } } diff --git a/packages/elements/src/create-custom-element.ts b/packages/elements/src/create-custom-element.ts index 791a5656f4887..79763b63deeea 100644 --- a/packages/elements/src/create-custom-element.ts +++ b/packages/elements/src/create-custom-element.ts @@ -31,7 +31,7 @@ export interface NgElementConstructor<P> { * Initializes a constructor instance. * @param injector If provided, overrides the configured injector. */ - new (injector?: Injector): NgElement&WithProperties<P>; + new(injector?: Injector): NgElement&WithProperties<P>; } /** @@ -44,20 +44,20 @@ export abstract class NgElement extends HTMLElement { * The strategy that controls how a component is transformed in a custom element. */ // TODO(issue/24571): remove '!'. - protected ngElementStrategy !: NgElementStrategy; + protected ngElementStrategy!: NgElementStrategy; /** * A subscription to change, connect, and disconnect events in the custom element. */ protected ngElementEventsSubscription: Subscription|null = null; /** - * Prototype for a handler that responds to a change in an observed attribute. - * @param attrName The name of the attribute that has changed. - * @param oldValue The previous value of the attribute. - * @param newValue The new value of the attribute. - * @param namespace The namespace in which the attribute is defined. - * @returns Nothing. - */ + * Prototype for a handler that responds to a change in an observed attribute. + * @param attrName The name of the attribute that has changed. + * @param oldValue The previous value of the attribute. + * @param newValue The new value of the attribute. + * @param namespace The namespace in which the attribute is defined. + * @returns Nothing. + */ abstract attributeChangedCallback( attrName: string, oldValue: string|null, newValue: string, namespace?: string): void; /** @@ -152,7 +152,7 @@ export function createCustomElement<P>( this.ngElementStrategy = strategyFactory.create(config.injector); } - const propName = attributeToPropertyInputs[attrName] !; + const propName = attributeToPropertyInputs[attrName]!; this.ngElementStrategy.setInputValue(propName, newValue); } @@ -165,7 +165,7 @@ export function createCustomElement<P>( // Listen for events from the strategy and dispatch them as custom events this.ngElementEventsSubscription = this.ngElementStrategy.events.subscribe(e => { - const customEvent = createCustomEvent(this.ownerDocument !, e.name, e.value); + const customEvent = createCustomEvent(this.ownerDocument!, e.name, e.value); this.dispatchEvent(customEvent); }); } @@ -186,8 +186,12 @@ export function createCustomElement<P>( // contain property inputs, use all inputs by default. inputs.map(({propName}) => propName).forEach(property => { Object.defineProperty(NgElementImpl.prototype, property, { - get: function() { return this.ngElementStrategy.getInputValue(property); }, - set: function(newValue: any) { this.ngElementStrategy.setInputValue(property, newValue); }, + get: function() { + return this.ngElementStrategy.getInputValue(property); + }, + set: function(newValue: any) { + this.ngElementStrategy.setInputValue(property, newValue); + }, configurable: true, enumerable: true, }); diff --git a/packages/elements/src/utils.ts b/packages/elements/src/utils.ts index 2333eb40b34a4..2e9bf93e2ac62 100644 --- a/packages/elements/src/utils.ts +++ b/packages/elements/src/utils.ts @@ -22,8 +22,10 @@ export const scheduler = { * * Returns a function that when executed will cancel the scheduled function. */ - schedule(taskFn: () => void, delay: number): () => - void{const id = setTimeout(taskFn, delay); return () => clearTimeout(id);}, + schedule(taskFn: () => void, delay: number): () => void { + const id = setTimeout(taskFn, delay); + return () => clearTimeout(id); + }, /** * Schedule a callback to be called before the next render. @@ -31,7 +33,7 @@ export const scheduler = { * * Returns a function that when executed will cancel the scheduled function. */ - scheduleBeforeRender(taskFn: () => void): () => void{ + scheduleBeforeRender(taskFn: () => void): () => void { // TODO(gkalpak): Implement a better way of accessing `requestAnimationFrame()` // (e.g. accounting for vendor prefix, SSR-compatibility, etc). if (typeof window === 'undefined') { @@ -76,7 +78,7 @@ export function createCustomEvent(doc: Document, name: string, detail: any): Cus /** * Check whether the input is an `Element`. */ -export function isElement(node: Node | null): node is Element { +export function isElement(node: Node|null): node is Element { return !!node && node.nodeType === Node.ELEMENT_NODE; } diff --git a/packages/elements/test/BUILD.bazel b/packages/elements/test/BUILD.bazel index c73f50e5bc1c3..29565ccc704cd 100644 --- a/packages/elements/test/BUILD.bazel +++ b/packages/elements/test/BUILD.bazel @@ -43,6 +43,17 @@ karma_web_test_suite( bootstrap = [ ":elements_test_bootstrap_scripts", ], + tags = [ + # disabled on 2020-04-14 due to failure on saucelabs monitor job + # https://app.circleci.com/pipelines/github/angular/angular/13320/workflows/9ca3527a-d448-4a64-880a-fb4de9d1fece/jobs/680645 + # ``` + # IE 10.0.0 (Windows 8.0.0) ERROR + # An error was thrown in afterAll + # Syntax error + # ``` + "fixme-saucelabs-ve", + "fixme-saucelabs-ivy", + ], deps = [ ":test_lib", ], diff --git a/packages/elements/test/component-factory-strategy_spec.ts b/packages/elements/test/component-factory-strategy_spec.ts index 54b8bd8f8af78..c93e182986b2f 100644 --- a/packages/elements/test/component-factory-strategy_spec.ts +++ b/packages/elements/test/component-factory-strategy_spec.ts @@ -53,11 +53,13 @@ describe('ComponentFactoryNgElementStrategy', () => { strategy.connect(document.createElement('div')); }); - it('should attach the component to the view', - () => { expect(applicationRef.attachView).toHaveBeenCalledWith(componentRef.hostView); }); + it('should attach the component to the view', () => { + expect(applicationRef.attachView).toHaveBeenCalledWith(componentRef.hostView); + }); - it('should detect changes', - () => { expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalled(); }); + it('should detect changes', () => { + expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalled(); + }); it('should listen to output events', () => { const events: NgElementStrategyEvent[] = []; @@ -93,22 +95,25 @@ describe('ComponentFactoryNgElementStrategy', () => { it('should call ngOnChanges with the change', () => { expectSimpleChanges(componentRef.instance.simpleChanges[0], { - fooFoo: new SimpleChange(undefined, 'fooFoo-1', false), - falsyNull: new SimpleChange(undefined, null, false), - falsyEmpty: new SimpleChange(undefined, '', false), - falsyFalse: new SimpleChange(undefined, false, false), - falsyZero: new SimpleChange(undefined, 0, false), + fooFoo: new SimpleChange(undefined, 'fooFoo-1', true), + falsyUndefined: new SimpleChange(undefined, undefined, true), + falsyNull: new SimpleChange(undefined, null, true), + falsyEmpty: new SimpleChange(undefined, '', true), + falsyFalse: new SimpleChange(undefined, false, true), + falsyZero: new SimpleChange(undefined, 0, true), }); }); it('should call ngOnChanges with proper firstChange value', fakeAsync(() => { - strategy.setInputValue('falsyUndefined', 'notanymore'); + strategy.setInputValue('fooFoo', 'fooFoo-2'); strategy.setInputValue('barBar', 'barBar-1'); + strategy.setInputValue('falsyUndefined', 'notanymore'); tick(16); // scheduler waits 16ms if RAF is unavailable (strategy as any).detectChanges(); expectSimpleChanges(componentRef.instance.simpleChanges[1], { - falsyUndefined: new SimpleChange(undefined, 'notanymore', false), + fooFoo: new SimpleChange('fooFoo-1', 'fooFoo-2', false), barBar: new SimpleChange(undefined, 'barBar-1', true), + falsyUndefined: new SimpleChange(undefined, 'notanymore', false), }); })); }); @@ -137,7 +142,9 @@ describe('ComponentFactoryNgElementStrategy', () => { }); describe('when inputs change and is connected', () => { - beforeEach(() => { strategy.connect(document.createElement('div')); }); + beforeEach(() => { + strategy.connect(document.createElement('div')); + }); it('should be set on the component instance', () => { strategy.setInputValue('fooFoo', 'fooFoo-1'); @@ -246,7 +253,9 @@ export class FakeComponent { // Keep track of the simple changes passed to ngOnChanges simpleChanges: SimpleChanges[] = []; - ngOnChanges(simpleChanges: SimpleChanges) { this.simpleChanges.push(simpleChanges); } + ngOnChanges(simpleChanges: SimpleChanges) { + this.simpleChanges.push(simpleChanges); + } } export class FakeComponentFactory extends ComponentFactory<any> { @@ -260,9 +269,15 @@ export class FakeComponentFactory extends ComponentFactory<any> { jasmine.createSpyObj('changeDetectorRef', ['detectChanges']); } - get selector(): string { return 'fake-component'; } - get componentType(): Type<any> { return FakeComponent; } - get ngContentSelectors(): string[] { return ['content-1', 'content-2']; } + get selector(): string { + return 'fake-component'; + } + get componentType(): Type<any> { + return FakeComponent; + } + get ngContentSelectors(): string[] { + return ['content-1', 'content-2']; + } get inputs(): {propName: string; templateName: string}[] { return [ {propName: 'fooFoo', templateName: 'fooFoo'}, @@ -290,15 +305,16 @@ export class FakeComponentFactory extends ComponentFactory<any> { } function expectSimpleChanges(actual: SimpleChanges, expected: SimpleChanges) { - Object.keys(actual).forEach( - key => { expect(expected[key]).toBeTruthy(`Change included additional key ${key}`); }); + Object.keys(actual).forEach(key => { + expect(expected[key]).toBeTruthy(`Change included additional key ${key}`); + }); Object.keys(expected).forEach(key => { expect(actual[key]).toBeTruthy(`Change should have included key ${key}`); if (actual[key]) { - expect(actual[key].previousValue).toBe(expected[key].previousValue); - expect(actual[key].currentValue).toBe(expected[key].currentValue); - expect(actual[key].firstChange).toBe(expected[key].firstChange); + expect(actual[key].previousValue).toBe(expected[key].previousValue, `${key}.previousValue`); + expect(actual[key].currentValue).toBe(expected[key].currentValue, `${key}.currentValue`); + expect(actual[key].firstChange).toBe(expected[key].firstChange, `${key}.firstChange`); } }); } diff --git a/packages/elements/test/create-custom-element_spec.ts b/packages/elements/test/create-custom-element_spec.ts index 34180a1e814db..450e1eed53031 100644 --- a/packages/elements/test/create-custom-element_spec.ts +++ b/packages/elements/test/create-custom-element_spec.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, DoBootstrap, EventEmitter, Injector, Input, NgModule, Output, destroyPlatform} from '@angular/core'; +import {Component, destroyPlatform, DoBootstrap, EventEmitter, Injector, Input, NgModule, Output} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; import {Subject} from 'rxjs'; -import {NgElementConstructor, createCustomElement} from '../src/create-custom-element'; +import {createCustomElement, NgElementConstructor} from '../src/create-custom-element'; import {NgElementStrategy, NgElementStrategyEvent, NgElementStrategyFactory} from '../src/element-strategy'; type WithFooBar = { @@ -105,7 +105,7 @@ if (browserDetection.supportsCustomElements) { class TestComponent { @Input() fooFoo: string = 'foo'; // TODO(issue/24571): remove '!'. - @Input('barbar') barBar !: string; + @Input('barbar') barBar!: string; @Output() bazBaz = new EventEmitter<boolean>(); @Output('quxqux') quxQux = new EventEmitter<Object>(); @@ -126,17 +126,27 @@ export class TestStrategy implements NgElementStrategy { events = new Subject<NgElementStrategyEvent>(); - connect(element: HTMLElement): void { this.connectedElement = element; } + connect(element: HTMLElement): void { + this.connectedElement = element; + } - disconnect(): void { this.disconnectCalled = true; } + disconnect(): void { + this.disconnectCalled = true; + } - getInputValue(propName: string): any { return this.inputs.get(propName); } + getInputValue(propName: string): any { + return this.inputs.get(propName); + } - setInputValue(propName: string, value: string): void { this.inputs.set(propName, value); } + setInputValue(propName: string, value: string): void { + this.inputs.set(propName, value); + } } export class TestStrategyFactory implements NgElementStrategyFactory { testStrategy = new TestStrategy(); - create(): NgElementStrategy { return this.testStrategy; } + create(): NgElementStrategy { + return this.testStrategy; + } } diff --git a/packages/elements/test/slots_spec.ts b/packages/elements/test/slots_spec.ts index 1a8bc7e150cda..f69c526c95b12 100644 --- a/packages/elements/test/slots_spec.ts +++ b/packages/elements/test/slots_spec.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, ComponentFactoryResolver, EventEmitter, Input, NgModule, Output, ViewEncapsulation, destroyPlatform} from '@angular/core'; +import {Component, ComponentFactoryResolver, destroyPlatform, EventEmitter, Input, NgModule, Output, ViewEncapsulation} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; -import {NgElement, createCustomElement} from '../src/create-custom-element'; +import {createCustomElement, NgElement} from '../src/create-custom-element'; // we only run these tests in browsers that support Shadom DOM slots natively @@ -46,9 +46,9 @@ if (browserDetection.supportsCustomElements && browserDetection.supportsShadowDo it('should use slots to project content', () => { const tpl = `<default-slot-el><span class="projected"></span></default-slot-el>`; testContainer.innerHTML = tpl; - const testEl = testContainer.querySelector('default-slot-el') !; - const content = testContainer.querySelector('span.projected') !; - const slot = testEl.shadowRoot !.querySelector('slot') !; + const testEl = testContainer.querySelector('default-slot-el')!; + const content = testContainer.querySelector('span.projected')!; + const slot = testEl.shadowRoot!.querySelector('slot')!; const assignedNodes = slot.assignedNodes(); expect(assignedNodes[0]).toBe(content); }); @@ -56,9 +56,9 @@ if (browserDetection.supportsCustomElements && browserDetection.supportsShadowDo it('should use a named slot to project content', () => { const tpl = `<named-slot-el><span class="projected" slot="header"></span></named-slot-el>`; testContainer.innerHTML = tpl; - const testEl = testContainer.querySelector('named-slot-el') !; - const content = testContainer.querySelector('span.projected') !; - const slot = testEl.shadowRoot !.querySelector('slot[name=header]') as HTMLSlotElement; + const testEl = testContainer.querySelector('named-slot-el')!; + const content = testContainer.querySelector('span.projected')!; + const slot = testEl.shadowRoot!.querySelector('slot[name=header]') as HTMLSlotElement; const assignedNodes = slot.assignedNodes(); expect(assignedNodes[0]).toBe(content); }); @@ -70,11 +70,11 @@ if (browserDetection.supportsCustomElements && browserDetection.supportsShadowDo <span class="projected-body" slot="body"></span> </named-slots-el>`; testContainer.innerHTML = tpl; - const testEl = testContainer.querySelector('named-slots-el') !; - const headerContent = testContainer.querySelector('span.projected-header') !; - const bodyContent = testContainer.querySelector('span.projected-body') !; - const headerSlot = testEl.shadowRoot !.querySelector('slot[name=header]') as HTMLSlotElement; - const bodySlot = testEl.shadowRoot !.querySelector('slot[name=body]') as HTMLSlotElement; + const testEl = testContainer.querySelector('named-slots-el')!; + const headerContent = testContainer.querySelector('span.projected-header')!; + const bodyContent = testContainer.querySelector('span.projected-body')!; + const headerSlot = testEl.shadowRoot!.querySelector('slot[name=header]') as HTMLSlotElement; + const bodySlot = testEl.shadowRoot!.querySelector('slot[name=body]') as HTMLSlotElement; expect(headerContent.assignedSlot).toBe(headerSlot); expect(bodyContent.assignedSlot).toBe(bodySlot); @@ -88,7 +88,7 @@ if (browserDetection.supportsCustomElements && browserDetection.supportsShadowDo </slot-events-el>`; templateEl.innerHTML = tpl; const template = templateEl.content.cloneNode(true) as DocumentFragment; - const testEl = template.querySelector('slot-events-el') !as NgElement & SlotEventsComponent; + const testEl = template.querySelector('slot-events-el')! as NgElement & SlotEventsComponent; testEl.addEventListener('slotEventsChange', e => { expect(testEl.slotEvents.length).toEqual(1); done(); diff --git a/packages/elements/test/utils_spec.ts b/packages/elements/test/utils_spec.ts index 930fcf086acf8..97cc62b7a545d 100644 --- a/packages/elements/test/utils_spec.ts +++ b/packages/elements/test/utils_spec.ts @@ -15,7 +15,10 @@ describe('utils', () => { let clearTimeoutSpy: jasmine.Spy; beforeEach(() => { - setTimeoutSpy = spyOn(window, 'setTimeout').and.returnValue(42); + // TODO: @JiaLiPassion, need to wait @types/jasmine to fix the wrong return + // type infer issue. + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486 + setTimeoutSpy = spyOn(window, 'setTimeout').and.returnValue(42 as any); clearTimeoutSpy = spyOn(window, 'clearTimeout'); }); @@ -81,8 +84,9 @@ describe('utils', () => { expect(camelToDashCase('foo1Bar2Baz3Qux4')).toBe('foo1-bar2-baz3-qux4'); }); - it('should keep existing dashes', - () => { expect(camelToDashCase('fooBar-baz-Qux')).toBe('foo-bar-baz--qux'); }); + it('should keep existing dashes', () => { + expect(camelToDashCase('fooBar-baz-Qux')).toBe('foo-bar-baz--qux'); + }); }); describe('createCustomEvent()', () => { @@ -97,7 +101,6 @@ describe('utils', () => { expect(event.cancelable).toBe(false); expect(event.detail).toEqual(value); }); - }); describe('isElement()', () => { @@ -129,7 +132,7 @@ describe('utils', () => { it('should return true for functions', () => { const obj = {foo: function() {}, bar: () => null, baz() {}}; const fns = [ - function(){}, + function() {}, () => null, obj.foo, obj.bar, @@ -180,7 +183,7 @@ describe('utils', () => { </ul> </div> `; - li = div.querySelector('li') !; + li = div.querySelector('li')!; }); it('should return whether the element matches the selector', () => { @@ -216,7 +219,9 @@ describe('utils', () => { ]; values.forEach((v1, i) => { - values.forEach((v2, j) => { expect(strictEquals(v1, v2)).toBe(i === j); }); + values.forEach((v2, j) => { + expect(strictEquals(v1, v2)).toBe(i === j); + }); }); }); diff --git a/packages/examples/common/location/ts/hash_location_component.ts b/packages/examples/common/location/ts/hash_location_component.ts index 4021655b8e082..3bff5e48e4736 100644 --- a/packages/examples/common/location/ts/hash_location_component.ts +++ b/packages/examples/common/location/ts/hash_location_component.ts @@ -21,6 +21,8 @@ import {Component} from '@angular/core'; }) export class HashLocationComponent { location: Location; - constructor(location: Location) { this.location = location; } + constructor(location: Location) { + this.location = location; + } } // #enddocregion diff --git a/packages/examples/common/location/ts/path_location_component.ts b/packages/examples/common/location/ts/path_location_component.ts index 3984f23e6139e..7718afbb17d3c 100644 --- a/packages/examples/common/location/ts/path_location_component.ts +++ b/packages/examples/common/location/ts/path_location_component.ts @@ -21,6 +21,8 @@ import {Component} from '@angular/core'; }) export class PathLocationComponent { location: Location; - constructor(location: Location) { this.location = location; } + constructor(location: Location) { + this.location = location; + } } // #enddocregion diff --git a/packages/examples/common/ngComponentOutlet/ts/e2e_test/ngComponentOutlet_spec.ts b/packages/examples/common/ngComponentOutlet/ts/e2e_test/ngComponentOutlet_spec.ts index 6dd03cfed86ad..fb8922060876e 100644 --- a/packages/examples/common/ngComponentOutlet/ts/e2e_test/ngComponentOutlet_spec.ts +++ b/packages/examples/common/ngComponentOutlet/ts/e2e_test/ngComponentOutlet_spec.ts @@ -7,7 +7,7 @@ */ import {modifiedInIvy} from '@angular/private/testing'; -import {$, ExpectedConditions, browser, by, element} from 'protractor'; +import {$, browser, by, element, ExpectedConditions} from 'protractor'; import {verifyNoBrowserErrors} from '../../../../test-utils'; diff --git a/packages/examples/common/ngIf/ts/e2e_test/ngIf_spec.ts b/packages/examples/common/ngIf/ts/e2e_test/ngIf_spec.ts index 941e1ed05d322..7ebd8299c930f 100644 --- a/packages/examples/common/ngIf/ts/e2e_test/ngIf_spec.ts +++ b/packages/examples/common/ngIf/ts/e2e_test/ngIf_spec.ts @@ -7,7 +7,8 @@ */ import {modifiedInIvy} from '@angular/private/testing'; -import {$, ExpectedConditions, browser, by, element} from 'protractor'; +import {$, browser, by, element, ExpectedConditions} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; function waitForElement(selector: string) { diff --git a/packages/examples/common/ngIf/ts/module.ts b/packages/examples/common/ngIf/ts/module.ts index 913b6e3dc3388..9a258fbf2f40b 100644 --- a/packages/examples/common/ngIf/ts/module.ts +++ b/packages/examples/common/ngIf/ts/module.ts @@ -60,16 +60,16 @@ export class NgIfThenElse implements OnInit { thenBlock: TemplateRef<any>|null = null; show: boolean = true; - @ViewChild('primaryBlock', {static: true}) - primaryBlock: TemplateRef<any>|null = null; - @ViewChild('secondaryBlock', {static: true}) - secondaryBlock: TemplateRef<any>|null = null; + @ViewChild('primaryBlock', {static: true}) primaryBlock: TemplateRef<any>|null = null; + @ViewChild('secondaryBlock', {static: true}) secondaryBlock: TemplateRef<any>|null = null; switchPrimary() { this.thenBlock = this.thenBlock === this.primaryBlock ? this.secondaryBlock : this.primaryBlock; } - ngOnInit() { this.thenBlock = this.primaryBlock; } + ngOnInit() { + this.thenBlock = this.primaryBlock; + } } // #enddocregion diff --git a/packages/examples/common/ngTemplateOutlet/ts/e2e_test/ngTemplateOutlet_spec.ts b/packages/examples/common/ngTemplateOutlet/ts/e2e_test/ngTemplateOutlet_spec.ts index 508a68b01e7aa..cbdf0958ec67c 100644 --- a/packages/examples/common/ngTemplateOutlet/ts/e2e_test/ngTemplateOutlet_spec.ts +++ b/packages/examples/common/ngTemplateOutlet/ts/e2e_test/ngTemplateOutlet_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {$, ExpectedConditions, browser, by, element} from 'protractor'; +import {$, browser, by, element, ExpectedConditions} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; function waitForElement(selector: string) { diff --git a/packages/examples/common/pipes/ts/async_pipe.ts b/packages/examples/common/pipes/ts/async_pipe.ts index c1a6028f1fc50..109e83f57dfb8 100644 --- a/packages/examples/common/pipes/ts/async_pipe.ts +++ b/packages/examples/common/pipes/ts/async_pipe.ts @@ -24,18 +24,22 @@ export class AsyncPromisePipeComponent { private resolve: Function|null = null; - constructor() { this.reset(); } + constructor() { + this.reset(); + } reset() { this.arrived = false; - this.greeting = new Promise<string>((resolve, reject) => { this.resolve = resolve; }); + this.greeting = new Promise<string>((resolve, reject) => { + this.resolve = resolve; + }); } clicked() { if (this.arrived) { this.reset(); } else { - this.resolve !('hi there!'); + this.resolve!('hi there!'); this.arrived = true; } } @@ -64,6 +68,8 @@ function setInterval(fn: Function, delay: number) { rootZone = rootZone.parent; } rootZone.run(() => { - window.setInterval(function(this: unknown) { zone.run(fn, this, arguments as any); }, delay); + window.setInterval(function(this: unknown) { + zone.run(fn, this, arguments as any); + }, delay); }); } diff --git a/packages/examples/common/pipes/ts/e2e_test/pipe_spec.ts b/packages/examples/common/pipes/ts/e2e_test/pipe_spec.ts index 6c75ea63d849b..00224b17f861b 100644 --- a/packages/examples/common/pipes/ts/e2e_test/pipe_spec.ts +++ b/packages/examples/common/pipes/ts/e2e_test/pipe_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {$, ExpectedConditions, browser, by, element} from 'protractor'; +import {$, browser, by, element, ExpectedConditions} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; function waitForElement(selector: string) { diff --git a/packages/examples/common/pipes/ts/lowerupper_pipe.ts b/packages/examples/common/pipes/ts/lowerupper_pipe.ts index 4e466b0cbbd84..9783156c2078d 100644 --- a/packages/examples/common/pipes/ts/lowerupper_pipe.ts +++ b/packages/examples/common/pipes/ts/lowerupper_pipe.ts @@ -19,7 +19,9 @@ import {Component} from '@angular/core'; }) export class LowerUpperPipeComponent { // TODO(issue/24571): remove '!'. - value !: string; - change(value: string) { this.value = value; } + value!: string; + change(value: string) { + this.value = value; + } } // #enddocregion diff --git a/packages/examples/core/animation/ts/dsl/animation_example.ts b/packages/examples/core/animation/ts/dsl/animation_example.ts index dc3e527ed72f6..3b8a563b22e10 100644 --- a/packages/examples/core/animation/ts/dsl/animation_example.ts +++ b/packages/examples/core/animation/ts/dsl/animation_example.ts @@ -30,8 +30,7 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; [ state('collapsed, void', style({height: '0px', color: 'maroon', borderColor: 'maroon'})), state('expanded', style({height: '*', borderColor: 'green', color: 'green'})), - transition( - 'collapsed <=> expanded', [animate(500, style({height: '250px'})), animate(500)]) + transition('collapsed <=> expanded', [animate(500, style({height: '250px'})), animate(500)]) ])], template: ` <button (click)="expand()">Open</button> @@ -44,10 +43,16 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; }) export class MyExpandoCmp { // TODO(issue/24571): remove '!'. - stateExpression !: string; - constructor() { this.collapse(); } - expand() { this.stateExpression = 'expanded'; } - collapse() { this.stateExpression = 'collapsed'; } + stateExpression!: string; + constructor() { + this.collapse(); + } + expand() { + this.stateExpression = 'expanded'; + } + collapse() { + this.stateExpression = 'collapsed'; + } } @NgModule( diff --git a/packages/examples/core/animation/ts/dsl/e2e_test/animation_example_spec.ts b/packages/examples/core/animation/ts/dsl/e2e_test/animation_example_spec.ts index 550af8d2a58e0..c10a054fb36a6 100644 --- a/packages/examples/core/animation/ts/dsl/e2e_test/animation_example_spec.ts +++ b/packages/examples/core/animation/ts/dsl/e2e_test/animation_example_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {$, ExpectedConditions, browser, by, element} from 'protractor'; +import {$, browser, by, element, ExpectedConditions} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../../test-utils'; function waitForElement(selector: string) { diff --git a/packages/examples/core/debug/ts/debug_element/debug_element.ts b/packages/examples/core/debug/ts/debug_element/debug_element.ts index 63cf722dc0aad..ba2edc338d12d 100644 --- a/packages/examples/core/debug/ts/debug_element/debug_element.ts +++ b/packages/examples/core/debug/ts/debug_element/debug_element.ts @@ -8,7 +8,7 @@ import {DebugElement} from '@angular/core'; -let debugElement: DebugElement = undefined !; +let debugElement: DebugElement = undefined!; let predicate: any; // #docregion scope_all diff --git a/packages/examples/core/di/ts/contentChild/content_child_example.ts b/packages/examples/core/di/ts/contentChild/content_child_example.ts index f24a611c1f5dc..5ffd18fed9a61 100644 --- a/packages/examples/core/di/ts/contentChild/content_child_example.ts +++ b/packages/examples/core/di/ts/contentChild/content_child_example.ts @@ -11,7 +11,7 @@ import {Component, ContentChild, Directive, Input} from '@angular/core'; @Directive({selector: 'pane'}) export class Pane { - @Input() id !: string; + @Input() id!: string; } @Component({ @@ -21,7 +21,7 @@ export class Pane { ` }) export class Tab { - @ContentChild(Pane) pane !: Pane; + @ContentChild(Pane) pane!: Pane; } @Component({ @@ -38,6 +38,8 @@ export class Tab { export class ContentChildComp { shouldShow = true; - toggle() { this.shouldShow = !this.shouldShow; } + toggle() { + this.shouldShow = !this.shouldShow; + } } // #enddocregion diff --git a/packages/examples/core/di/ts/contentChild/content_child_howto.ts b/packages/examples/core/di/ts/contentChild/content_child_howto.ts index d062b703f81c0..b8786393eac54 100644 --- a/packages/examples/core/di/ts/contentChild/content_child_howto.ts +++ b/packages/examples/core/di/ts/contentChild/content_child_howto.ts @@ -15,7 +15,7 @@ class ChildDirective { @Directive({selector: 'someDir'}) class SomeDir implements AfterContentInit { - @ContentChild(ChildDirective) contentChild !: ChildDirective; + @ContentChild(ChildDirective) contentChild!: ChildDirective; ngAfterContentInit() { // contentChild is set diff --git a/packages/examples/core/di/ts/contentChild/e2e_test/content_child_spec.ts b/packages/examples/core/di/ts/contentChild/e2e_test/content_child_spec.ts index 8c88393838d65..96290c5f650e6 100644 --- a/packages/examples/core/di/ts/contentChild/e2e_test/content_child_spec.ts +++ b/packages/examples/core/di/ts/contentChild/e2e_test/content_child_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../../test-utils'; describe('contentChild example', () => { diff --git a/packages/examples/core/di/ts/contentChildren/content_children_example.ts b/packages/examples/core/di/ts/contentChildren/content_children_example.ts index 99e981b87b68a..d4de03552ed1a 100644 --- a/packages/examples/core/di/ts/contentChildren/content_children_example.ts +++ b/packages/examples/core/di/ts/contentChildren/content_children_example.ts @@ -11,7 +11,7 @@ import {Component, ContentChildren, Directive, Input, QueryList} from '@angular/ @Directive({selector: 'pane'}) export class Pane { - @Input() id !: string; + @Input() id!: string; } @Component({ @@ -22,8 +22,8 @@ export class Pane { ` }) export class Tab { - @ContentChildren(Pane) topLevelPanes !: QueryList<Pane>; - @ContentChildren(Pane, {descendants: true}) arbitraryNestedPanes !: QueryList<Pane>; + @ContentChildren(Pane) topLevelPanes!: QueryList<Pane>; + @ContentChildren(Pane, {descendants: true}) arbitraryNestedPanes!: QueryList<Pane>; get serializedPanes(): string { return this.topLevelPanes ? this.topLevelPanes.map(p => p.id).join(', ') : ''; @@ -53,6 +53,8 @@ export class Tab { export class ContentChildrenComp { shouldShow = false; - show() { this.shouldShow = true; } + show() { + this.shouldShow = true; + } } // #enddocregion diff --git a/packages/examples/core/di/ts/contentChildren/content_children_howto.ts b/packages/examples/core/di/ts/contentChildren/content_children_howto.ts index 32c5b4b81cd7c..6cec0762d1bfa 100644 --- a/packages/examples/core/di/ts/contentChildren/content_children_howto.ts +++ b/packages/examples/core/di/ts/contentChildren/content_children_howto.ts @@ -15,7 +15,7 @@ class ChildDirective { @Directive({selector: 'someDir'}) class SomeDir implements AfterContentInit { - @ContentChildren(ChildDirective) contentChildren !: QueryList<ChildDirective>; + @ContentChildren(ChildDirective) contentChildren!: QueryList<ChildDirective>; ngAfterContentInit() { // contentChildren is set diff --git a/packages/examples/core/di/ts/contentChildren/e2e_test/content_children_spec.ts b/packages/examples/core/di/ts/contentChildren/e2e_test/content_children_spec.ts index 068e457704269..fcf4f2ddd1b58 100644 --- a/packages/examples/core/di/ts/contentChildren/e2e_test/content_children_spec.ts +++ b/packages/examples/core/di/ts/contentChildren/e2e_test/content_children_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../../test-utils'; describe('contentChildren example', () => { diff --git a/packages/examples/core/di/ts/forward_ref/forward_ref_spec.ts b/packages/examples/core/di/ts/forward_ref/forward_ref_spec.ts index 213c972222cbd..a9e9ad5d7eef9 100644 --- a/packages/examples/core/di/ts/forward_ref/forward_ref_spec.ts +++ b/packages/examples/core/di/ts/forward_ref/forward_ref_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angular/core'; +import {forwardRef, Inject, ReflectiveInjector, resolveForwardRef} from '@angular/core'; { describe('forwardRef examples', () => { @@ -26,7 +26,9 @@ import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angula // Door attempts to inject Lock, despite it not being defined yet. // forwardRef makes this possible. - constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { this.lock = lock; } + constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { + this.lock = lock; + } } // Only at this point Lock is defined. @@ -42,7 +44,7 @@ import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angula it('can be unwrapped', () => { // #docregion resolve_forward_ref const ref = forwardRef(() => 'refValue'); - expect(resolveForwardRef(ref)).toEqual('refValue'); + expect(resolveForwardRef(ref as any)).toEqual('refValue'); expect(resolveForwardRef('regularValue')).toEqual('regularValue'); // #enddocregion }); diff --git a/packages/examples/core/di/ts/injector_spec.ts b/packages/examples/core/di/ts/injector_spec.ts index 600bc132e884e..d89f6c8fe340a 100644 --- a/packages/examples/core/di/ts/injector_spec.ts +++ b/packages/examples/core/di/ts/injector_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {InjectFlags, InjectionToken, Injector, Type, inject, ɵsetCurrentInjector as setCurrentInjector} from '@angular/core'; +import {inject, InjectFlags, InjectionToken, Injector, Type, ɵsetCurrentInjector as setCurrentInjector} from '@angular/core'; class MockRootScopeInjector implements Injector { constructor(readonly parent: Injector) {} diff --git a/packages/examples/core/di/ts/metadata_spec.ts b/packages/examples/core/di/ts/metadata_spec.ts index 811790dd96b9a..48d2f038783c1 100644 --- a/packages/examples/core/di/ts/metadata_spec.ts +++ b/packages/examples/core/di/ts/metadata_spec.ts @@ -59,9 +59,8 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; } const injector = Injector.create({ - providers: [ - {provide: NeedsService, deps: [UsefulService]}, {provide: UsefulService, deps: []} - ] + providers: + [{provide: NeedsService, deps: [UsefulService]}, {provide: UsefulService, deps: []}] }); expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true); // #enddocregion @@ -104,7 +103,9 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; @Injectable() class NeedsDependency { - constructor(@SkipSelf() public dependency: Dependency) { this.dependency = dependency; } + constructor(@SkipSelf() public dependency: Dependency) { + this.dependency = dependency; + } } const parent = Injector.create({providers: [{provide: Dependency, deps: []}]}); @@ -158,7 +159,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; declarations: [App, ParentCmp, ChildDirective], }); - let cmp: ComponentFixture<App> = undefined !; + let cmp: ComponentFixture<App> = undefined!; expect(() => cmp = TestBed.createComponent(App)).not.toThrow(); expect(cmp.debugElement.children[0].children[0].injector.get(ChildDirective).logs).toEqual([ @@ -167,6 +168,5 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; ]); }); }); - }); } diff --git a/packages/examples/core/di/ts/provider_spec.ts b/packages/examples/core/di/ts/provider_spec.ts index b08423e55ccd1..17462158cfd78 100644 --- a/packages/examples/core/di/ts/provider_spec.ts +++ b/packages/examples/core/di/ts/provider_spec.ts @@ -55,7 +55,9 @@ import {Injectable, InjectionToken, Injector, Optional, ReflectiveInjector} from describe('ClassProvider', () => { it('works', () => { // #docregion ClassProvider - abstract class Shape { name !: string; } + abstract class Shape { + name!: string; + } class Square extends Shape { name = 'square'; @@ -92,7 +94,9 @@ import {Injectable, InjectionToken, Injector, Optional, ReflectiveInjector} from describe('StaticClassProvider', () => { it('works', () => { // #docregion StaticClassProvider - abstract class Shape { name !: string; } + abstract class Shape { + name!: string; + } class Square extends Shape { name = 'square'; @@ -200,6 +204,5 @@ import {Injectable, InjectionToken, Injector, Optional, ReflectiveInjector} from // #enddocregion }); }); - }); } diff --git a/packages/examples/core/di/ts/viewChild/e2e_test/view_child_spec.ts b/packages/examples/core/di/ts/viewChild/e2e_test/view_child_spec.ts index 295cd1dbf1fde..dae43e8b59874 100644 --- a/packages/examples/core/di/ts/viewChild/e2e_test/view_child_spec.ts +++ b/packages/examples/core/di/ts/viewChild/e2e_test/view_child_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../../test-utils'; describe('viewChild example', () => { diff --git a/packages/examples/core/di/ts/viewChild/view_child_example.ts b/packages/examples/core/di/ts/viewChild/view_child_example.ts index 6601fed8f9a9f..50b72216d1e7a 100644 --- a/packages/examples/core/di/ts/viewChild/view_child_example.ts +++ b/packages/examples/core/di/ts/viewChild/view_child_example.ts @@ -11,7 +11,7 @@ import {Component, Directive, Input, ViewChild} from '@angular/core'; @Directive({selector: 'pane'}) export class Pane { - @Input() id !: string; + @Input() id!: string; } @Component({ @@ -28,10 +28,14 @@ export class Pane { export class ViewChildComp { @ViewChild(Pane) set pane(v: Pane) { - setTimeout(() => { this.selectedPane = v.id; }, 0); + setTimeout(() => { + this.selectedPane = v.id; + }, 0); } selectedPane: string = ''; shouldShow = true; - toggle() { this.shouldShow = !this.shouldShow; } + toggle() { + this.shouldShow = !this.shouldShow; + } } // #enddocregion diff --git a/packages/examples/core/di/ts/viewChild/view_child_howto.ts b/packages/examples/core/di/ts/viewChild/view_child_howto.ts index 74c906bff6256..cfa5f923acca9 100644 --- a/packages/examples/core/di/ts/viewChild/view_child_howto.ts +++ b/packages/examples/core/di/ts/viewChild/view_child_howto.ts @@ -15,7 +15,7 @@ class ChildDirective { @Component({selector: 'someCmp', templateUrl: 'someCmp.html'}) class SomeCmp implements AfterViewInit { - @ViewChild(ChildDirective) child !: ChildDirective; + @ViewChild(ChildDirective) child!: ChildDirective; ngAfterViewInit() { // child is set diff --git a/packages/examples/core/di/ts/viewChildren/e2e_test/view_children_spec.ts b/packages/examples/core/di/ts/viewChildren/e2e_test/view_children_spec.ts index 701e8cc389bf6..09cd00945dde1 100644 --- a/packages/examples/core/di/ts/viewChildren/e2e_test/view_children_spec.ts +++ b/packages/examples/core/di/ts/viewChildren/e2e_test/view_children_spec.ts @@ -7,7 +7,8 @@ */ -import {ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../../test-utils'; describe('viewChildren example', () => { diff --git a/packages/examples/core/di/ts/viewChildren/view_children_example.ts b/packages/examples/core/di/ts/viewChildren/view_children_example.ts index f89bf47524461..3f8fa48b99702 100644 --- a/packages/examples/core/di/ts/viewChildren/view_children_example.ts +++ b/packages/examples/core/di/ts/viewChildren/view_children_example.ts @@ -11,7 +11,7 @@ import {AfterViewInit, Component, Directive, Input, QueryList, ViewChildren} fro @Directive({selector: 'pane'}) export class Pane { - @Input() id !: string; + @Input() id!: string; } @Component({ @@ -27,20 +27,26 @@ export class Pane { `, }) export class ViewChildrenComp implements AfterViewInit { - @ViewChildren(Pane) panes !: QueryList<Pane>; + @ViewChildren(Pane) panes!: QueryList<Pane>; serializedPanes: string = ''; shouldShow = false; - show() { this.shouldShow = true; } + show() { + this.shouldShow = true; + } ngAfterViewInit() { this.calculateSerializedPanes(); - this.panes.changes.subscribe((r) => { this.calculateSerializedPanes(); }); + this.panes.changes.subscribe((r) => { + this.calculateSerializedPanes(); + }); } calculateSerializedPanes() { - setTimeout(() => { this.serializedPanes = this.panes.map(p => p.id).join(', '); }, 0); + setTimeout(() => { + this.serializedPanes = this.panes.map(p => p.id).join(', '); + }, 0); } } // #enddocregion diff --git a/packages/examples/core/di/ts/viewChildren/view_children_howto.ts b/packages/examples/core/di/ts/viewChildren/view_children_howto.ts index 4d4273e5ba920..efb68d7394d80 100644 --- a/packages/examples/core/di/ts/viewChildren/view_children_howto.ts +++ b/packages/examples/core/di/ts/viewChildren/view_children_howto.ts @@ -15,7 +15,7 @@ class ChildDirective { @Component({selector: 'someCmp', templateUrl: 'someCmp.html'}) class SomeCmp implements AfterViewInit { - @ViewChildren(ChildDirective) viewChildren !: QueryList<ChildDirective>; + @ViewChildren(ChildDirective) viewChildren!: QueryList<ChildDirective>; ngAfterViewInit() { // viewChildren is set diff --git a/packages/examples/core/testability/ts/whenStable/e2e_test/testability_example_spec.ts b/packages/examples/core/testability/ts/whenStable/e2e_test/testability_example_spec.ts index 9ffc27a77446b..5bf7a58c8283b 100644 --- a/packages/examples/core/testability/ts/whenStable/e2e_test/testability_example_spec.ts +++ b/packages/examples/core/testability/ts/whenStable/e2e_test/testability_example_spec.ts @@ -29,7 +29,9 @@ describe('testability example', () => { let waitWithResultScript = function(done: any) { let rootEl = document.querySelector('example-app'); let testability = window.getAngularTestability(rootEl); - testability.whenStable((didWork: boolean, tasks: any) => { done(tasks); }, 1000); + testability.whenStable((didWork: boolean, tasks: any) => { + done(tasks); + }, 1000); }; element(by.css('.start-button')).click(); @@ -43,6 +45,8 @@ describe('testability example', () => { }); }); - afterAll(() => { browser.ignoreSynchronization = false; }); + afterAll(() => { + browser.ignoreSynchronization = false; + }); }); }); diff --git a/packages/examples/core/testability/ts/whenStable/testability_example.ts b/packages/examples/core/testability/ts/whenStable/testability_example.ts index d2df32fdcadd9..ec5633e237be9 100644 --- a/packages/examples/core/testability/ts/whenStable/testability_example.ts +++ b/packages/examples/core/testability/ts/whenStable/testability_example.ts @@ -20,7 +20,9 @@ export class StableTestCmp { status = 'none'; start() { this.status = 'running'; - setTimeout(() => { this.status = 'done'; }, 5000); + setTimeout(() => { + this.status = 'done'; + }, 5000); } } diff --git a/packages/examples/core/testing/ts/fake_async.ts b/packages/examples/core/testing/ts/fake_async.ts index 10e9ca2f33230..805289003af8a 100644 --- a/packages/examples/core/testing/ts/fake_async.ts +++ b/packages/examples/core/testing/ts/fake_async.ts @@ -13,7 +13,9 @@ import {discardPeriodicTasks, fakeAsync, tick} from '@angular/core/testing'; describe('this test', () => { it('looks async but is synchronous', <any>fakeAsync((): void => { let flag = false; - setTimeout(() => { flag = true; }, 100); + setTimeout(() => { + flag = true; + }, 100); expect(flag).toBe(false); tick(50); expect(flag).toBe(false); diff --git a/packages/examples/core/ts/change_detect/change-detection.ts b/packages/examples/core/ts/change_detect/change-detection.ts index 4b4b8b9bd0e39..c78d345e1d3a0 100644 --- a/packages/examples/core/ts/change_detect/change-detection.ts +++ b/packages/examples/core/ts/change_detect/change-detection.ts @@ -33,7 +33,9 @@ class AppComponent { // #docregion detach class DataListProvider { // in a real application the returned data will be different every time - get data() { return [1, 2, 3, 4, 5]; } + get data() { + return [1, 2, 3, 4, 5]; + } } @Component({ @@ -45,7 +47,9 @@ class DataListProvider { class GiantList { constructor(private ref: ChangeDetectorRef, public dataProvider: DataListProvider) { ref.detach(); - setInterval(() => { this.ref.detectChanges(); }, 5000); + setInterval(() => { + this.ref.detectChanges(); + }, 5000); } } @@ -64,7 +68,9 @@ class App { class DataProvider { data = 1; constructor() { - setInterval(() => { this.data = 2; }, 500); + setInterval(() => { + this.data = 2; + }, 500); } } diff --git a/packages/examples/core/ts/metadata/directives.ts b/packages/examples/core/ts/metadata/directives.ts index 49aa0ea6b1bab..9443de82d0da6 100644 --- a/packages/examples/core/ts/metadata/directives.ts +++ b/packages/examples/core/ts/metadata/directives.ts @@ -60,8 +60,12 @@ export class IntervalDirComponent { ` }) export class MyOutputComponent { - onEverySecond() { console.log('second'); } - onEveryFiveSeconds() { console.log('five seconds'); } + onEverySecond() { + console.log('second'); + } + onEveryFiveSeconds() { + console.log('five seconds'); + } } // #enddocregion component-output-interval diff --git a/packages/examples/core/ts/metadata/lifecycle_hooks_spec.ts b/packages/examples/core/ts/metadata/lifecycle_hooks_spec.ts index a4a0d76b5963f..7eac18007ccfb 100644 --- a/packages/examples/core/ts/metadata/lifecycle_hooks_spec.ts +++ b/packages/examples/core/ts/metadata/lifecycle_hooks_spec.ts @@ -10,142 +10,143 @@ import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, import {TestBed} from '@angular/core/testing'; (function() { - describe('lifecycle hooks examples', () => { - it('should work with ngOnInit', () => { - // #docregion OnInit - @Component({selector: 'my-cmp', template: `...`}) - class MyComponent implements OnInit { - ngOnInit() { - // ... - } +describe('lifecycle hooks examples', () => { + it('should work with ngOnInit', () => { + // #docregion OnInit + @Component({selector: 'my-cmp', template: `...`}) + class MyComponent implements OnInit { + ngOnInit() { + // ... } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngOnInit', []]]); - }); - - it('should work with ngDoCheck', () => { - // #docregion DoCheck - @Component({selector: 'my-cmp', template: `...`}) - class MyComponent implements DoCheck { - ngDoCheck() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngDoCheck', []]]); - }); - - it('should work with ngAfterContentChecked', () => { - // #docregion AfterContentChecked - @Component({selector: 'my-cmp', template: `...`}) - class MyComponent implements AfterContentChecked { - ngAfterContentChecked() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentChecked', []]]); - }); - - it('should work with ngAfterContentInit', () => { - // #docregion AfterContentInit - @Component({selector: 'my-cmp', template: `...`}) - class MyComponent implements AfterContentInit { - ngAfterContentInit() { - // ... - } + } + // #enddocregion + + expect(createAndLogComponent(MyComponent)).toEqual([['ngOnInit', []]]); + }); + + it('should work with ngDoCheck', () => { + // #docregion DoCheck + @Component({selector: 'my-cmp', template: `...`}) + class MyComponent implements DoCheck { + ngDoCheck() { + // ... } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentInit', []]]); - }); - - it('should work with ngAfterViewChecked', () => { - // #docregion AfterViewChecked - @Component({selector: 'my-cmp', template: `...`}) - class MyComponent implements AfterViewChecked { - ngAfterViewChecked() { - // ... - } + } + // #enddocregion + + expect(createAndLogComponent(MyComponent)).toEqual([['ngDoCheck', []]]); + }); + + it('should work with ngAfterContentChecked', () => { + // #docregion AfterContentChecked + @Component({selector: 'my-cmp', template: `...`}) + class MyComponent implements AfterContentChecked { + ngAfterContentChecked() { + // ... } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewChecked', []]]); - }); - - it('should work with ngAfterViewInit', () => { - // #docregion AfterViewInit - @Component({selector: 'my-cmp', template: `...`}) - class MyComponent implements AfterViewInit { - ngAfterViewInit() { - // ... - } + } + // #enddocregion + + expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentChecked', []]]); + }); + + it('should work with ngAfterContentInit', () => { + // #docregion AfterContentInit + @Component({selector: 'my-cmp', template: `...`}) + class MyComponent implements AfterContentInit { + ngAfterContentInit() { + // ... } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewInit', []]]); - }); - - it('should work with ngOnDestroy', () => { - // #docregion OnDestroy - @Component({selector: 'my-cmp', template: `...`}) - class MyComponent implements OnDestroy { - ngOnDestroy() { - // ... - } + } + // #enddocregion + + expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentInit', []]]); + }); + + it('should work with ngAfterViewChecked', () => { + // #docregion AfterViewChecked + @Component({selector: 'my-cmp', template: `...`}) + class MyComponent implements AfterViewChecked { + ngAfterViewChecked() { + // ... } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngOnDestroy', []]]); - }); - - it('should work with ngOnChanges', () => { - // #docregion OnChanges - @Component({selector: 'my-cmp', template: `...`}) - class MyComponent implements OnChanges { - // TODO(issue/24571): remove '!'. - @Input() - prop !: number; - - ngOnChanges(changes: SimpleChanges) { - // changes.prop contains the old and the new value... - } + } + // #enddocregion + + expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewChecked', []]]); + }); + + it('should work with ngAfterViewInit', () => { + // #docregion AfterViewInit + @Component({selector: 'my-cmp', template: `...`}) + class MyComponent implements AfterViewInit { + ngAfterViewInit() { + // ... } - // #enddocregion - - const log = createAndLogComponent(MyComponent, ['prop']); - expect(log.length).toBe(1); - expect(log[0][0]).toBe('ngOnChanges'); - const changes: SimpleChanges = log[0][1][0]; - expect(changes['prop'].currentValue).toBe(true); - }); + } + // #enddocregion + + expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewInit', []]]); }); - function createAndLogComponent(clazz: Type<any>, inputs: string[] = []): any[] { - const log: any[] = []; - createLoggingSpiesFromProto(clazz, log); + it('should work with ngOnDestroy', () => { + // #docregion OnDestroy + @Component({selector: 'my-cmp', template: `...`}) + class MyComponent implements OnDestroy { + ngOnDestroy() { + // ... + } + } + // #enddocregion + + expect(createAndLogComponent(MyComponent)).toEqual([['ngOnDestroy', []]]); + }); - const inputBindings = inputs.map(input => `[${input}] = true`).join(' '); + it('should work with ngOnChanges', () => { + // #docregion OnChanges + @Component({selector: 'my-cmp', template: `...`}) + class MyComponent implements OnChanges { + // TODO(issue/24571): remove '!'. + @Input() prop!: number; - @Component({template: `<my-cmp ${inputBindings}></my-cmp>`}) - class ParentComponent { + ngOnChanges(changes: SimpleChanges) { + // changes.prop contains the old and the new value... + } } + // #enddocregion + const log = createAndLogComponent(MyComponent, ['prop']); + expect(log.length).toBe(1); + expect(log[0][0]).toBe('ngOnChanges'); + const changes: SimpleChanges = log[0][1][0]; + expect(changes['prop'].currentValue).toBe(true); + }); +}); - const fixture = TestBed.configureTestingModule({declarations: [ParentComponent, clazz]}) - .createComponent(ParentComponent); - fixture.detectChanges(); - fixture.destroy(); - return log; - } +function createAndLogComponent(clazz: Type<any>, inputs: string[] = []): any[] { + const log: any[] = []; + createLoggingSpiesFromProto(clazz, log); + + const inputBindings = inputs.map(input => `[${input}] = true`).join(' '); - function createLoggingSpiesFromProto(clazz: Type<any>, log: any[]) { - const proto = clazz.prototype; - Object.keys(proto).forEach((method) => { - proto[method] = (...args: any[]) => { log.push([method, args]); }; - }); + @Component({template: `<my-cmp ${inputBindings}></my-cmp>`}) + class ParentComponent { } + + + const fixture = TestBed.configureTestingModule({declarations: [ParentComponent, clazz]}) + .createComponent(ParentComponent); + fixture.detectChanges(); + fixture.destroy(); + return log; +} + +function createLoggingSpiesFromProto(clazz: Type<any>, log: any[]) { + const proto = clazz.prototype; + Object.keys(proto).forEach((method) => { + proto[method] = (...args: any[]) => { + log.push([method, args]); + }; + }); +} })(); diff --git a/packages/examples/core/ts/metadata/metadata.ts b/packages/examples/core/ts/metadata/metadata.ts index 179436741e4a9..9d7bdb02d1e26 100644 --- a/packages/examples/core/ts/metadata/metadata.ts +++ b/packages/examples/core/ts/metadata/metadata.ts @@ -21,7 +21,9 @@ class Greet { @Component({selector: 'page', template: 'Title: {{title}}'}) class Page { title: string; - constructor(@Attribute('title') title: string) { this.title = title; } + constructor(@Attribute('title') title: string) { + this.title = title; + } } // #enddocregion @@ -46,6 +48,8 @@ class InputDirective { // #docregion pipe @Pipe({name: 'lowercase'}) class Lowercase { - transform(v: string, args: any[]) { return v.toLowerCase(); } + transform(v: string, args: any[]) { + return v.toLowerCase(); + } } // #enddocregion diff --git a/packages/examples/core/ts/prod_mode/prod_mode_example.ts b/packages/examples/core/ts/prod_mode/prod_mode_example.ts index 34cc039b4e7dc..d71b801a4bd09 100644 --- a/packages/examples/core/ts/prod_mode/prod_mode_example.ts +++ b/packages/examples/core/ts/prod_mode/prod_mode_example.ts @@ -7,9 +7,10 @@ */ // #docregion enableProdMode -import {NgModule, enableProdMode} from '@angular/core'; +import {enableProdMode, NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; + import {MyComponent} from './my_component'; enableProdMode(); diff --git a/packages/examples/forms/ts/formBuilder/e2e_test/form_builder_spec.ts b/packages/examples/forms/ts/formBuilder/e2e_test/form_builder_spec.ts index 03c964d0c6bc9..f7147df783382 100644 --- a/packages/examples/forms/ts/formBuilder/e2e_test/form_builder_spec.ts +++ b/packages/examples/forms/ts/formBuilder/e2e_test/form_builder_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('formBuilder example', () => { @@ -33,5 +34,4 @@ describe('formBuilder example', () => { inputs.get(0).sendKeys('a'); expect(paragraphs.get(1).getText()).toEqual('Validation status: INVALID'); }); - }); diff --git a/packages/examples/forms/ts/nestedFormArray/e2e_test/nested_form_array_spec.ts b/packages/examples/forms/ts/nestedFormArray/e2e_test/nested_form_array_spec.ts index 9baaac992c6f2..8f67dfd19d50d 100644 --- a/packages/examples/forms/ts/nestedFormArray/e2e_test/nested_form_array_spec.ts +++ b/packages/examples/forms/ts/nestedFormArray/e2e_test/nested_form_array_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('nestedFormArray example', () => { @@ -39,5 +40,4 @@ describe('nestedFormArray example', () => { expect(inputs.get(0).getAttribute('value')).toEqual('LA'); expect(inputs.get(1).getAttribute('value')).toEqual('MTV'); }); - }); diff --git a/packages/examples/forms/ts/nestedFormArray/nested_form_array_example.ts b/packages/examples/forms/ts/nestedFormArray/nested_form_array_example.ts index 1554c7ef17831..6713e04ac904b 100644 --- a/packages/examples/forms/ts/nestedFormArray/nested_form_array_example.ts +++ b/packages/examples/forms/ts/nestedFormArray/nested_form_array_example.ts @@ -35,15 +35,21 @@ export class NestedFormArray { ]), }); - get cities(): FormArray { return this.form.get('cities') as FormArray; } + get cities(): FormArray { + return this.form.get('cities') as FormArray; + } - addCity() { this.cities.push(new FormControl()); } + addCity() { + this.cities.push(new FormControl()); + } onSubmit() { console.log(this.cities.value); // ['SF', 'NY'] console.log(this.form.value); // { cities: ['SF', 'NY'] } } - setPreset() { this.cities.patchValue(['LA', 'MTV']); } + setPreset() { + this.cities.patchValue(['LA', 'MTV']); + } } // #enddocregion diff --git a/packages/examples/forms/ts/nestedFormGroup/e2e_test/nested_form_group_spec.ts b/packages/examples/forms/ts/nestedFormGroup/e2e_test/nested_form_group_spec.ts index a3fb76047c9f4..3049a4cd6cb02 100644 --- a/packages/examples/forms/ts/nestedFormGroup/e2e_test/nested_form_group_spec.ts +++ b/packages/examples/forms/ts/nestedFormGroup/e2e_test/nested_form_group_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('nestedFormGroup example', () => { @@ -40,5 +41,4 @@ describe('nestedFormGroup example', () => { expect(firstInput.getAttribute('value')).toEqual('Bess'); expect(lastInput.getAttribute('value')).toEqual('Marvin'); }); - }); diff --git a/packages/examples/forms/ts/nestedFormGroup/module.ts b/packages/examples/forms/ts/nestedFormGroup/module.ts index 0d5503f1ae55e..851450eb5eb3b 100644 --- a/packages/examples/forms/ts/nestedFormGroup/module.ts +++ b/packages/examples/forms/ts/nestedFormGroup/module.ts @@ -1,10 +1,10 @@ /** - * @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 - */ + * @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 {NgModule} from '@angular/core'; import {ReactiveFormsModule} from '@angular/forms'; diff --git a/packages/examples/forms/ts/nestedFormGroup/nested_form_group_example.ts b/packages/examples/forms/ts/nestedFormGroup/nested_form_group_example.ts index 38c61535b8716..d5e56370dcc11 100644 --- a/packages/examples/forms/ts/nestedFormGroup/nested_form_group_example.ts +++ b/packages/examples/forms/ts/nestedFormGroup/nested_form_group_example.ts @@ -37,9 +37,13 @@ export class NestedFormGroupComp { email: new FormControl() }); - get first(): any { return this.form.get('name.first'); } + get first(): any { + return this.form.get('name.first'); + } - get name(): any { return this.form.get('name'); } + get name(): any { + return this.form.get('name'); + } onSubmit() { console.log(this.first.value); // 'Nancy' @@ -48,6 +52,8 @@ export class NestedFormGroupComp { console.log(this.form.status); // VALID } - setPreset() { this.name.setValue({first: 'Bess', last: 'Marvin'}); } + setPreset() { + this.name.setValue({first: 'Bess', last: 'Marvin'}); + } } // #enddocregion diff --git a/packages/examples/forms/ts/ngModelGroup/e2e_test/ng_model_group_spec.ts b/packages/examples/forms/ts/ngModelGroup/e2e_test/ng_model_group_spec.ts index 6d700e69ee422..e27a692309f98 100644 --- a/packages/examples/forms/ts/ngModelGroup/e2e_test/ng_model_group_spec.ts +++ b/packages/examples/forms/ts/ngModelGroup/e2e_test/ng_model_group_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('ngModelGroup example', () => { @@ -38,5 +39,4 @@ describe('ngModelGroup example', () => { expect(inputs.get(0).getAttribute('value')).toEqual('Bess'); expect(inputs.get(1).getAttribute('value')).toEqual('Marvin'); }); - }); diff --git a/packages/examples/forms/ts/ngModelGroup/ng_model_group_example.ts b/packages/examples/forms/ts/ngModelGroup/ng_model_group_example.ts index fe794843f153a..a0f6b853b4221 100644 --- a/packages/examples/forms/ts/ngModelGroup/ng_model_group_example.ts +++ b/packages/examples/forms/ts/ngModelGroup/ng_model_group_example.ts @@ -37,6 +37,8 @@ export class NgModelGroupComp { console.log(f.valid); // true } - setValue() { this.name = {first: 'Bess', last: 'Marvin'}; } + setValue() { + this.name = {first: 'Bess', last: 'Marvin'}; + } } // #enddocregion diff --git a/packages/examples/forms/ts/radioButtons/e2e_test/radio_button_spec.ts b/packages/examples/forms/ts/radioButtons/e2e_test/radio_button_spec.ts index 56f668ec14499..8976d9b77c5eb 100644 --- a/packages/examples/forms/ts/radioButtons/e2e_test/radio_button_spec.ts +++ b/packages/examples/forms/ts/radioButtons/e2e_test/radio_button_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('radioButtons example', () => { diff --git a/packages/examples/forms/ts/reactiveRadioButtons/e2e_test/reactive_radio_button_spec.ts b/packages/examples/forms/ts/reactiveRadioButtons/e2e_test/reactive_radio_button_spec.ts index 4b23a8ffd5996..14f4870d2ad5a 100644 --- a/packages/examples/forms/ts/reactiveRadioButtons/e2e_test/reactive_radio_button_spec.ts +++ b/packages/examples/forms/ts/reactiveRadioButtons/e2e_test/reactive_radio_button_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('radioButtons example', () => { diff --git a/packages/examples/forms/ts/reactiveSelectControl/e2e_test/reactive_select_control_spec.ts b/packages/examples/forms/ts/reactiveSelectControl/e2e_test/reactive_select_control_spec.ts index 1da4d5eff0b10..a3b406eef7979 100644 --- a/packages/examples/forms/ts/reactiveSelectControl/e2e_test/reactive_select_control_spec.ts +++ b/packages/examples/forms/ts/reactiveSelectControl/e2e_test/reactive_select_control_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('reactiveSelectControl example', () => { @@ -33,5 +34,4 @@ describe('reactiveSelectControl example', () => { expect(p.getText()).toEqual('Form value: { "state": { "name": "Arizona", "abbrev": "AZ" } }'); }); - }); diff --git a/packages/examples/forms/ts/selectControl/e2e_test/select_control_spec.ts b/packages/examples/forms/ts/selectControl/e2e_test/select_control_spec.ts index 2fbbc22fd308d..53397400b6347 100644 --- a/packages/examples/forms/ts/selectControl/e2e_test/select_control_spec.ts +++ b/packages/examples/forms/ts/selectControl/e2e_test/select_control_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('selectControl example', () => { @@ -22,8 +23,9 @@ describe('selectControl example', () => { p = element(by.css('p')); }); - it('should initially select the placeholder option', - () => { expect(options.get(0).getAttribute('selected')).toBe('true'); }); + it('should initially select the placeholder option', () => { + expect(options.get(0).getAttribute('selected')).toBe('true'); + }); it('should update the model when the value changes in the UI', () => { select.click(); @@ -31,5 +33,4 @@ describe('selectControl example', () => { expect(p.getText()).toEqual('Form value: { "state": { "name": "Arizona", "abbrev": "AZ" } }'); }); - }); diff --git a/packages/examples/forms/ts/simpleForm/e2e_test/simple_form_spec.ts b/packages/examples/forms/ts/simpleForm/e2e_test/simple_form_spec.ts index 0588a5882de98..a97431bf33902 100644 --- a/packages/examples/forms/ts/simpleForm/e2e_test/simple_form_spec.ts +++ b/packages/examples/forms/ts/simpleForm/e2e_test/simple_form_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('simpleForm example', () => { @@ -40,5 +41,4 @@ describe('simpleForm example', () => { expect(paragraphs.get(1).getText()).toEqual('First name valid: true'); expect(paragraphs.get(3).getText()).toEqual('Form valid: true'); }); - }); diff --git a/packages/examples/forms/ts/simpleFormControl/e2e_test/simple_form_control_spec.ts b/packages/examples/forms/ts/simpleFormControl/e2e_test/simple_form_control_spec.ts index 810064705c6cd..240e3df9dc93d 100644 --- a/packages/examples/forms/ts/simpleFormControl/e2e_test/simple_form_control_spec.ts +++ b/packages/examples/forms/ts/simpleFormControl/e2e_test/simple_form_control_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('simpleFormControl example', () => { @@ -49,6 +50,5 @@ describe('simpleFormControl example', () => { element(by.css('button')).click(); expect(input.getAttribute('value')).toEqual('new value'); }); - }); }); diff --git a/packages/examples/forms/ts/simpleFormControl/simple_form_control_example.ts b/packages/examples/forms/ts/simpleFormControl/simple_form_control_example.ts index cd799034c7bf8..782e89b79d926 100644 --- a/packages/examples/forms/ts/simpleFormControl/simple_form_control_example.ts +++ b/packages/examples/forms/ts/simpleFormControl/simple_form_control_example.ts @@ -24,6 +24,8 @@ import {FormControl, Validators} from '@angular/forms'; export class SimpleFormControl { control: FormControl = new FormControl('value', Validators.minLength(2)); - setValue() { this.control.setValue('new value'); } + setValue() { + this.control.setValue('new value'); + } } // #enddocregion diff --git a/packages/examples/forms/ts/simpleFormGroup/e2e_test/simple_form_group_spec.ts b/packages/examples/forms/ts/simpleFormGroup/e2e_test/simple_form_group_spec.ts index 76fdf8e7c6493..9bca0217d216e 100644 --- a/packages/examples/forms/ts/simpleFormGroup/e2e_test/simple_form_group_spec.ts +++ b/packages/examples/forms/ts/simpleFormGroup/e2e_test/simple_form_group_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../test-utils'; describe('formControlName example', () => { @@ -40,6 +41,5 @@ describe('formControlName example', () => { expect(firstInput.getAttribute('value')).toEqual('Carson'); expect(lastInput.getAttribute('value')).toEqual('Drew'); }); - }); }); diff --git a/packages/examples/forms/ts/simpleFormGroup/simple_form_group_example.ts b/packages/examples/forms/ts/simpleFormGroup/simple_form_group_example.ts index 7ca0d21abe4ac..15dced1bcd133 100644 --- a/packages/examples/forms/ts/simpleFormGroup/simple_form_group_example.ts +++ b/packages/examples/forms/ts/simpleFormGroup/simple_form_group_example.ts @@ -31,13 +31,17 @@ export class SimpleFormGroup { last: new FormControl('Drew'), }); - get first(): any { return this.form.get('first'); } + get first(): any { + return this.form.get('first'); + } onSubmit(): void { console.log(this.form.value); // {first: 'Nancy', last: 'Drew'} } - setValue() { this.form.setValue({first: 'Carson', last: 'Drew'}); } + setValue() { + this.form.setValue({first: 'Carson', last: 'Drew'}); + } } diff --git a/packages/examples/forms/ts/simpleNgModel/e2e_test/simple_ng_model_spec.ts b/packages/examples/forms/ts/simpleNgModel/e2e_test/simple_ng_model_spec.ts index 6241b2e2b92c9..4791139f5009e 100644 --- a/packages/examples/forms/ts/simpleNgModel/e2e_test/simple_ng_model_spec.ts +++ b/packages/examples/forms/ts/simpleNgModel/e2e_test/simple_ng_model_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder, ElementFinder} from 'protractor'; import {verifyNoBrowserErrors} from '../../../../test-utils'; @@ -42,5 +42,4 @@ describe('simpleNgModel example', () => { button.click(); expect(input.getAttribute('value')).toEqual('Nancy'); }); - }); diff --git a/packages/examples/forms/ts/simpleNgModel/simple_ng_model_example.ts b/packages/examples/forms/ts/simpleNgModel/simple_ng_model_example.ts index 3b0c068d54460..50775cec1672f 100644 --- a/packages/examples/forms/ts/simpleNgModel/simple_ng_model_example.ts +++ b/packages/examples/forms/ts/simpleNgModel/simple_ng_model_example.ts @@ -23,6 +23,8 @@ import {Component} from '@angular/core'; export class SimpleNgModelComp { name: string = ''; - setValue() { this.name = 'Nancy'; } + setValue() { + this.name = 'Nancy'; + } } // #enddocregion diff --git a/packages/examples/platform-browser/dom/debug/ts/by/by.ts b/packages/examples/platform-browser/dom/debug/ts/by/by.ts index 70283ebda742f..4bdffd8585ecf 100644 --- a/packages/examples/platform-browser/dom/debug/ts/by/by.ts +++ b/packages/examples/platform-browser/dom/debug/ts/by/by.ts @@ -9,7 +9,7 @@ import {DebugElement} from '@angular/core'; import {By} from '@angular/platform-browser'; -let debugElement: DebugElement = undefined !; +let debugElement: DebugElement = undefined!; class MyDirective {} // #docregion by_all diff --git a/packages/examples/test-utils/index.ts b/packages/examples/test-utils/index.ts index 4bf0dd2b5d03f..61d4c395083fb 100644 --- a/packages/examples/test-utils/index.ts +++ b/packages/examples/test-utils/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ /* tslint:disable:no-console */ -import {WebDriver, logging} from 'selenium-webdriver'; +import {logging, WebDriver} from 'selenium-webdriver'; declare var browser: WebDriver; declare var expect: any; diff --git a/packages/examples/testing/ts/testing.ts b/packages/examples/testing/ts/testing.ts index ac193163a224e..c859f842170f0 100644 --- a/packages/examples/testing/ts/testing.ts +++ b/packages/examples/testing/ts/testing.ts @@ -13,68 +13,88 @@ class MyMockService implements MyService {} // #docregion describeIt describe('some component', () => { - it('does something', () => { - // This is a test. - }); + it('does something', + () => { + // This is a test. + }); }); // #enddocregion // #docregion fdescribe /* tslint:disable-next-line:no-jasmine-focus */ fdescribe('some component', () => { - it('has a test', () => { - // This test will run. - }); + it('has a test', + () => { + // This test will run. + }); }); describe('another component', () => { - it('also has a test', () => { throw 'This test will not run.'; }); + it('also has a test', () => { + throw 'This test will not run.'; + }); }); // #enddocregion // #docregion xdescribe -xdescribe( - 'some component', () => { it('has a test', () => { throw 'This test will not run.'; }); }); +xdescribe('some component', () => { + it('has a test', () => { + throw 'This test will not run.'; + }); +}); describe('another component', () => { - it('also has a test', () => { - // This test will run. - }); + it('also has a test', + () => { + // This test will run. + }); }); // #enddocregion // #docregion fit describe('some component', () => { /* tslint:disable-next-line:no-jasmine-focus */ - fit('has a test', () => { - // This test will run. - }); - it('has another test', () => { throw 'This test will not run.'; }); + fit('has a test', + () => { + // This test will run. + }); + it('has another test', () => { + throw 'This test will not run.'; + }); }); // #enddocregion // #docregion xit describe('some component', () => { - xit('has a test', () => { throw 'This test will not run.'; }); - it('has another test', () => { - // This test will run. - }); + xit('has a test', () => { + throw 'This test will not run.'; + }); + it('has another test', + () => { + // This test will run. + }); }); // #enddocregion // #docregion beforeEach describe('some component', () => { - beforeEach(() => { db.connect(); }); - it('uses the db', () => { - // Database is connected. - }); + beforeEach(() => { + db.connect(); + }); + it('uses the db', + () => { + // Database is connected. + }); }); // #enddocregion // #docregion afterEach describe('some component', () => { - afterEach((done: Function) => { db.reset().then((_: any) => done()); }); - it('uses the db', () => { - // This test can leave the database in a dirty state. - // The afterEach will ensure it gets reset. - }); + afterEach((done: Function) => { + db.reset().then((_: any) => done()); + }); + it('uses the db', + () => { + // This test can leave the database in a dirty state. + // The afterEach will ensure it gets reset. + }); }); // #enddocregion diff --git a/packages/examples/upgrade/static/ts/full/module.spec.ts b/packages/examples/upgrade/static/ts/full/module.spec.ts index 1a51f76aa208e..2f6db0bdb9595 100644 --- a/packages/examples/upgrade/static/ts/full/module.spec.ts +++ b/packages/examples/upgrade/static/ts/full/module.spec.ts @@ -10,13 +10,12 @@ import {TestBed} from '@angular/core/testing'; import {createAngularJSTestingModule, createAngularTestingModule} from '@angular/upgrade/static/testing'; -import {HeroesService, Ng2AppModule, ng1AppModule} from './module'; +import {HeroesService, ng1AppModule, Ng2AppModule} from './module'; const {module, inject} = (window as any).angular.mock; // #enddocregion angular-setup describe('HeroesService (from Angular)', () => { - // #docregion angular-setup beforeEach(() => { TestBed.configureTestingModule( @@ -40,7 +39,8 @@ describe('HeroesService (from AngularJS)', () => { // #enddocregion angularjs-setup // #docregion angularjs-spec - it('should have access to the HeroesService', - inject((heroesService: HeroesService) => { expect(heroesService).toBeDefined(); })); + it('should have access to the HeroesService', inject((heroesService: HeroesService) => { + expect(heroesService).toBeDefined(); + })); // #enddocregion angularjs-spec }); diff --git a/packages/examples/upgrade/static/ts/full/module.ts b/packages/examples/upgrade/static/ts/full/module.ts index 2e908f0ed0940..8c84f4787a5b8 100644 --- a/packages/examples/upgrade/static/ts/full/module.ts +++ b/packages/examples/upgrade/static/ts/full/module.ts @@ -9,7 +9,7 @@ import {Component, Directive, ElementRef, EventEmitter, Injectable, Injector, Input, NgModule, Output} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import {UpgradeComponent, UpgradeModule, downgradeComponent, downgradeInjectable} from '@angular/upgrade/static'; +import {downgradeComponent, downgradeInjectable, UpgradeComponent, UpgradeModule} from '@angular/upgrade/static'; declare var angular: ng.IAngularStatic; @@ -20,7 +20,9 @@ export interface Hero { // #docregion ng1-text-formatter-service export class TextFormatter { - titleCase(value: string) { return value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase()); } + titleCase(value: string) { + return value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase()); + } } // #enddocregion @@ -39,7 +41,7 @@ export class TextFormatter { <button (click)="addHero.emit()">Add Hero</button>`, }) export class Ng2HeroesComponent { - @Input() heroes !: Hero[]; + @Input() heroes!: Hero[]; @Output() addHero = new EventEmitter(); @Output() removeHero = new EventEmitter(); } @@ -67,7 +69,9 @@ export class HeroesService { this.heroes.concat([{name: 'Kamala Khan', description: 'Epic shape-shifting healer'}]); } - removeHero(hero: Hero) { this.heroes = this.heroes.filter((item: Hero) => item !== hero); } + removeHero(hero: Hero) { + this.heroes = this.heroes.filter((item: Hero) => item !== hero); + } } // #enddocregion @@ -77,8 +81,8 @@ export class HeroesService { export class Ng1HeroComponentWrapper extends UpgradeComponent { // The names of the input and output properties here must match the names of the // `<` and `&` bindings in the AngularJS component that is being wrapped - @Input() hero !: Hero; - @Output() onRemove !: EventEmitter<void>; + @Input() hero!: Hero; + @Output() onRemove!: EventEmitter<void>; constructor(elementRef: ElementRef, injector: Injector) { // We must pass the name of the directive as used by AngularJS to the super @@ -159,7 +163,10 @@ ng1AppModule.component('exampleApp', { // (We don't need the `HeroesService` type for AngularJS DI - it just helps with TypeScript // compilation) controller: [ - 'heroesService', function(heroesService: HeroesService) { this.heroesService = heroesService; } + 'heroesService', + function(heroesService: HeroesService) { + this.heroesService = heroesService; + } ], // This template makes use of the downgraded `ng2-heroes` component // Note that because its element is compiled by AngularJS we must use kebab-case attributes diff --git a/packages/examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts b/packages/examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts index 9a7c6e45d2b19..bb493e641a589 100644 --- a/packages/examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts +++ b/packages/examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts @@ -24,6 +24,7 @@ describe('upgrade/static (lite with multiple downgraded modules and shared root expect(compB.getText()).toBe('Component B (Service ID: 2)'); }); - it('should use a different injectable instance on downgraded module C', - () => { expect(compC.getText()).toBe('Component C (Service ID: 1)'); }); + it('should use a different injectable instance on downgraded module C', () => { + expect(compC.getText()).toBe('Component C (Service ID: 1)'); + }); }); diff --git a/packages/examples/upgrade/static/ts/lite-multi-shared/module.ts b/packages/examples/upgrade/static/ts/lite-multi-shared/module.ts index 0024f14850a98..48ecc21156366 100644 --- a/packages/examples/upgrade/static/ts/lite-multi-shared/module.ts +++ b/packages/examples/upgrade/static/ts/lite-multi-shared/module.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Compiler, Component, Injectable, Injector, NgModule, StaticProvider, getPlatform} from '@angular/core'; +import {Compiler, Component, getPlatform, Injectable, Injector, NgModule, StaticProvider} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {downgradeComponent, downgradeModule} from '@angular/upgrade/static'; @@ -100,12 +100,12 @@ const getRootInjector = (extraProviders: StaticProvider[]) => { return rootInjectorPromise; }; -const downgradedNg2AModule = downgradeModule(async(extraProviders: StaticProvider[]) => { +const downgradedNg2AModule = downgradeModule(async (extraProviders: StaticProvider[]) => { const rootInjector = await getRootInjector(extraProviders); const moduleAFactory = await rootInjector.get(Compiler).compileModuleAsync(Ng2AModule); return moduleAFactory.create(rootInjector); }); -const downgradedNg2BModule = downgradeModule(async(extraProviders: StaticProvider[]) => { +const downgradedNg2BModule = downgradeModule(async (extraProviders: StaticProvider[]) => { const rootInjector = await getRootInjector(extraProviders); const moduleBFactory = await rootInjector.get(Compiler).compileModuleAsync(Ng2BModule); return moduleBFactory.create(rootInjector); diff --git a/packages/examples/upgrade/static/ts/lite-multi/module.ts b/packages/examples/upgrade/static/ts/lite-multi/module.ts index 9f7468bbcb86f..efe7bf38895db 100644 --- a/packages/examples/upgrade/static/ts/lite-multi/module.ts +++ b/packages/examples/upgrade/static/ts/lite-multi/module.ts @@ -7,10 +7,10 @@ */ // #docplaster -import {Component, Directive, ElementRef, Injectable, Injector, NgModule, StaticProvider, getPlatform} from '@angular/core'; +import {Component, Directive, ElementRef, getPlatform, Injectable, Injector, NgModule, StaticProvider} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import {UpgradeComponent, downgradeComponent, downgradeInjectable, downgradeModule} from '@angular/upgrade/static'; +import {downgradeComponent, downgradeInjectable, downgradeModule, UpgradeComponent} from '@angular/upgrade/static'; declare var angular: ng.IAngularStatic; @@ -28,12 +28,16 @@ export class Ng2AComponent { selector: 'ng1A', }) export class Ng1AComponentFacade extends UpgradeComponent { - constructor(elementRef: ElementRef, injector: Injector) { super('ng1A', elementRef, injector); } + constructor(elementRef: ElementRef, injector: Injector) { + super('ng1A', elementRef, injector); + } } @Injectable() export class Ng2AService { - getValue() { return 'ng2'; } + getValue() { + return 'ng2'; + } } @NgModule({ @@ -91,13 +95,17 @@ const appModule = <ng2-b ng-switch-when="B"></ng2-b> </main> `, - controller: class ExampleAppController{page = 'A';}, + controller: class ExampleAppController { + page = 'A'; + }, }) .component('ng1A', { template: 'ng1({{ $ctrl.value }})', controller: [ - 'ng2AService', class Ng1AController{ - value = this.ng2AService.getValue(); constructor(private ng2AService: Ng2AService) {} + 'ng2AService', + class Ng1AController { + value = this.ng2AService.getValue(); + constructor(private ng2AService: Ng2AService) {} } ], }) diff --git a/packages/examples/upgrade/static/ts/lite/e2e_test/e2e_util.ts b/packages/examples/upgrade/static/ts/lite/e2e_test/e2e_util.ts index c429b50000240..778e7fd35d9ed 100644 --- a/packages/examples/upgrade/static/ts/lite/e2e_test/e2e_util.ts +++ b/packages/examples/upgrade/static/ts/lite/e2e_test/e2e_util.ts @@ -6,14 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementFinder, by} from 'protractor'; +import {by, ElementFinder} from 'protractor'; declare global { namespace jasmine { - interface Matchers<T> { - toBeAHero(): Promise<void>; - toHaveName(exectedName: string): Promise<void>; - } + interface Matchers<T> { + toBeAHero(): Promise<void>; + toHaveName(exectedName: string): Promise<void>; + } } } @@ -22,43 +22,41 @@ const isTitleCased = (text: string) => export function addCustomMatchers() { jasmine.addMatchers({ - toBeAHero: - () => ({ - compare(actualNg1Hero: ElementFinder | undefined) { - const getText = (selector: string) => - actualNg1Hero !.element(by.css(selector)).getText(); - const result = { - message: 'Expected undefined to be an `ng1Hero` ElementFinder.', - pass: !!actualNg1Hero && - Promise.all(['.title', 'h2', 'p'].map(getText) as PromiseLike<string>[]) - .then(([actualTitle, actualName, actualDescription]) => { - const pass = (actualTitle === 'Super Hero') && isTitleCased(actualName) && - (actualDescription.length > 0); + toBeAHero: () => ({ + compare(actualNg1Hero: ElementFinder|undefined) { + const getText = (selector: string) => actualNg1Hero!.element(by.css(selector)).getText(); + const result = { + message: 'Expected undefined to be an `ng1Hero` ElementFinder.', + pass: !!actualNg1Hero && + Promise.all(['.title', 'h2', 'p'].map(getText) as PromiseLike<string>[]) + .then(([actualTitle, actualName, actualDescription]) => { + const pass = (actualTitle === 'Super Hero') && isTitleCased(actualName) && + (actualDescription.length > 0); - const actualHero = - `Hero(${actualTitle}, ${actualName}, ${actualDescription})`; - result.message = - `Expected ${actualHero}'${pass ? ' not' : ''} to be a real hero.`; + const actualHero = `Hero(${actualTitle}, ${actualName}, ${actualDescription})`; + result.message = + `Expected ${actualHero}'${pass ? ' not' : ''} to be a real hero.`; - return pass; - }) - }; - return result; - } - }), - toHaveName: () => ({ - compare(actualNg1Hero: ElementFinder | undefined, expectedName: string) { - const result = { - message: 'Expected undefined to be an `ng1Hero` ElementFinder.', - pass: !!actualNg1Hero && actualNg1Hero.element(by.css('h2')).getText().then(actualName => { + return pass; + }) + }; + return result; + } + }), + toHaveName: () => ({ + compare(actualNg1Hero: ElementFinder|undefined, expectedName: string) { + const result = { + message: 'Expected undefined to be an `ng1Hero` ElementFinder.', + pass: + !!actualNg1Hero && actualNg1Hero.element(by.css('h2')).getText().then(actualName => { const pass = actualName === expectedName; - result.message = - `Expected Hero(${actualName})${pass ? ' not' : ''} to have name '${expectedName}'.`; + result.message = `Expected Hero(${actualName})${pass ? ' not' : ''} to have name '${ + expectedName}'.`; return pass; }) - }; - return result; - } - }), + }; + return result; + } + }), } as any); } diff --git a/packages/examples/upgrade/static/ts/lite/e2e_test/static_lite_spec.ts b/packages/examples/upgrade/static/ts/lite/e2e_test/static_lite_spec.ts index 61bf67d64edee..cbf61a7506e04 100644 --- a/packages/examples/upgrade/static/ts/lite/e2e_test/static_lite_spec.ts +++ b/packages/examples/upgrade/static/ts/lite/e2e_test/static_lite_spec.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor'; +import {browser, by, element, ElementArrayFinder, ElementFinder} from 'protractor'; + import {verifyNoBrowserErrors} from '../../../../../test-utils'; import {addCustomMatchers} from './e2e_util'; diff --git a/packages/examples/upgrade/static/ts/lite/module.ts b/packages/examples/upgrade/static/ts/lite/module.ts index 806c8022e8f96..a99d1350d1603 100644 --- a/packages/examples/upgrade/static/ts/lite/module.ts +++ b/packages/examples/upgrade/static/ts/lite/module.ts @@ -51,7 +51,9 @@ class HeroesService { return newHero; } - removeHero(hero: Hero) { this.heroes = this.heroes.filter((item: Hero) => item !== hero); } + removeHero(hero: Hero) { + this.heroes = this.heroes.filter((item: Hero) => item !== hero); + } } @@ -103,8 +105,8 @@ class Ng2HeroesComponent { class Ng1HeroComponentWrapper extends UpgradeComponent { // The names of the input and output properties here must match the names of the // `<` and `&` bindings in the AngularJS component that is being wrapped. - @Input() hero !: Hero; - @Output() onRemove !: EventEmitter<void>; + @Input() hero!: Hero; + @Output() onRemove!: EventEmitter<void>; constructor(elementRef: ElementRef, injector: Injector) { // We must pass the name of the directive as used by AngularJS to the super. diff --git a/packages/forms/src/directives/abstract_control_directive.ts b/packages/forms/src/directives/abstract_control_directive.ts index 5c20cda5515ae..61e9bf84d9e23 100644 --- a/packages/forms/src/directives/abstract_control_directive.ts +++ b/packages/forms/src/directives/abstract_control_directive.ts @@ -31,7 +31,9 @@ export abstract class AbstractControlDirective { * @description * Reports the value of the control if it is present, otherwise null. */ - get value(): any { return this.control ? this.control.value : null; } + get value(): any { + return this.control ? this.control.value : null; + } /** * @description @@ -39,14 +41,18 @@ export abstract class AbstractControlDirective { * validation errors exist with the current value. * If the control is not present, null is returned. */ - get valid(): boolean|null { return this.control ? this.control.valid : null; } + get valid(): boolean|null { + return this.control ? this.control.valid : null; + } /** * @description * Reports whether the control is invalid, meaning that an error exists in the input value. * If the control is not present, null is returned. */ - get invalid(): boolean|null { return this.control ? this.control.invalid : null; } + get invalid(): boolean|null { + return this.control ? this.control.invalid : null; + } /** * @description @@ -54,7 +60,9 @@ export abstract class AbstractControlDirective { * errors are not yet available for the input value. If the control is not present, null is * returned. */ - get pending(): boolean|null { return this.control ? this.control.pending : null; } + get pending(): boolean|null { + return this.control ? this.control.pending : null; + } /** * @description @@ -62,41 +70,53 @@ export abstract class AbstractControlDirective { * in the UI and is exempt from validation checks and excluded from aggregate * values of ancestor controls. If the control is not present, null is returned. */ - get disabled(): boolean|null { return this.control ? this.control.disabled : null; } + get disabled(): boolean|null { + return this.control ? this.control.disabled : null; + } /** * @description * Reports whether the control is enabled, meaning that the control is included in ancestor * calculations of validity or value. If the control is not present, null is returned. */ - get enabled(): boolean|null { return this.control ? this.control.enabled : null; } + get enabled(): boolean|null { + return this.control ? this.control.enabled : null; + } /** * @description * Reports the control's validation errors. If the control is not present, null is returned. */ - get errors(): ValidationErrors|null { return this.control ? this.control.errors : null; } + get errors(): ValidationErrors|null { + return this.control ? this.control.errors : null; + } /** * @description * Reports whether the control is pristine, meaning that the user has not yet changed * the value in the UI. If the control is not present, null is returned. */ - get pristine(): boolean|null { return this.control ? this.control.pristine : null; } + get pristine(): boolean|null { + return this.control ? this.control.pristine : null; + } /** * @description * Reports whether the control is dirty, meaning that the user has changed * the value in the UI. If the control is not present, null is returned. */ - get dirty(): boolean|null { return this.control ? this.control.dirty : null; } + get dirty(): boolean|null { + return this.control ? this.control.dirty : null; + } /** * @description * Reports whether the control is touched, meaning that the user has triggered * a `blur` event on it. If the control is not present, null is returned. */ - get touched(): boolean|null { return this.control ? this.control.touched : null; } + get touched(): boolean|null { + return this.control ? this.control.touched : null; + } /** * @description @@ -104,14 +124,18 @@ export abstract class AbstractControlDirective { * 'VALID', 'INVALID', 'DISABLED', and 'PENDING'. * If the control is not present, null is returned. */ - get status(): string|null { return this.control ? this.control.status : null; } + get status(): string|null { + return this.control ? this.control.status : null; + } /** * @description * Reports whether the control is untouched, meaning that the user has not yet triggered * a `blur` event on it. If the control is not present, null is returned. */ - get untouched(): boolean|null { return this.control ? this.control.untouched : null; } + get untouched(): boolean|null { + return this.control ? this.control.untouched : null; + } /** * @description @@ -137,7 +161,9 @@ export abstract class AbstractControlDirective { * Returns an array that represents the path from the top-level form to this control. * Each index is the string name of the control on that level. */ - get path(): string[]|null { return null; } + get path(): string[]|null { + return null; + } /** * @description diff --git a/packages/forms/src/directives/abstract_form_group_directive.ts b/packages/forms/src/directives/abstract_form_group_directive.ts index 9a73f32656e4d..6d7e5caddc9d9 100644 --- a/packages/forms/src/directives/abstract_form_group_directive.ts +++ b/packages/forms/src/directives/abstract_form_group_directive.ts @@ -31,7 +31,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn * @internal */ // TODO(issue/24571): remove '!'. - _parent !: ControlContainer; + _parent!: ControlContainer; /** * @description @@ -40,7 +40,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn * @internal */ // TODO(issue/24571): remove '!'. - _validators !: any[]; + _validators!: any[]; /** * @description @@ -49,7 +49,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn * @internal */ // TODO(issue/24571): remove '!'. - _asyncValidators !: any[]; + _asyncValidators!: any[]; /** * @description @@ -58,7 +58,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn */ ngOnInit(): void { this._checkParentType(); - this.formDirective !.addFormGroup(this); + this.formDirective!.addFormGroup(this); } /** @@ -76,7 +76,9 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn * @description * The `FormGroup` bound to this directive. */ - get control(): FormGroup { return this.formDirective !.getFormGroup(this); } + get control(): FormGroup { + return this.formDirective!.getFormGroup(this); + } /** * @description @@ -90,13 +92,17 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn * @description * The top-level directive for this group if present, otherwise null. */ - get formDirective(): Form|null { return this._parent ? this._parent.formDirective : null; } + get formDirective(): Form|null { + return this._parent ? this._parent.formDirective : null; + } /** * @description * The synchronous validators registered with this group. */ - get validator(): ValidatorFn|null { return composeValidators(this._validators); } + get validator(): ValidatorFn|null { + return composeValidators(this._validators); + } /** * @description diff --git a/packages/forms/src/directives/checkbox_value_accessor.ts b/packages/forms/src/directives/checkbox_value_accessor.ts index 9a526222661bf..4a1dd17654f1f 100644 --- a/packages/forms/src/directives/checkbox_value_accessor.ts +++ b/packages/forms/src/directives/checkbox_value_accessor.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, Renderer2, forwardRef} from '@angular/core'; +import {Directive, ElementRef, forwardRef, Renderer2} from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; @@ -75,7 +75,9 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor { * * @param fn The callback function */ - registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; } + registerOnChange(fn: (_: any) => {}): void { + this.onChange = fn; + } /** * @description @@ -83,7 +85,9 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor { * * @param fn The callback function */ - registerOnTouched(fn: () => {}): void { this.onTouched = fn; } + registerOnTouched(fn: () => {}): void { + this.onTouched = fn; + } /** * Sets the "disabled" property on the input element. diff --git a/packages/forms/src/directives/control_container.ts b/packages/forms/src/directives/control_container.ts index 254d3c7827b8e..678b18f473ae9 100644 --- a/packages/forms/src/directives/control_container.ts +++ b/packages/forms/src/directives/control_container.ts @@ -23,17 +23,21 @@ export abstract class ControlContainer extends AbstractControlDirective { * The name for the control */ // TODO(issue/24571): remove '!'. - name !: string | number | null; + name!: string|number|null; /** * @description * The top-level form directive for the control. */ - get formDirective(): Form|null { return null; } + get formDirective(): Form|null { + return null; + } /** * @description * The path to this group. */ - get path(): string[]|null { return null; } + get path(): string[]|null { + return null; + } } diff --git a/packages/forms/src/directives/default_value_accessor.ts b/packages/forms/src/directives/default_value_accessor.ts index 1e946e679571f..b900b5fbe3785 100644 --- a/packages/forms/src/directives/default_value_accessor.ts +++ b/packages/forms/src/directives/default_value_accessor.ts @@ -7,7 +7,8 @@ */ import {ɵgetDOM as getDOM} from '@angular/common'; -import {Directive, ElementRef, Inject, InjectionToken, Optional, Renderer2, forwardRef} from '@angular/core'; +import {Directive, ElementRef, forwardRef, Inject, InjectionToken, Optional, Renderer2} from '@angular/core'; + import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; export const DEFAULT_VALUE_ACCESSOR: any = { @@ -112,7 +113,9 @@ export class DefaultValueAccessor implements ControlValueAccessor { * * @param fn The callback function */ - registerOnChange(fn: (_: any) => void): void { this.onChange = fn; } + registerOnChange(fn: (_: any) => void): void { + this.onChange = fn; + } /** * @description @@ -120,7 +123,9 @@ export class DefaultValueAccessor implements ControlValueAccessor { * * @param fn The callback function */ - registerOnTouched(fn: () => void): void { this.onTouched = fn; } + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } /** * Sets the "disabled" property on the input element. @@ -139,7 +144,9 @@ export class DefaultValueAccessor implements ControlValueAccessor { } /** @internal */ - _compositionStart(): void { this._composing = true; } + _compositionStart(): void { + this._composing = true; + } /** @internal */ _compositionEnd(value: any): void { diff --git a/packages/forms/src/directives/ng_control.ts b/packages/forms/src/directives/ng_control.ts index de09f7ff83d1a..47e92f6db49e0 100644 --- a/packages/forms/src/directives/ng_control.ts +++ b/packages/forms/src/directives/ng_control.ts @@ -66,7 +66,9 @@ export abstract class NgControl extends AbstractControlDirective { * * @throws An exception that this method is not implemented */ - get validator(): ValidatorFn|null { return <ValidatorFn>unimplemented(); } + get validator(): ValidatorFn|null { + return <ValidatorFn>unimplemented(); + } /** * @description @@ -74,7 +76,9 @@ export abstract class NgControl extends AbstractControlDirective { * * @throws An exception that this method is not implemented */ - get asyncValidator(): AsyncValidatorFn|null { return <AsyncValidatorFn>unimplemented(); } + get asyncValidator(): AsyncValidatorFn|null { + return <AsyncValidatorFn>unimplemented(); + } /** * @description diff --git a/packages/forms/src/directives/ng_control_status.ts b/packages/forms/src/directives/ng_control_status.ts index 735c1b5938463..12c7f5d5254d4 100644 --- a/packages/forms/src/directives/ng_control_status.ts +++ b/packages/forms/src/directives/ng_control_status.ts @@ -15,15 +15,31 @@ import {NgControl} from './ng_control'; export class AbstractControlStatus { private _cd: AbstractControlDirective; - constructor(cd: AbstractControlDirective) { this._cd = cd; } + constructor(cd: AbstractControlDirective) { + this._cd = cd; + } - get ngClassUntouched(): boolean { return this._cd.control ? this._cd.control.untouched : false; } - get ngClassTouched(): boolean { return this._cd.control ? this._cd.control.touched : false; } - get ngClassPristine(): boolean { return this._cd.control ? this._cd.control.pristine : false; } - get ngClassDirty(): boolean { return this._cd.control ? this._cd.control.dirty : false; } - get ngClassValid(): boolean { return this._cd.control ? this._cd.control.valid : false; } - get ngClassInvalid(): boolean { return this._cd.control ? this._cd.control.invalid : false; } - get ngClassPending(): boolean { return this._cd.control ? this._cd.control.pending : false; } + get ngClassUntouched(): boolean { + return this._cd.control ? this._cd.control.untouched : false; + } + get ngClassTouched(): boolean { + return this._cd.control ? this._cd.control.touched : false; + } + get ngClassPristine(): boolean { + return this._cd.control ? this._cd.control.pristine : false; + } + get ngClassDirty(): boolean { + return this._cd.control ? this._cd.control.dirty : false; + } + get ngClassValid(): boolean { + return this._cd.control ? this._cd.control.valid : false; + } + get ngClassInvalid(): boolean { + return this._cd.control ? this._cd.control.invalid : false; + } + get ngClassPending(): boolean { + return this._cd.control ? this._cd.control.pending : false; + } } export const ngControlStatusHost = { @@ -61,14 +77,16 @@ export const ngControlStatusHost = { */ @Directive({selector: '[formControlName],[ngModel],[formControl]', host: ngControlStatusHost}) export class NgControlStatus extends AbstractControlStatus { - constructor(@Self() cd: NgControl) { super(cd); } + constructor(@Self() cd: NgControl) { + super(cd); + } } /** * @description * Directive automatically applied to Angular form groups that sets CSS classes * based on control status (valid/invalid/dirty/etc). - * + * * @see `NgControlStatus` * * @ngModule ReactiveFormsModule @@ -81,5 +99,7 @@ export class NgControlStatus extends AbstractControlStatus { host: ngControlStatusHost }) export class NgControlStatusGroup extends AbstractControlStatus { - constructor(@Self() cd: ControlContainer) { super(cd); } + constructor(@Self() cd: ControlContainer) { + super(cd); + } } diff --git a/packages/forms/src/directives/ng_form.ts b/packages/forms/src/directives/ng_form.ts index 9720671b11f74..5952d0263791a 100644 --- a/packages/forms/src/directives/ng_form.ts +++ b/packages/forms/src/directives/ng_form.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AfterViewInit, Directive, EventEmitter, Inject, Input, Optional, Self, forwardRef} from '@angular/core'; +import {AfterViewInit, Directive, EventEmitter, forwardRef, Inject, Input, Optional, Self} from '@angular/core'; import {AbstractControl, FormControl, FormGroup, FormHooks} from '../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators'; @@ -96,8 +96,7 @@ const resolvedPromise = (() => Promise.resolve(null))(); outputs: ['ngSubmit'], exportAs: 'ngForm' }) -export class NgForm extends ControlContainer implements Form, - AfterViewInit { +export class NgForm extends ControlContainer implements Form, AfterViewInit { /** * @description * Returns whether the form submission has been triggered. @@ -128,7 +127,7 @@ export class NgForm extends ControlContainer implements Form, * */ // TODO(issue/24571): remove '!'. - @Input('ngFormOptions') options !: {updateOn?: FormHooks}; + @Input('ngFormOptions') options!: {updateOn?: FormHooks}; constructor( @Optional() @Self() @Inject(NG_VALIDATORS) validators: any[], @@ -142,32 +141,42 @@ export class NgForm extends ControlContainer implements Form, * @description * Lifecycle method called after the view is initialized. For internal use only. */ - ngAfterViewInit() { this._setUpdateStrategy(); } + ngAfterViewInit() { + this._setUpdateStrategy(); + } /** * @description * The directive instance. */ - get formDirective(): Form { return this; } + get formDirective(): Form { + return this; + } /** * @description * The internal `FormGroup` instance. */ - get control(): FormGroup { return this.form; } + get control(): FormGroup { + return this.form; + } /** * @description * Returns an array representing the path to this group. Because this directive * always lives at the top level of a form, it is always an empty array. */ - get path(): string[] { return []; } + get path(): string[] { + return []; + } /** * @description * Returns a map of the controls in this group. */ - get controls(): {[key: string]: AbstractControl} { return this.form.controls; } + get controls(): {[key: string]: AbstractControl} { + return this.form.controls; + } /** * @description @@ -179,7 +188,7 @@ export class NgForm extends ControlContainer implements Form, addControl(dir: NgModel): void { resolvedPromise.then(() => { const container = this._findContainer(dir.path); - (dir as{control: FormControl}).control = + (dir as {control: FormControl}).control = <FormControl>container.registerControl(dir.name, dir.control); setUpControl(dir.control, dir); dir.control.updateValueAndValidity({emitEvent: false}); @@ -193,7 +202,9 @@ export class NgForm extends ControlContainer implements Form, * * @param dir The `NgModel` directive instance. */ - getControl(dir: NgModel): FormControl { return <FormControl>this.form.get(dir.path); } + getControl(dir: NgModel): FormControl { + return <FormControl>this.form.get(dir.path); + } /** * @description @@ -248,7 +259,9 @@ export class NgForm extends ControlContainer implements Form, * * @param dir The `NgModelGroup` directive instance. */ - getFormGroup(dir: NgModelGroup): FormGroup { return <FormGroup>this.form.get(dir.path); } + getFormGroup(dir: NgModelGroup): FormGroup { + return <FormGroup>this.form.get(dir.path); + } /** * Sets the new value for the provided `NgControl` directive. @@ -258,7 +271,7 @@ export class NgForm extends ControlContainer implements Form, */ updateModel(dir: NgControl, value: any): void { resolvedPromise.then(() => { - const ctrl = <FormControl>this.form.get(dir.path !); + const ctrl = <FormControl>this.form.get(dir.path!); ctrl.setValue(value); }); } @@ -269,7 +282,9 @@ export class NgForm extends ControlContainer implements Form, * * @param value The new value */ - setValue(value: {[key: string]: any}): void { this.control.setValue(value); } + setValue(value: {[key: string]: any}): void { + this.control.setValue(value); + } /** * @description @@ -279,7 +294,7 @@ export class NgForm extends ControlContainer implements Form, * @param $event The "submit" event object */ onSubmit($event: Event): boolean { - (this as{submitted: boolean}).submitted = true; + (this as {submitted: boolean}).submitted = true; syncPendingControls(this.form, this._directives); this.ngSubmit.emit($event); return false; @@ -289,7 +304,9 @@ export class NgForm extends ControlContainer implements Form, * @description * Method called when the "reset" event is triggered on the form. */ - onReset(): void { this.resetForm(); } + onReset(): void { + this.resetForm(); + } /** * @description @@ -299,7 +316,7 @@ export class NgForm extends ControlContainer implements Form, */ resetForm(value: any = undefined): void { this.form.reset(value); - (this as{submitted: boolean}).submitted = false; + (this as {submitted: boolean}).submitted = false; } private _setUpdateStrategy() { diff --git a/packages/forms/src/directives/ng_model.ts b/packages/forms/src/directives/ng_model.ts index b85aa6f2407cd..67cf5c0ecddb2 100644 --- a/packages/forms/src/directives/ng_model.ts +++ b/packages/forms/src/directives/ng_model.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, EventEmitter, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; +import {Directive, EventEmitter, forwardRef, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges} from '@angular/core'; import {FormControl, FormHooks} from '../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators'; @@ -64,17 +64,17 @@ const resolvedPromise = (() => Promise.resolve(null))(); * (also known as 'banana-box syntax'), the value in the UI always syncs back to * the domain model in your class. * - * To inspect the properties of the associated `FormControl` (like validity state), - * export the directive into a local template variable using `ngModel` as the key (ex: `#myVar="ngModel"`). - * You then access the control using the directive's `control` property, - * but most properties used (like `valid` and `dirty`) fall through to the control anyway for direct access. - * See a full list of properties directly available in `AbstractControlDirective`. + * To inspect the properties of the associated `FormControl` (like validity state), + * export the directive into a local template variable using `ngModel` as the key (ex: + * `#myVar="ngModel"`). You then access the control using the directive's `control` property, but + * most properties used (like `valid` and `dirty`) fall through to the control anyway for direct + * access. See a full list of properties directly available in `AbstractControlDirective`. * - * @see `RadioControlValueAccessor` + * @see `RadioControlValueAccessor` * @see `SelectControlValueAccessor` - * + * * @usageNotes - * + * * ### Using ngModel on a standalone control * * The following examples show a simple standalone control using `ngModel`: @@ -84,23 +84,23 @@ const resolvedPromise = (() => Promise.resolve(null))(); * When using the `ngModel` within `<form>` tags, you'll also need to supply a `name` attribute * so that the control can be registered with the parent form under that name. * - * In the context of a parent form, it's often unnecessary to include one-way or two-way binding, - * as the parent form syncs the value for you. You access its properties by exporting it into a - * local template variable using `ngForm` such as (`#f="ngForm"`). Use the variable where + * In the context of a parent form, it's often unnecessary to include one-way or two-way binding, + * as the parent form syncs the value for you. You access its properties by exporting it into a + * local template variable using `ngForm` such as (`#f="ngForm"`). Use the variable where * needed on form submission. * * If you do need to populate initial values into your form, using a one-way binding for * `ngModel` tends to be sufficient as long as you use the exported form's value rather * than the domain model's value on submit. - * + * * ### Using ngModel within a form * * The following example shows controls using `ngModel` within a form: * * {@example forms/ts/simpleForm/simple_form_example.ts region='Component'} - * + * * ### Using a standalone ngModel within a group - * + * * The following example shows you how to use a standalone ngModel control * within a form. This controls the display of the form, but doesn't contain form data. * @@ -111,11 +111,11 @@ const resolvedPromise = (() => Promise.resolve(null))(); * </form> * <!-- form value: {login: ''} --> * ``` - * + * * ### Setting the ngModel name attribute through options - * - * The following example shows you an alternate way to set the name attribute. The name attribute is used - * within a custom form component, and the name `@Input` property serves a different purpose. + * + * The following example shows you an alternate way to set the name attribute. The name attribute is + * used within a custom form component, and the name `@Input` property serves a different purpose. * * ```html * <form> @@ -133,8 +133,7 @@ const resolvedPromise = (() => Promise.resolve(null))(); providers: [formControlBinding], exportAs: 'ngModel' }) -export class NgModel extends NgControl implements OnChanges, - OnDestroy { +export class NgModel extends NgControl implements OnChanges, OnDestroy { public readonly control: FormControl = new FormControl(); // At runtime we coerce arbitrary values assigned to the "disabled" input to a "boolean". @@ -161,14 +160,14 @@ export class NgModel extends NgControl implements OnChanges, * uses this name as a key to retrieve this control's value. */ // TODO(issue/24571): remove '!'. - @Input() name !: string; + @Input() name!: string; /** * @description * Tracks whether the control is disabled. */ // TODO(issue/24571): remove '!'. - @Input('disabled') isDisabled !: boolean; + @Input('disabled') isDisabled!: boolean; /** * @description @@ -192,8 +191,7 @@ export class NgModel extends NgControl implements OnChanges, * */ // TODO(issue/24571): remove '!'. - @Input('ngModelOptions') - options !: {name?: string, standalone?: boolean, updateOn?: FormHooks}; + @Input('ngModelOptions') options!: {name?: string, standalone?: boolean, updateOn?: FormHooks}; /** * @description @@ -202,151 +200,156 @@ export class NgModel extends NgControl implements OnChanges, */ @Output('ngModelChange') update = new EventEmitter(); - constructor(@Optional() @Host() parent: ControlContainer, - @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>, - @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>, - @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) - valueAccessors: ControlValueAccessor[]) { - super(); - this._parent = parent; - this._rawValidators = validators || []; - this._rawAsyncValidators = asyncValidators || []; - this.valueAccessor = selectValueAccessor(this, valueAccessors); - } - - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use - * only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ - ngOnChanges(changes: SimpleChanges) { - this._checkForErrors(); - if (!this._registered) this._setUpControl(); - if ('isDisabled' in changes) { - this._updateDisabled(changes); - } - - if (isPropertyUpdated(changes, this.viewModel)) { - this._updateValue(this.model); - this.viewModel = this.model; - } - } - - /** - * @description - * Lifecycle method called before the directive's instance is destroyed. For internal - * use only. - */ - ngOnDestroy(): void { this.formDirective && this.formDirective.removeControl(this); } - - /** - * @description - * Returns an array that represents the path from the top-level form to this control. - * Each index is the string name of the control on that level. - */ - get path(): string[] { - return this._parent ? controlPath(this.name, this._parent) : [this.name]; - } - - /** - * @description - * The top-level directive for this control if present, otherwise null. - */ - get formDirective(): any { return this._parent ? this._parent.formDirective : null; } - - /** - * @description - * Synchronous validator function composed of all the synchronous validators - * registered with this directive. - */ - get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); } - - /** - * @description - * Async validator function composed of all the async validators registered with this - * directive. - */ - get asyncValidator(): AsyncValidatorFn|null { - return composeAsyncValidators(this._rawAsyncValidators); - } - - /** - * @description - * Sets the new value for the view model and emits an `ngModelChange` event. - * - * @param newValue The new value emitted by `ngModelChange`. - */ - viewToModelUpdate(newValue: any): void { - this.viewModel = newValue; - this.update.emit(newValue); - } - - private _setUpControl(): void { - this._setUpdateStrategy(); - this._isStandalone() ? this._setUpStandalone() : - this.formDirective.addControl(this); - this._registered = true; - } - - private _setUpdateStrategy(): void { - if (this.options && this.options.updateOn != null) { - this.control._updateOn = this.options.updateOn; - } - } - - private _isStandalone(): boolean { - return !this._parent || !!(this.options && this.options.standalone); - } - - private _setUpStandalone(): void { - setUpControl(this.control, this); - this.control.updateValueAndValidity({emitEvent: false}); - } - - private _checkForErrors(): void { - if (!this._isStandalone()) { - this._checkParentType(); - } - this._checkName(); - } - - private _checkParentType(): void { - if (!(this._parent instanceof NgModelGroup) && - this._parent instanceof AbstractFormGroupDirective) { - TemplateDrivenErrors.formGroupNameException(); - } else if ( - !(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) { - TemplateDrivenErrors.modelParentException(); - } - } - - private _checkName(): void { - if (this.options && this.options.name) this.name = this.options.name; - - if (!this._isStandalone() && !this.name) { - TemplateDrivenErrors.missingNameException(); - } - } - - private _updateValue(value: any): void { - resolvedPromise.then( - () => { this.control.setValue(value, {emitViewToModelChange: false}); }); - } - - private _updateDisabled(changes: SimpleChanges) { - const disabledValue = changes['isDisabled'].currentValue; - - const isDisabled = - disabledValue === '' || (disabledValue && disabledValue !== 'false'); - - resolvedPromise.then(() => { - if (isDisabled && !this.control.disabled) { - this.control.disable(); - } else if (!isDisabled && this.control.disabled) { - this.control.enable(); - } - }); - } + constructor( + @Optional() @Host() parent: ControlContainer, + @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>, + @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: + Array<AsyncValidator|AsyncValidatorFn>, + @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) { + super(); + this._parent = parent; + this._rawValidators = validators || []; + this._rawAsyncValidators = asyncValidators || []; + this.valueAccessor = selectValueAccessor(this, valueAccessors); + } + + /** + * @description + * A lifecycle method called when the directive's inputs change. For internal use + * only. + * + * @param changes A object of key/value pairs for the set of changed inputs. + */ + ngOnChanges(changes: SimpleChanges) { + this._checkForErrors(); + if (!this._registered) this._setUpControl(); + if ('isDisabled' in changes) { + this._updateDisabled(changes); + } + + if (isPropertyUpdated(changes, this.viewModel)) { + this._updateValue(this.model); + this.viewModel = this.model; + } + } + + /** + * @description + * Lifecycle method called before the directive's instance is destroyed. For internal + * use only. + */ + ngOnDestroy(): void { + this.formDirective && this.formDirective.removeControl(this); + } + + /** + * @description + * Returns an array that represents the path from the top-level form to this control. + * Each index is the string name of the control on that level. + */ + get path(): string[] { + return this._parent ? controlPath(this.name, this._parent) : [this.name]; + } + + /** + * @description + * The top-level directive for this control if present, otherwise null. + */ + get formDirective(): any { + return this._parent ? this._parent.formDirective : null; + } + + /** + * @description + * Synchronous validator function composed of all the synchronous validators + * registered with this directive. + */ + get validator(): ValidatorFn|null { + return composeValidators(this._rawValidators); + } + + /** + * @description + * Async validator function composed of all the async validators registered with this + * directive. + */ + get asyncValidator(): AsyncValidatorFn|null { + return composeAsyncValidators(this._rawAsyncValidators); + } + + /** + * @description + * Sets the new value for the view model and emits an `ngModelChange` event. + * + * @param newValue The new value emitted by `ngModelChange`. + */ + viewToModelUpdate(newValue: any): void { + this.viewModel = newValue; + this.update.emit(newValue); + } + + private _setUpControl(): void { + this._setUpdateStrategy(); + this._isStandalone() ? this._setUpStandalone() : this.formDirective.addControl(this); + this._registered = true; + } + + private _setUpdateStrategy(): void { + if (this.options && this.options.updateOn != null) { + this.control._updateOn = this.options.updateOn; + } + } + + private _isStandalone(): boolean { + return !this._parent || !!(this.options && this.options.standalone); + } + + private _setUpStandalone(): void { + setUpControl(this.control, this); + this.control.updateValueAndValidity({emitEvent: false}); + } + + private _checkForErrors(): void { + if (!this._isStandalone()) { + this._checkParentType(); + } + this._checkName(); + } + + private _checkParentType(): void { + if (!(this._parent instanceof NgModelGroup) && + this._parent instanceof AbstractFormGroupDirective) { + TemplateDrivenErrors.formGroupNameException(); + } else if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) { + TemplateDrivenErrors.modelParentException(); + } + } + + private _checkName(): void { + if (this.options && this.options.name) this.name = this.options.name; + + if (!this._isStandalone() && !this.name) { + TemplateDrivenErrors.missingNameException(); + } + } + + private _updateValue(value: any): void { + resolvedPromise.then(() => { + this.control.setValue(value, {emitViewToModelChange: false}); + }); + } + + private _updateDisabled(changes: SimpleChanges) { + const disabledValue = changes['isDisabled'].currentValue; + + const isDisabled = disabledValue === '' || (disabledValue && disabledValue !== 'false'); + + resolvedPromise.then(() => { + if (isDisabled && !this.control.disabled) { + this.control.disable(); + } else if (!isDisabled && this.control.disabled) { + this.control.enable(); + } + }); + } } diff --git a/packages/forms/src/directives/ng_model_group.ts b/packages/forms/src/directives/ng_model_group.ts index c7cabf035e72e..55ec88d51de84 100644 --- a/packages/forms/src/directives/ng_model_group.ts +++ b/packages/forms/src/directives/ng_model_group.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Host, Inject, Input, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core'; +import {Directive, forwardRef, Host, Inject, Input, OnDestroy, OnInit, Optional, Self, SkipSelf} from '@angular/core'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators'; @@ -54,7 +54,7 @@ export class NgModelGroup extends AbstractFormGroupDirective implements OnInit, * to a key in the parent `NgForm`. */ // TODO(issue/24571): remove '!'. - @Input('ngModelGroup') name !: string; + @Input('ngModelGroup') name!: string; constructor( @Host() @SkipSelf() parent: ControlContainer, diff --git a/packages/forms/src/directives/normalize_validator.ts b/packages/forms/src/directives/normalize_validator.ts index 3a08e61558c6d..f12aa0aae5c69 100644 --- a/packages/forms/src/directives/normalize_validator.ts +++ b/packages/forms/src/directives/normalize_validator.ts @@ -9,7 +9,7 @@ import {AbstractControl} from '../model'; import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators'; -export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn { +export function normalizeValidator(validator: ValidatorFn|Validator): ValidatorFn { if ((<Validator>validator).validate) { return (c: AbstractControl) => (<Validator>validator).validate(c); } else { @@ -17,8 +17,8 @@ export function normalizeValidator(validator: ValidatorFn | Validator): Validato } } -export function normalizeAsyncValidator(validator: AsyncValidatorFn | AsyncValidator): - AsyncValidatorFn { +export function normalizeAsyncValidator(validator: AsyncValidatorFn| + AsyncValidator): AsyncValidatorFn { if ((<AsyncValidator>validator).validate) { return (c: AbstractControl) => (<AsyncValidator>validator).validate(c); } else { diff --git a/packages/forms/src/directives/number_value_accessor.ts b/packages/forms/src/directives/number_value_accessor.ts index df5edc43fa607..dc85ae7b0d9fd 100644 --- a/packages/forms/src/directives/number_value_accessor.ts +++ b/packages/forms/src/directives/number_value_accessor.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, Renderer2, forwardRef} from '@angular/core'; +import {Directive, ElementRef, forwardRef, Renderer2} from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; @@ -84,7 +84,9 @@ export class NumberValueAccessor implements ControlValueAccessor { * @param fn The callback function */ registerOnChange(fn: (_: number|null) => void): void { - this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); }; + this.onChange = (value) => { + fn(value == '' ? null : parseFloat(value)); + }; } /** @@ -93,7 +95,9 @@ export class NumberValueAccessor implements ControlValueAccessor { * * @param fn The callback function */ - registerOnTouched(fn: () => void): void { this.onTouched = fn; } + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } /** * Sets the "disabled" property on the input element. diff --git a/packages/forms/src/directives/radio_control_value_accessor.ts b/packages/forms/src/directives/radio_control_value_accessor.ts index bca1040d21dad..6f5ee4905b8d9 100644 --- a/packages/forms/src/directives/radio_control_value_accessor.ts +++ b/packages/forms/src/directives/radio_control_value_accessor.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, Injectable, Injector, Input, OnDestroy, OnInit, Renderer2, forwardRef} from '@angular/core'; +import {Directive, ElementRef, forwardRef, Injectable, Injector, Input, OnDestroy, OnInit, Renderer2} from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; import {NgControl} from './ng_control'; @@ -93,17 +93,16 @@ export class RadioControlRegistry { host: {'(change)': 'onChange()', '(blur)': 'onTouched()'}, providers: [RADIO_VALUE_ACCESSOR] }) -export class RadioControlValueAccessor implements ControlValueAccessor, - OnDestroy, OnInit { +export class RadioControlValueAccessor implements ControlValueAccessor, OnDestroy, OnInit { /** @internal */ // TODO(issue/24571): remove '!'. - _state !: boolean; + _state!: boolean; /** @internal */ // TODO(issue/24571): remove '!'. - _control !: NgControl; + _control!: NgControl; /** @internal */ // TODO(issue/24571): remove '!'. - _fn !: Function; + _fn!: Function; /** * @description @@ -122,7 +121,7 @@ export class RadioControlValueAccessor implements ControlValueAccessor, * Tracks the name of the radio input element. */ // TODO(issue/24571): remove '!'. - @Input() name !: string; + @Input() name!: string; /** * @description @@ -130,7 +129,7 @@ export class RadioControlValueAccessor implements ControlValueAccessor, * to a key in the parent `FormGroup` or `FormArray`. */ // TODO(issue/24571): remove '!'. - @Input() formControlName !: string; + @Input() formControlName!: string; /** * @description @@ -156,7 +155,9 @@ export class RadioControlValueAccessor implements ControlValueAccessor, * @description * Lifecycle method called before the directive's instance is destroyed. For internal use only. */ - ngOnDestroy(): void { this._registry.remove(this); } + ngOnDestroy(): void { + this._registry.remove(this); + } /** * @description @@ -188,7 +189,9 @@ export class RadioControlValueAccessor implements ControlValueAccessor, * * @param value */ - fireUncheck(value: any): void { this.writeValue(value); } + fireUncheck(value: any): void { + this.writeValue(value); + } /** * @description @@ -196,7 +199,9 @@ export class RadioControlValueAccessor implements ControlValueAccessor, * * @param fn The callback function */ - registerOnTouched(fn: () => {}): void { this.onTouched = fn; } + registerOnTouched(fn: () => {}): void { + this.onTouched = fn; + } /** * Sets the "disabled" property on the input element. diff --git a/packages/forms/src/directives/range_value_accessor.ts b/packages/forms/src/directives/range_value_accessor.ts index e386fe14445a6..7a01f7c1d81ee 100644 --- a/packages/forms/src/directives/range_value_accessor.ts +++ b/packages/forms/src/directives/range_value_accessor.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, Renderer2, StaticProvider, forwardRef} from '@angular/core'; +import {Directive, ElementRef, forwardRef, Renderer2, StaticProvider} from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; @@ -82,7 +82,9 @@ export class RangeValueAccessor implements ControlValueAccessor { * @param fn The callback function */ registerOnChange(fn: (_: number|null) => void): void { - this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); }; + this.onChange = (value) => { + fn(value == '' ? null : parseFloat(value)); + }; } /** @@ -91,7 +93,9 @@ export class RangeValueAccessor implements ControlValueAccessor { * * @param fn The callback function */ - registerOnTouched(fn: () => void): void { this.onTouched = fn; } + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } /** * Sets the "disabled" property on the range input element. diff --git a/packages/forms/src/directives/reactive_directives/form_control_directive.ts b/packages/forms/src/directives/reactive_directives/form_control_directive.ts index c329d5ab31f6f..0d9711fc03449 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_directive.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, EventEmitter, Inject, InjectionToken, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; +import {Directive, EventEmitter, forwardRef, Inject, InjectionToken, Input, OnChanges, Optional, Output, Self, SimpleChanges} from '@angular/core'; import {FormControl} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators'; @@ -30,88 +30,23 @@ export const formControlBinding: any = { /** * @description - * * Syncs a standalone `FormControl` instance to a form control element. - * + * Synchronizes a standalone `FormControl` instance to a form control element. + * + * Note that support for using the `ngModel` input property and `ngModelChange` event with reactive + * form directives was deprecated in Angular v6 and is scheduled for removal in + * a future version of Angular. + * For details, see [Deprecated features](guide/deprecations#ngmodel-with-reactive-forms). + * * @see [Reactive Forms Guide](guide/reactive-forms) * @see `FormControl` * @see `AbstractControl` * * @usageNotes * - * ### Registering a single form control - * - * The following examples shows how to register a standalone control and set its value. + * The following example shows how to register a standalone control and set its value. * * {@example forms/ts/simpleFormControl/simple_form_control_example.ts region='Component'} * - * ### Use with ngModel - * - * Support for using the `ngModel` input property and `ngModelChange` event with reactive - * form directives has been deprecated in Angular v6 and will be removed in a future version - * of Angular. - * - * Now deprecated: - * - * ```html - * <input [formControl]="control" [(ngModel)]="value"> - * ``` - * - * ```ts - * this.value = 'some value'; - * ``` - * - * This has been deprecated for a few reasons. First, developers have found this pattern - * confusing. It seems like the actual `ngModel` directive is being used, but in fact it's - * an input/output property named `ngModel` on the reactive form directive that simply - * approximates (some of) its behavior. Specifically, it allows getting/setting the value - * and intercepting value events. However, some of `ngModel`'s other features - like - * delaying updates with`ngModelOptions` or exporting the directive - simply don't work, - * which has understandably caused some confusion. - * - * In addition, this pattern mixes template-driven and reactive forms strategies, which - * we generally don't recommend because it doesn't take advantage of the full benefits of - * either strategy. Setting the value in the template violates the template-agnostic - * principles behind reactive forms, whereas adding a `FormControl`/`FormGroup` layer in - * the class removes the convenience of defining forms in the template. - * - * To update your code before support is removed, you'll want to decide whether to stick - * with reactive form directives (and get/set values using reactive forms patterns) or - * switch over to template-driven directives. - * - * After (choice 1 - use reactive forms): - * - * ```html - * <input [formControl]="control"> - * ``` - * - * ```ts - * this.control.setValue('some value'); - * ``` - * - * After (choice 2 - use template-driven forms): - * - * ```html - * <input [(ngModel)]="value"> - * ``` - * - * ```ts - * this.value = 'some value'; - * ``` - * - * By default, when you use this pattern, you will see a deprecation warning once in dev - * mode. You can choose to silence this warning by providing a config for - * `ReactiveFormsModule` at import time: - * - * ```ts - * imports: [ - * ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'}); - * ] - * ``` - * - * Alternatively, you can choose to surface a separate warning for each instance of this - * pattern with a config value of `"always"`. This may help to track down where in the code - * the pattern is being used as the code is being updated. - * * @ngModule ReactiveFormsModule * @publicApi */ @@ -129,14 +64,16 @@ export class FormControlDirective extends NgControl implements OnChanges { * Tracks the `FormControl` instance bound to the directive. */ // TODO(issue/24571): remove '!'. - @Input('formControl') form !: FormControl; + @Input('formControl') form!: FormControl; /** * @description * Triggers a warning that this input should not be used with reactive forms. */ @Input('disabled') - set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); } + set isDisabled(isDisabled: boolean) { + ReactiveErrors.disabledAttrWarning(); + } // TODO(kara): remove next 4 properties once deprecation period is over @@ -164,81 +101,88 @@ export class FormControlDirective extends NgControl implements OnChanges { */ _ngModelWarningSent = false; - constructor(@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>, - @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>, - @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) - valueAccessors: ControlValueAccessor[], - @Optional() @Inject(NG_MODEL_WITH_FORM_CONTROL_WARNING) private _ngModelWarningConfig: string|null) { - super(); - this._rawValidators = validators || []; - this._rawAsyncValidators = asyncValidators || []; - this.valueAccessor = selectValueAccessor(this, valueAccessors); - } - - /** - * @description - * A lifecycle method called when the directive's inputs change. For internal use - * only. - * - * @param changes A object of key/value pairs for the set of changed inputs. - */ - ngOnChanges(changes: SimpleChanges): void { - if (this._isControlChanged(changes)) { - setUpControl(this.form, this); - if (this.control.disabled && this.valueAccessor !.setDisabledState) { - this.valueAccessor !.setDisabledState !(true); - } - this.form.updateValueAndValidity({emitEvent: false}); - } - if (isPropertyUpdated(changes, this.viewModel)) { - _ngModelWarning( - 'formControl', FormControlDirective, this, this._ngModelWarningConfig); - this.form.setValue(this.model); - this.viewModel = this.model; - } - } - - /** - * @description - * Returns an array that represents the path from the top-level form to this control. - * Each index is the string name of the control on that level. - */ - get path(): string[] { return []; } - - /** - * @description - * Synchronous validator function composed of all the synchronous validators - * registered with this directive. - */ - get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); } - - /** - * @description - * Async validator function composed of all the async validators registered with this - * directive. - */ - get asyncValidator(): AsyncValidatorFn|null { - return composeAsyncValidators(this._rawAsyncValidators); - } - - /** - * @description - * The `FormControl` bound to this directive. - */ - get control(): FormControl { return this.form; } - - /** - * @description - * Sets the new value for the view model and emits an `ngModelChange` event. - * - * @param newValue The new value for the view model. - */ - viewToModelUpdate(newValue: any): void { - this.viewModel = newValue; - this.update.emit(newValue); - } - - private _isControlChanged(changes: {[key: string]: any}): boolean { - return changes.hasOwnProperty('form'); - } + constructor( + @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>, + @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: + Array<AsyncValidator|AsyncValidatorFn>, + @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[], + @Optional() @Inject(NG_MODEL_WITH_FORM_CONTROL_WARNING) private _ngModelWarningConfig: string| + null) { + super(); + this._rawValidators = validators || []; + this._rawAsyncValidators = asyncValidators || []; + this.valueAccessor = selectValueAccessor(this, valueAccessors); + } + + /** + * @description + * A lifecycle method called when the directive's inputs change. For internal use + * only. + * + * @param changes A object of key/value pairs for the set of changed inputs. + */ + ngOnChanges(changes: SimpleChanges): void { + if (this._isControlChanged(changes)) { + setUpControl(this.form, this); + if (this.control.disabled && this.valueAccessor!.setDisabledState) { + this.valueAccessor!.setDisabledState!(true); + } + this.form.updateValueAndValidity({emitEvent: false}); + } + if (isPropertyUpdated(changes, this.viewModel)) { + _ngModelWarning('formControl', FormControlDirective, this, this._ngModelWarningConfig); + this.form.setValue(this.model); + this.viewModel = this.model; + } + } + + /** + * @description + * Returns an array that represents the path from the top-level form to this control. + * Each index is the string name of the control on that level. + */ + get path(): string[] { + return []; + } + + /** + * @description + * Synchronous validator function composed of all the synchronous validators + * registered with this directive. + */ + get validator(): ValidatorFn|null { + return composeValidators(this._rawValidators); + } + + /** + * @description + * Async validator function composed of all the async validators registered with this + * directive. + */ + get asyncValidator(): AsyncValidatorFn|null { + return composeAsyncValidators(this._rawAsyncValidators); + } + + /** + * @description + * The `FormControl` bound to this directive. + */ + get control(): FormControl { + return this.form; + } + + /** + * @description + * Sets the new value for the view model and emits an `ngModelChange` event. + * + * @param newValue The new value for the view model. + */ + viewToModelUpdate(newValue: any): void { + this.viewModel = newValue; + this.update.emit(newValue); + } + + private _isControlChanged(changes: {[key: string]: any}): boolean { + return changes.hasOwnProperty('form'); + } } diff --git a/packages/forms/src/directives/reactive_directives/form_control_name.ts b/packages/forms/src/directives/reactive_directives/form_control_name.ts index a466ea4f45605..d425f8da5d29a 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_name.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, EventEmitter, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core'; +import {Directive, EventEmitter, forwardRef, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, SkipSelf} from '@angular/core'; import {FormControl} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators'; @@ -31,13 +31,13 @@ export const controlNameBinding: any = { * @description * Syncs a `FormControl` in an existing `FormGroup` to a form control * element by name. - * + * * @see [Reactive Forms Guide](guide/reactive-forms) * @see `FormControl` * @see `AbstractControl` * * @usageNotes - * + * * ### Register `FormControl` within a group * * The following example shows how to register multiple form controls within a form group @@ -50,77 +50,13 @@ export const controlNameBinding: any = { * * Radio buttons: `RadioControlValueAccessor` * * Selects: `SelectControlValueAccessor` * - * ### Use with ngModel + * ### Use with ngModel is deprecated * * Support for using the `ngModel` input property and `ngModelChange` event with reactive - * form directives has been deprecated in Angular v6 and will be removed in a future - * version of Angular. - * - * Now deprecated: - * - * ```html - * <form [formGroup]="form"> - * <input formControlName="first" [(ngModel)]="value"> - * </form> - * ``` - * - * ```ts - * this.value = 'some value'; - * ``` - * - * This has been deprecated for a few reasons. First, developers have found this pattern - * confusing. It seems like the actual `ngModel` directive is being used, but in fact it's - * an input/output property named `ngModel` on the reactive form directive that simply - * approximates (some of) its behavior. Specifically, it allows getting/setting the value - * and intercepting value events. However, some of `ngModel`'s other features - like - * delaying updates with `ngModelOptions` or exporting the directive - simply don't work, - * which has understandably caused some confusion. - * - * In addition, this pattern mixes template-driven and reactive forms strategies, which - * we generally don't recommend because it doesn't take advantage of the full benefits of - * either strategy. Setting the value in the template violates the template-agnostic - * principles behind reactive forms, whereas adding a `FormControl`/`FormGroup` layer in - * the class removes the convenience of defining forms in the template. - * - * To update your code before support is removed, you'll want to decide whether to stick with - * reactive form directives (and get/set values using reactive forms patterns) or switch over to - * template-driven directives. - * - * After (choice 1 - use reactive forms): - * - * ```html - * <form [formGroup]="form"> - * <input formControlName="first"> - * </form> - * ``` + * form directives has been deprecated in Angular v6 and is scheduled for removal in + * a future version of Angular. * - * ```ts - * this.form.get('first').setValue('some value'); - * ``` - * - * After (choice 2 - use template-driven forms): - * - * ```html - * <input [(ngModel)]="value"> - * ``` - * - * ```ts - * this.value = 'some value'; - * ``` - * - * By default, when you use this pattern, you will see a deprecation warning once in dev - * mode. You can choose to silence this warning by providing a config for - * `ReactiveFormsModule` at import time: - * - * ```ts - * imports: [ - * ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'}) - * ] - * ``` - * - * Alternatively, you can choose to surface a separate warning for each instance of this - * pattern with a config value of `"always"`. This may help to track down where in the code - * the pattern is being used as the code is being updated. + * For details, see [Deprecated features](guide/deprecations#ngmodel-with-reactive-forms). * * @ngModule ReactiveFormsModule * @publicApi @@ -140,7 +76,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { * Tracks the `FormControl` instance bound to the directive. */ // TODO(issue/24571): remove '!'. - readonly control !: FormControl; + readonly control!: FormControl; /** * @description @@ -152,14 +88,16 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { * to indices when iterating over controls in a `FormArray`. */ // TODO(issue/24571): remove '!'. - @Input('formControlName') name !: string | number | null; + @Input('formControlName') name!: string|number|null; /** * @description * Triggers a warning that this input should not be used with reactive forms. */ @Input('disabled') - set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); } + set isDisabled(isDisabled: boolean) { + ReactiveErrors.disabledAttrWarning(); + } // TODO(kara): remove next 4 properties once deprecation period is over @@ -244,21 +182,25 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { * Each index is the string name of the control on that level. */ get path(): string[] { - return controlPath(this.name == null ? this.name : this.name.toString(), this._parent !); + return controlPath(this.name == null ? this.name : this.name.toString(), this._parent!); } /** * @description * The top-level directive for this group if present, otherwise null. */ - get formDirective(): any { return this._parent ? this._parent.formDirective : null; } + get formDirective(): any { + return this._parent ? this._parent.formDirective : null; + } /** * @description * Synchronous validator function composed of all the synchronous validators * registered with this directive. */ - get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); } + get validator(): ValidatorFn|null { + return composeValidators(this._rawValidators); + } /** * @description @@ -266,7 +208,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { * directive. */ get asyncValidator(): AsyncValidatorFn { - return composeAsyncValidators(this._rawAsyncValidators) !; + return composeAsyncValidators(this._rawAsyncValidators)!; } private _checkParentType(): void { @@ -282,9 +224,9 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { private _setUpControl() { this._checkParentType(); - (this as{control: FormControl}).control = this.formDirective.addControl(this); - if (this.control.disabled && this.valueAccessor !.setDisabledState) { - this.valueAccessor !.setDisabledState !(true); + (this as {control: FormControl}).control = this.formDirective.addControl(this); + if (this.control.disabled && this.valueAccessor!.setDisabledState) { + this.valueAccessor!.setDisabledState!(true); } this._added = true; } diff --git a/packages/forms/src/directives/reactive_directives/form_group_directive.ts b/packages/forms/src/directives/reactive_directives/form_group_directive.ts index 2d3e617adef39..4fabe81ea4c0f 100644 --- a/packages/forms/src/directives/reactive_directives/form_group_directive.ts +++ b/packages/forms/src/directives/reactive_directives/form_group_directive.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, EventEmitter, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; +import {Directive, EventEmitter, forwardRef, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges} from '@angular/core'; + import {FormArray, FormControl, FormGroup} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators'; import {ControlContainer} from '../control_container'; @@ -31,7 +32,7 @@ export const formDirectiveProvider: any = { * `FormGroup` instance to match any child `FormControl`, `FormGroup`, * and `FormArray` instances to child `FormControlName`, `FormGroupName`, * and `FormArrayName` directives. - * + * * @see [Reactive Forms Guide](guide/reactive-forms) * @see `AbstractControl` * @@ -51,8 +52,7 @@ export const formDirectiveProvider: any = { host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'}, exportAs: 'ngForm' }) -export class FormGroupDirective extends ControlContainer implements Form, - OnChanges { +export class FormGroupDirective extends ControlContainer implements Form, OnChanges { /** * @description * Reports whether the form submission has been triggered. @@ -60,7 +60,7 @@ export class FormGroupDirective extends ControlContainer implements Form, public readonly submitted: boolean = false; // TODO(issue/24571): remove '!'. - private _oldForm !: FormGroup; + private _oldForm!: FormGroup; /** * @description @@ -72,7 +72,7 @@ export class FormGroupDirective extends ControlContainer implements Form, * @description * Tracks the `FormGroup` bound to this directive. */ - @Input('formGroup') form: FormGroup = null !; + @Input('formGroup') form: FormGroup = null!; /** * @description @@ -105,20 +105,26 @@ export class FormGroupDirective extends ControlContainer implements Form, * @description * Returns this directive's instance. */ - get formDirective(): Form { return this; } + get formDirective(): Form { + return this; + } /** * @description * Returns the `FormGroup` bound to this directive. */ - get control(): FormGroup { return this.form; } + get control(): FormGroup { + return this.form; + } /** * @description * Returns an array representing the path to this group. Because this directive * always lives at the top level of a form, it always an empty array. */ - get path(): string[] { return []; } + get path(): string[] { + return []; + } /** * @description @@ -141,7 +147,9 @@ export class FormGroupDirective extends ControlContainer implements Form, * * @param dir The `FormControlName` directive instance. */ - getControl(dir: FormControlName): FormControl { return <FormControl>this.form.get(dir.path); } + getControl(dir: FormControlName): FormControl { + return <FormControl>this.form.get(dir.path); + } /** * @description @@ -149,7 +157,9 @@ export class FormGroupDirective extends ControlContainer implements Form, * * @param dir The `FormControlName` directive instance. */ - removeControl(dir: FormControlName): void { removeDir<FormControlName>(this.directives, dir); } + removeControl(dir: FormControlName): void { + removeDir<FormControlName>(this.directives, dir); + } /** * Adds a new `FormGroupName` directive instance to the form. @@ -175,7 +185,9 @@ export class FormGroupDirective extends ControlContainer implements Form, * * @param dir The `FormGroupName` directive instance. */ - getFormGroup(dir: FormGroupName): FormGroup { return <FormGroup>this.form.get(dir.path); } + getFormGroup(dir: FormGroupName): FormGroup { + return <FormGroup>this.form.get(dir.path); + } /** * Adds a new `FormArrayName` directive instance to the form. @@ -201,7 +213,9 @@ export class FormGroupDirective extends ControlContainer implements Form, * * @param dir The `FormArrayName` directive instance. */ - getFormArray(dir: FormArrayName): FormArray { return <FormArray>this.form.get(dir.path); } + getFormArray(dir: FormArrayName): FormArray { + return <FormArray>this.form.get(dir.path); + } /** * Sets the new value for the provided `FormControlName` directive. @@ -222,7 +236,7 @@ export class FormGroupDirective extends ControlContainer implements Form, * @param $event The "submit" event object */ onSubmit($event: Event): boolean { - (this as{submitted: boolean}).submitted = true; + (this as {submitted: boolean}).submitted = true; syncPendingControls(this.form, this.directives); this.ngSubmit.emit($event); return false; @@ -232,7 +246,9 @@ export class FormGroupDirective extends ControlContainer implements Form, * @description * Method called when the "reset" event is triggered on the form. */ - onReset(): void { this.resetForm(); } + onReset(): void { + this.resetForm(); + } /** * @description @@ -242,7 +258,7 @@ export class FormGroupDirective extends ControlContainer implements Form, */ resetForm(value: any = undefined): void { this.form.reset(value); - (this as{submitted: boolean}).submitted = false; + (this as {submitted: boolean}).submitted = false; } @@ -253,7 +269,7 @@ export class FormGroupDirective extends ControlContainer implements Form, if (dir.control !== newCtrl) { cleanUpControl(dir.control, dir); if (newCtrl) setUpControl(newCtrl, dir); - (dir as{control: FormControl}).control = newCtrl; + (dir as {control: FormControl}).control = newCtrl; } }); @@ -268,10 +284,10 @@ export class FormGroupDirective extends ControlContainer implements Form, private _updateValidators() { const sync = composeValidators(this._validators); - this.form.validator = Validators.compose([this.form.validator !, sync !]); + this.form.validator = Validators.compose([this.form.validator!, sync!]); const async = composeAsyncValidators(this._asyncValidators); - this.form.asyncValidator = Validators.composeAsync([this.form.asyncValidator !, async !]); + this.form.asyncValidator = Validators.composeAsync([this.form.asyncValidator!, async!]); } private _checkFormPresent() { diff --git a/packages/forms/src/directives/reactive_directives/form_group_name.ts b/packages/forms/src/directives/reactive_directives/form_group_name.ts index 92ca96a17bea0..c3f48352fe75a 100644 --- a/packages/forms/src/directives/reactive_directives/form_group_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_group_name.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Host, Inject, Input, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core'; +import {Directive, forwardRef, Host, Inject, Input, OnDestroy, OnInit, Optional, Self, SkipSelf} from '@angular/core'; import {FormArray} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators'; @@ -82,7 +82,7 @@ export class FormGroupName extends AbstractFormGroupDirective implements OnInit, * to indices when iterating over groups in a `FormArray`. */ // TODO(issue/24571): remove '!'. - @Input('formGroupName') name !: string | number | null; + @Input('formGroupName') name!: string|number|null; constructor( @Optional() @Host() @SkipSelf() parent: ControlContainer, @@ -152,7 +152,7 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy * to indices when iterating over arrays in a `FormArray`. */ // TODO(issue/24571): remove '!'. - @Input('formArrayName') name !: string | number | null; + @Input('formArrayName') name!: string|number|null; constructor( @Optional() @Host() @SkipSelf() parent: ControlContainer, @@ -172,7 +172,7 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy */ ngOnInit(): void { this._checkParentType(); - this.formDirective !.addFormArray(this); + this.formDirective!.addFormArray(this); } /** @@ -189,7 +189,9 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy * @description * The `FormArray` bound to this directive. */ - get control(): FormArray { return this.formDirective !.getFormArray(this); } + get control(): FormArray { + return this.formDirective!.getFormArray(this); + } /** * @description @@ -213,7 +215,9 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy * Synchronous validator function composed of all the synchronous validators registered with this * directive. */ - get validator(): ValidatorFn|null { return composeValidators(this._validators); } + get validator(): ValidatorFn|null { + return composeValidators(this._validators); + } /** * @description diff --git a/packages/forms/src/directives/reactive_errors.ts b/packages/forms/src/directives/reactive_errors.ts index acca6a319f49f..b7e46673e6bf1 100644 --- a/packages/forms/src/directives/reactive_errors.ts +++ b/packages/forms/src/directives/reactive_errors.ts @@ -83,8 +83,9 @@ export class ReactiveErrors { in Angular v7. For more information on this, see our API docs here: - https://angular.io/api/forms/${directiveName === 'formControl' ? 'FormControlDirective' - : 'FormControlName'}#use-with-ngmodel + https://angular.io/api/forms/${ + directiveName === 'formControl' ? 'FormControlDirective' : + 'FormControlName'}#use-with-ngmodel `); } } diff --git a/packages/forms/src/directives/select_control_value_accessor.ts b/packages/forms/src/directives/select_control_value_accessor.ts index a8fa16e8cb9c8..9edb9d47e6ecf 100644 --- a/packages/forms/src/directives/select_control_value_accessor.ts +++ b/packages/forms/src/directives/select_control_value_accessor.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider, forwardRef, ɵlooseIdentical as looseIdentical} from '@angular/core'; +import {Directive, ElementRef, forwardRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider, ɵlooseIdentical as looseIdentical} from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; @@ -16,7 +16,7 @@ export const SELECT_VALUE_ACCESSOR: StaticProvider = { multi: true }; -function _buildValueString(id: string | null, value: any): string { +function _buildValueString(id: string|null, value: any): string { if (id == null) return `${value}`; if (value && typeof value === 'object') value = 'Object'; return `${id}: ${value}`.slice(0, 50); @@ -160,7 +160,9 @@ export class SelectControlValueAccessor implements ControlValueAccessor { * * @param fn The callback function */ - registerOnTouched(fn: () => any): void { this.onTouched = fn; } + registerOnTouched(fn: () => any): void { + this.onTouched = fn; + } /** * Sets the "disabled" property on the select input element. @@ -172,7 +174,9 @@ export class SelectControlValueAccessor implements ControlValueAccessor { } /** @internal */ - _registerOption(): string { return (this._idCounter++).toString(); } + _registerOption(): string { + return (this._idCounter++).toString(); + } /** @internal */ _getOptionId(value: any): string|null { @@ -206,7 +210,7 @@ export class NgSelectOption implements OnDestroy { * ID of the option element */ // TODO(issue/24571): remove '!'. - id !: string; + id!: string; constructor( private _element: ElementRef, private _renderer: Renderer2, diff --git a/packages/forms/src/directives/select_multiple_control_value_accessor.ts b/packages/forms/src/directives/select_multiple_control_value_accessor.ts index 5e0800947b773..5bc28e20c07fd 100644 --- a/packages/forms/src/directives/select_multiple_control_value_accessor.ts +++ b/packages/forms/src/directives/select_multiple_control_value_accessor.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider, forwardRef, ɵlooseIdentical as looseIdentical} from '@angular/core'; +import {Directive, ElementRef, forwardRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider, ɵlooseIdentical as looseIdentical} from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; @@ -36,24 +36,24 @@ interface HTMLOption { /** Mock interface for HTMLCollection */ abstract class HTMLCollection { // TODO(issue/24571): remove '!'. - length !: number; + length!: number; abstract item(_: number): HTMLOption; } /** * @description - * The `ControlValueAccessor` for writing multi-select control values and listening to multi-select control - * changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel` - * directives. - * + * The `ControlValueAccessor` for writing multi-select control values and listening to multi-select + * control changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and + * `NgModel` directives. + * * @see `SelectControlValueAccessor` * * @usageNotes - * + * * ### Using a multi-select control - * + * * The follow example shows you how to use a multi-select control with a reactive form. - * + * * ```ts * const countryControl = new FormControl(); * ``` @@ -65,9 +65,9 @@ abstract class HTMLCollection { * </option> * </select> * ``` - * + * * ### Customizing option selection - * + * * To customize the default option comparison algorithm, `<select>` supports `compareWith` input. * See the `SelectControlValueAccessor` for usage. * @@ -135,9 +135,13 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor if (Array.isArray(value)) { // convert values to ids const ids = value.map((v) => this._getOptionId(v)); - optionSelectedStateSetter = (opt, o) => { opt._setSelected(ids.indexOf(o.toString()) > -1); }; + optionSelectedStateSetter = (opt, o) => { + opt._setSelected(ids.indexOf(o.toString()) > -1); + }; } else { - optionSelectedStateSetter = (opt, o) => { opt._setSelected(false); }; + optionSelectedStateSetter = (opt, o) => { + opt._setSelected(false); + }; } this._optionMap.forEach(optionSelectedStateSetter); } @@ -182,7 +186,9 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor * * @param fn The callback function */ - registerOnTouched(fn: () => any): void { this.onTouched = fn; } + registerOnTouched(fn: () => any): void { + this.onTouched = fn; + } /** * Sets the "disabled" property on the select input element. @@ -203,7 +209,7 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor /** @internal */ _getOptionId(value: any): string|null { for (const id of Array.from(this._optionMap.keys())) { - if (this._compareWith(this._optionMap.get(id) !._value, value)) return id; + if (this._compareWith(this._optionMap.get(id)!._value, value)) return id; } return null; } @@ -211,7 +217,7 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor /** @internal */ _getOptionValue(valueString: string): any { const id: string = _extractId(valueString); - return this._optionMap.has(id) ? this._optionMap.get(id) !._value : valueString; + return this._optionMap.has(id) ? this._optionMap.get(id)!._value : valueString; } } @@ -228,7 +234,7 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor @Directive({selector: 'option'}) export class ɵNgSelectMultipleOption implements OnDestroy { // TODO(issue/24571): remove '!'. - id !: string; + id!: string; /** @internal */ _value: any; diff --git a/packages/forms/src/directives/shared.ts b/packages/forms/src/directives/shared.ts index 17347db421cdf..2bdf5e51b40b6 100644 --- a/packages/forms/src/directives/shared.ts +++ b/packages/forms/src/directives/shared.ts @@ -28,43 +28,44 @@ import {SelectMultipleControlValueAccessor} from './select_multiple_control_valu import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators'; -export function controlPath(name: string | null, parent: ControlContainer): string[] { - return [...parent.path !, name !]; +export function controlPath(name: string|null, parent: ControlContainer): string[] { + return [...parent.path!, name!]; } export function setUpControl(control: FormControl, dir: NgControl): void { if (!control) _throwError(dir, 'Cannot find control with'); if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with'); - control.validator = Validators.compose([control.validator !, dir.validator]); - control.asyncValidator = Validators.composeAsync([control.asyncValidator !, dir.asyncValidator]); - dir.valueAccessor !.writeValue(control.value); + control.validator = Validators.compose([control.validator!, dir.validator]); + control.asyncValidator = Validators.composeAsync([control.asyncValidator!, dir.asyncValidator]); + dir.valueAccessor!.writeValue(control.value); setUpViewChangePipeline(control, dir); setUpModelChangePipeline(control, dir); setUpBlurPipeline(control, dir); - if (dir.valueAccessor !.setDisabledState) { - control.registerOnDisabledChange( - (isDisabled: boolean) => { dir.valueAccessor !.setDisabledState !(isDisabled); }); + if (dir.valueAccessor!.setDisabledState) { + control.registerOnDisabledChange((isDisabled: boolean) => { + dir.valueAccessor!.setDisabledState!(isDisabled); + }); } // re-run validation when validator binding changes, e.g. minlength=3 -> minlength=4 - dir._rawValidators.forEach((validator: Validator | ValidatorFn) => { + dir._rawValidators.forEach((validator: Validator|ValidatorFn) => { if ((<Validator>validator).registerOnValidatorChange) - (<Validator>validator).registerOnValidatorChange !(() => control.updateValueAndValidity()); + (<Validator>validator).registerOnValidatorChange!(() => control.updateValueAndValidity()); }); - dir._rawAsyncValidators.forEach((validator: AsyncValidator | AsyncValidatorFn) => { + dir._rawAsyncValidators.forEach((validator: AsyncValidator|AsyncValidatorFn) => { if ((<Validator>validator).registerOnValidatorChange) - (<Validator>validator).registerOnValidatorChange !(() => control.updateValueAndValidity()); + (<Validator>validator).registerOnValidatorChange!(() => control.updateValueAndValidity()); }); } export function cleanUpControl(control: FormControl, dir: NgControl) { - dir.valueAccessor !.registerOnChange(() => _noControlError(dir)); - dir.valueAccessor !.registerOnTouched(() => _noControlError(dir)); + dir.valueAccessor!.registerOnChange(() => _noControlError(dir)); + dir.valueAccessor!.registerOnTouched(() => _noControlError(dir)); dir._rawValidators.forEach((validator: any) => { if (validator.registerOnValidatorChange) { @@ -82,7 +83,7 @@ export function cleanUpControl(control: FormControl, dir: NgControl) { } function setUpViewChangePipeline(control: FormControl, dir: NgControl): void { - dir.valueAccessor !.registerOnChange((newValue: any) => { + dir.valueAccessor!.registerOnChange((newValue: any) => { control._pendingValue = newValue; control._pendingChange = true; control._pendingDirty = true; @@ -92,7 +93,7 @@ function setUpViewChangePipeline(control: FormControl, dir: NgControl): void { } function setUpBlurPipeline(control: FormControl, dir: NgControl): void { - dir.valueAccessor !.registerOnTouched(() => { + dir.valueAccessor!.registerOnTouched(() => { control._pendingTouched = true; if (control.updateOn === 'blur' && control._pendingChange) updateControl(control, dir); @@ -110,7 +111,7 @@ function updateControl(control: FormControl, dir: NgControl): void { function setUpModelChangePipeline(control: FormControl, dir: NgControl): void { control.registerOnChange((newValue: any, emitModelEvent: boolean) => { // control -> view - dir.valueAccessor !.writeValue(newValue); + dir.valueAccessor!.writeValue(newValue); // control -> ngModel if (emitModelEvent) dir.viewToModelUpdate(newValue); @@ -118,7 +119,7 @@ function setUpModelChangePipeline(control: FormControl, dir: NgControl): void { } export function setUpFormContainer( - control: FormGroup | FormArray, dir: AbstractFormGroupDirective | FormArrayName) { + control: FormGroup|FormArray, dir: AbstractFormGroupDirective|FormArrayName) { if (control == null) _throwError(dir, 'Cannot find control with'); control.validator = Validators.compose([control.validator, dir.validator]); control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]); @@ -130,9 +131,9 @@ function _noControlError(dir: NgControl) { function _throwError(dir: AbstractControlDirective, message: string): void { let messageEnd: string; - if (dir.path !.length > 1) { + if (dir.path!.length > 1) { messageEnd = `path: '${dir.path!.join(' -> ')}'`; - } else if (dir.path ![0]) { + } else if (dir.path![0]) { messageEnd = `name: '${dir.path}'`; } else { messageEnd = 'unspecified name attribute'; @@ -226,7 +227,7 @@ export function removeDir<T>(list: T[], el: T): void { // TODO(kara): remove after deprecation period export function _ngModelWarning( name: string, type: {_ngModelWarningSentOnce: boolean}, - instance: {_ngModelWarningSent: boolean}, warningConfig: string | null) { + instance: {_ngModelWarningSent: boolean}, warningConfig: string|null) { if (!isDevMode() || warningConfig === 'never') return; if (((warningConfig === null || warningConfig === 'once') && !type._ngModelWarningSentOnce) || diff --git a/packages/forms/src/directives/validators.ts b/packages/forms/src/directives/validators.ts index e8d6c0d251098..27d5c1a72708a 100644 --- a/packages/forms/src/directives/validators.ts +++ b/packages/forms/src/directives/validators.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Input, OnChanges, SimpleChanges, StaticProvider, forwardRef} from '@angular/core'; +import {Directive, forwardRef, Input, OnChanges, SimpleChanges, StaticProvider} from '@angular/core'; import {Observable} from 'rxjs'; import {AbstractControl} from '../model'; @@ -136,11 +136,11 @@ export const CHECKBOX_REQUIRED_VALIDATOR: StaticProvider = { * @description * A directive that adds the `required` validator to any controls marked with the * `required` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. - * + * * @see [Form Validation](guide/form-validation) * * @usageNotes - * + * * ### Adding a required validator using template-driven forms * * ``` @@ -159,16 +159,18 @@ export const CHECKBOX_REQUIRED_VALIDATOR: StaticProvider = { }) export class RequiredValidator implements Validator { // TODO(issue/24571): remove '!'. - private _required !: boolean; + private _required!: boolean; // TODO(issue/24571): remove '!'. - private _onChange !: () => void; + private _onChange!: () => void; /** * @description * Tracks changes to the required attribute bound to this directive. */ @Input() - get required(): boolean|string { return this._required; } + get required(): boolean|string { + return this._required; + } set required(value: boolean|string) { this._required = value != null && value !== false && `${value}` !== 'false'; @@ -190,22 +192,25 @@ export class RequiredValidator implements Validator { * * @param fn The callback function */ - registerOnValidatorChange(fn: () => void): void { this._onChange = fn; } + registerOnValidatorChange(fn: () => void): void { + this._onChange = fn; + } } /** * A Directive that adds the `required` validator to checkbox controls marked with the * `required` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. - * + * * @see [Form Validation](guide/form-validation) * * @usageNotes - * + * * ### Adding a required checkbox validator using template-driven forms * - * The following example shows how to add a checkbox required validator to an input attached to an ngModel binding. - * + * The following example shows how to add a checkbox required validator to an input attached to an + * ngModel binding. + * * ``` * <input type="checkbox" name="active" ngModel required> * ``` @@ -248,11 +253,12 @@ export const EMAIL_VALIDATOR: any = { * @see [Form Validation](guide/form-validation) * * @usageNotes - * + * * ### Adding an email validator * - * The following example shows how to add an email validator to an input attached to an ngModel binding. - * + * The following example shows how to add an email validator to an input attached to an ngModel + * binding. + * * ``` * <input type="email" name="email" ngModel email> * <input type="email" name="email" ngModel email="true"> @@ -269,9 +275,9 @@ export const EMAIL_VALIDATOR: any = { }) export class EmailValidator implements Validator { // TODO(issue/24571): remove '!'. - private _enabled !: boolean; + private _enabled!: boolean; // TODO(issue/24571): remove '!'. - private _onChange !: () => void; + private _onChange!: () => void; /** * @description @@ -298,7 +304,9 @@ export class EmailValidator implements Validator { * * @param fn The callback function */ - registerOnValidatorChange(fn: () => void): void { this._onChange = fn; } + registerOnValidatorChange(fn: () => void): void { + this._onChange = fn; + } } /** @@ -308,7 +316,9 @@ export class EmailValidator implements Validator { * * @publicApi */ -export interface ValidatorFn { (control: AbstractControl): ValidationErrors|null; } +export interface ValidatorFn { + (control: AbstractControl): ValidationErrors|null; +} /** * @description @@ -334,7 +344,7 @@ export const MIN_LENGTH_VALIDATOR: any = { /** * A directive that adds minimum length validation to controls marked with the * `minlength` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. - * + * * @see [Form Validation](guide/form-validation) * * @usageNotes @@ -357,19 +367,18 @@ export const MIN_LENGTH_VALIDATOR: any = { providers: [MIN_LENGTH_VALIDATOR], host: {'[attr.minlength]': 'minlength ? minlength : null'} }) -export class MinLengthValidator implements Validator, - OnChanges { +export class MinLengthValidator implements Validator, OnChanges { // TODO(issue/24571): remove '!'. - private _validator !: ValidatorFn; + private _validator!: ValidatorFn; // TODO(issue/24571): remove '!'. - private _onChange !: () => void; + private _onChange!: () => void; /** * @description * Tracks changes to the the minimum length bound to this directive. */ // TODO(issue/24571): remove '!'. - @Input() minlength !: string | number; + @Input() minlength!: string|number; /** * @description @@ -400,7 +409,9 @@ export class MinLengthValidator implements Validator, * * @param fn The callback function */ - registerOnValidatorChange(fn: () => void): void { this._onChange = fn; } + registerOnValidatorChange(fn: () => void): void { + this._onChange = fn; + } private _createValidator(): void { this._validator = Validators.minLength( @@ -421,7 +432,7 @@ export const MAX_LENGTH_VALIDATOR: any = { /** * A directive that adds max length validation to controls marked with the * `maxlength` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. - * + * * @see [Form Validation](guide/form-validation) * * @usageNotes @@ -444,19 +455,18 @@ export const MAX_LENGTH_VALIDATOR: any = { providers: [MAX_LENGTH_VALIDATOR], host: {'[attr.maxlength]': 'maxlength ? maxlength : null'} }) -export class MaxLengthValidator implements Validator, - OnChanges { +export class MaxLengthValidator implements Validator, OnChanges { // TODO(issue/24571): remove '!'. - private _validator !: ValidatorFn; + private _validator!: ValidatorFn; // TODO(issue/24571): remove '!'. - private _onChange !: () => void; + private _onChange!: () => void; /** * @description * Tracks changes to the the maximum length bound to this directive. */ // TODO(issue/24571): remove '!'. - @Input() maxlength !: string | number; + @Input() maxlength!: string|number; /** * @description @@ -487,7 +497,9 @@ export class MaxLengthValidator implements Validator, * * @param fn The callback function */ - registerOnValidatorChange(fn: () => void): void { this._onChange = fn; } + registerOnValidatorChange(fn: () => void): void { + this._onChange = fn; + } private _createValidator(): void { this._validator = Validators.maxLength( @@ -511,7 +523,7 @@ export const PATTERN_VALIDATOR: any = { * A directive that adds regex pattern validation to controls marked with the * `pattern` attribute. The regex must match the entire control value. * The directive is provided with the `NG_VALIDATORS` multi-provider list. - * + * * @see [Form Validation](guide/form-validation) * * @usageNotes @@ -524,7 +536,7 @@ export const PATTERN_VALIDATOR: any = { * ```html * <input name="firstName" ngModel pattern="[a-zA-Z ]*"> * ``` - * + * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi @@ -534,19 +546,18 @@ export const PATTERN_VALIDATOR: any = { providers: [PATTERN_VALIDATOR], host: {'[attr.pattern]': 'pattern ? pattern : null'} }) -export class PatternValidator implements Validator, - OnChanges { +export class PatternValidator implements Validator, OnChanges { // TODO(issue/24571): remove '!'. - private _validator !: ValidatorFn; + private _validator!: ValidatorFn; // TODO(issue/24571): remove '!'. - private _onChange !: () => void; + private _onChange!: () => void; /** * @description * Tracks changes to the pattern bound to this directive. */ // TODO(issue/24571): remove '!'. - @Input() pattern !: string | RegExp; + @Input() pattern!: string|RegExp; /** * @description @@ -567,7 +578,9 @@ export class PatternValidator implements Validator, * Method that validates whether the value matches the * the pattern requirement. */ - validate(control: AbstractControl): ValidationErrors|null { return this._validator(control); } + validate(control: AbstractControl): ValidationErrors|null { + return this._validator(control); + } /** * @description @@ -575,7 +588,11 @@ export class PatternValidator implements Validator, * * @param fn The callback function */ - registerOnValidatorChange(fn: () => void): void { this._onChange = fn; } + registerOnValidatorChange(fn: () => void): void { + this._onChange = fn; + } - private _createValidator(): void { this._validator = Validators.pattern(this.pattern); } + private _createValidator(): void { + this._validator = Validators.pattern(this.pattern); + } } diff --git a/packages/forms/src/form_builder.ts b/packages/forms/src/form_builder.ts index aa6faa7c816bc..d59cbeffaaa69 100644 --- a/packages/forms/src/form_builder.ts +++ b/packages/forms/src/form_builder.ts @@ -11,8 +11,8 @@ import {Injectable} from '@angular/core'; import {AsyncValidatorFn, ValidatorFn} from './directives/validators'; import {AbstractControl, AbstractControlOptions, FormArray, FormControl, FormGroup, FormHooks} from './model'; -function isAbstractControlOptions(options: AbstractControlOptions | {[key: string]: any}): - options is AbstractControlOptions { +function isAbstractControlOptions(options: AbstractControlOptions| + {[key: string]: any}): options is AbstractControlOptions { return (<AbstractControlOptions>options).asyncValidators !== undefined || (<AbstractControlOptions>options).validators !== undefined || (<AbstractControlOptions>options).updateOn !== undefined; diff --git a/packages/forms/src/form_providers.ts b/packages/forms/src/form_providers.ts index 84934e932760f..5de31a70411ff 100644 --- a/packages/forms/src/form_providers.ts +++ b/packages/forms/src/form_providers.ts @@ -16,7 +16,8 @@ import {FormBuilder} from './form_builder'; * Exports the required providers and directives for template-driven forms, * making them available for import by NgModules that import this module. * - * @see [Forms Guide](/guide/forms) + * @see [Forms Overview](/guide/forms-overview) + * @see [Template-driven Forms Guide](/guide/forms) * * @publicApi */ @@ -31,9 +32,9 @@ export class FormsModule { /** * Exports the required infrastructure and directives for reactive forms, * making them available for import by NgModules that import this module. - * @see [Forms](guide/reactive-forms) * - * @see [Reactive Forms Guide](/guide/reactive-forms) + * @see [Forms Overview](guide/forms-overview) + * @see [Reactive Forms Guide](guide/reactive-forms) * * @publicApi */ @@ -52,14 +53,13 @@ export class ReactiveFormsModule { * binding is used with reactive form directives. */ static withConfig(opts: { - /** @deprecated as of v6 */ warnOnNgModelWithFormControl: 'never' | 'once' | 'always' + /** @deprecated as of v6 */ warnOnNgModelWithFormControl: 'never'|'once'|'always' }): ModuleWithProviders<ReactiveFormsModule> { return { ngModule: ReactiveFormsModule, - providers: [{ - provide: NG_MODEL_WITH_FORM_CONTROL_WARNING, - useValue: opts.warnOnNgModelWithFormControl - }] + providers: [ + {provide: NG_MODEL_WITH_FORM_CONTROL_WARNING, useValue: opts.warnOnNgModelWithFormControl} + ] }; } } diff --git a/packages/forms/src/model.ts b/packages/forms/src/model.ts index ddbcfd76fc946..dc49b050753a3 100644 --- a/packages/forms/src/model.ts +++ b/packages/forms/src/model.ts @@ -44,7 +44,7 @@ export const PENDING = 'PENDING'; */ export const DISABLED = 'DISABLED'; -function _find(control: AbstractControl, path: Array<string|number>| string, delimiter: string) { +function _find(control: AbstractControl, path: Array<string|number>|string, delimiter: string) { if (path == null) return null; if (!Array.isArray(path)) { @@ -55,7 +55,7 @@ function _find(control: AbstractControl, path: Array<string|number>| string, del // Not using Array.reduce here due to a Chrome 80 bug // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982 let controlToFind: AbstractControl|null = control; - path.forEach((name: string | number) => { + path.forEach((name: string|number) => { if (controlToFind instanceof FormGroup) { controlToFind = controlToFind.controls.hasOwnProperty(name as string) ? controlToFind.controls[name] : @@ -69,9 +69,8 @@ function _find(control: AbstractControl, path: Array<string|number>| string, del return controlToFind; } -function coerceToValidator( - validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null): ValidatorFn| - null { +function coerceToValidator(validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions| + null): ValidatorFn|null { const validator = (isOptionsObj(validatorOrOpts) ? (validatorOrOpts as AbstractControlOptions).validators : validatorOrOpts) as ValidatorFn | @@ -81,8 +80,9 @@ function coerceToValidator( } function coerceToAsyncValidator( - asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null, validatorOrOpts?: ValidatorFn | - ValidatorFn[] | AbstractControlOptions | null): AsyncValidatorFn|null { + asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null, + validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null): AsyncValidatorFn| + null { const origAsyncValidator = (isOptionsObj(validatorOrOpts) ? (validatorOrOpts as AbstractControlOptions).asyncValidators : asyncValidator) as AsyncValidatorFn | @@ -92,7 +92,7 @@ function coerceToAsyncValidator( origAsyncValidator || null; } -export type FormHooks = 'change' | 'blur' | 'submit'; +export type FormHooks = 'change'|'blur'|'submit'; /** * Interface for options provided to an `AbstractControl`. @@ -118,8 +118,8 @@ export interface AbstractControlOptions { } -function isOptionsObj( - validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null): boolean { +function isOptionsObj(validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions| + null): boolean { return validatorOrOpts != null && !Array.isArray(validatorOrOpts) && typeof validatorOrOpts === 'object'; } @@ -142,21 +142,21 @@ function isOptionsObj( export abstract class AbstractControl { /** @internal */ // TODO(issue/24571): remove '!'. - _pendingDirty !: boolean; + _pendingDirty!: boolean; /** @internal */ // TODO(issue/24571): remove '!'. - _pendingTouched !: boolean; + _pendingTouched!: boolean; /** @internal */ _onCollectionChange = () => {}; /** @internal */ // TODO(issue/24571): remove '!'. - _updateOn !: FormHooks; + _updateOn!: FormHooks; // TODO(issue/24571): remove '!'. - private _parent !: FormGroup | FormArray; + private _parent!: FormGroup|FormArray; private _asyncValidationSubscription: any; /** @@ -184,7 +184,9 @@ export abstract class AbstractControl { /** * The parent control. */ - get parent(): FormGroup|FormArray { return this._parent; } + get parent(): FormGroup|FormArray { + return this._parent; + } /** * The validation status of the control. There are four possible @@ -199,7 +201,7 @@ export abstract class AbstractControl { * both valid AND invalid or invalid AND disabled. */ // TODO(issue/24571): remove '!'. - public readonly status !: string; + public readonly status!: string; /** * A control is `valid` when its `status` is `VALID`. @@ -209,7 +211,9 @@ export abstract class AbstractControl { * @returns True if the control has passed all of its validation tests, * false otherwise. */ - get valid(): boolean { return this.status === VALID; } + get valid(): boolean { + return this.status === VALID; + } /** * A control is `invalid` when its `status` is `INVALID`. @@ -219,7 +223,9 @@ export abstract class AbstractControl { * @returns True if this control has failed one or more of its validation checks, * false otherwise. */ - get invalid(): boolean { return this.status === INVALID; } + get invalid(): boolean { + return this.status === INVALID; + } /** * A control is `pending` when its `status` is `PENDING`. @@ -229,7 +235,9 @@ export abstract class AbstractControl { * @returns True if this control is in the process of conducting a validation check, * false otherwise. */ - get pending(): boolean { return this.status == PENDING; } + get pending(): boolean { + return this.status == PENDING; + } /** * A control is `disabled` when its `status` is `DISABLED`. @@ -242,7 +250,9 @@ export abstract class AbstractControl { * * @returns True if the control is disabled, false otherwise. */ - get disabled(): boolean { return this.status === DISABLED; } + get disabled(): boolean { + return this.status === DISABLED; + } /** * A control is `enabled` as long as its `status` is not `DISABLED`. @@ -253,14 +263,16 @@ export abstract class AbstractControl { * @see {@link AbstractControl.status} * */ - get enabled(): boolean { return this.status !== DISABLED; } + get enabled(): boolean { + return this.status !== DISABLED; + } /** * An object containing any errors generated by failing validation, * or null if there are no errors. */ // TODO(issue/24571): remove '!'. - public readonly errors !: ValidationErrors | null; + public readonly errors!: ValidationErrors|null; /** * A control is `pristine` if the user has not yet changed @@ -278,7 +290,9 @@ export abstract class AbstractControl { * @returns True if the user has changed the value of this control in the UI; compare `pristine`. * Programmatic changes to a control's value do not mark it dirty. */ - get dirty(): boolean { return !this.pristine; } + get dirty(): boolean { + return !this.pristine; + } /** * True if the control is marked as `touched`. @@ -294,7 +308,9 @@ export abstract class AbstractControl { * A control is `untouched` if the user has not yet triggered * a `blur` event on it. */ - get untouched(): boolean { return !this.touched; } + get untouched(): boolean { + return !this.touched; + } /** * A multicasting observable that emits an event every time the value of the control changes, in @@ -302,7 +318,7 @@ export abstract class AbstractControl { * without passing along {emitEvent: false} as a function argument. */ // TODO(issue/24571): remove '!'. - public readonly valueChanges !: Observable<any>; + public readonly valueChanges!: Observable<any>; /** * A multicasting observable that emits an event every time the validation `status` of the control @@ -312,7 +328,7 @@ export abstract class AbstractControl { * */ // TODO(issue/24571): remove '!'. - public readonly statusChanges !: Observable<any>; + public readonly statusChanges!: Observable<any>; /** * Reports the update strategy of the `AbstractControl` (meaning @@ -355,7 +371,9 @@ export abstract class AbstractControl { * `updateValueAndValidity()` for the new validation to take effect. * */ - clearValidators(): void { this.validator = null; } + clearValidators(): void { + this.validator = null; + } /** * Empties out the async validator list. @@ -364,7 +382,9 @@ export abstract class AbstractControl { * `updateValueAndValidity()` for the new validation to take effect. * */ - clearAsyncValidators(): void { this.asyncValidator = null; } + clearAsyncValidators(): void { + this.asyncValidator = null; + } /** * Marks the control as `touched`. A control is touched by focus and @@ -380,7 +400,7 @@ export abstract class AbstractControl { * marks all direct ancestors. Default is false. */ markAsTouched(opts: {onlySelf?: boolean} = {}): void { - (this as{touched: boolean}).touched = true; + (this as {touched: boolean}).touched = true; if (this._parent && !opts.onlySelf) { this._parent.markAsTouched(opts); @@ -413,11 +433,12 @@ export abstract class AbstractControl { * marks all direct ancestors. Default is false. */ markAsUntouched(opts: {onlySelf?: boolean} = {}): void { - (this as{touched: boolean}).touched = false; + (this as {touched: boolean}).touched = false; this._pendingTouched = false; - this._forEachChild( - (control: AbstractControl) => { control.markAsUntouched({onlySelf: true}); }); + this._forEachChild((control: AbstractControl) => { + control.markAsUntouched({onlySelf: true}); + }); if (this._parent && !opts.onlySelf) { this._parent._updateTouched(opts); @@ -438,7 +459,7 @@ export abstract class AbstractControl { * marks all direct ancestors. Default is false. */ markAsDirty(opts: {onlySelf?: boolean} = {}): void { - (this as{pristine: boolean}).pristine = false; + (this as {pristine: boolean}).pristine = false; if (this._parent && !opts.onlySelf) { this._parent.markAsDirty(opts); @@ -459,13 +480,15 @@ export abstract class AbstractControl { * @param opts Configuration options that determine how the control emits events after * marking is applied. * * `onlySelf`: When true, mark only this control. When false or not supplied, - * marks all direct ancestors. Default is false.. + * marks all direct ancestors. Default is false. */ markAsPristine(opts: {onlySelf?: boolean} = {}): void { - (this as{pristine: boolean}).pristine = true; + (this as {pristine: boolean}).pristine = true; this._pendingDirty = false; - this._forEachChild((control: AbstractControl) => { control.markAsPristine({onlySelf: true}); }); + this._forEachChild((control: AbstractControl) => { + control.markAsPristine({onlySelf: true}); + }); if (this._parent && !opts.onlySelf) { this._parent._updatePristine(opts); @@ -482,14 +505,14 @@ export abstract class AbstractControl { * @param opts Configuration options that determine how the control propagates changes and * emits events after marking is applied. * * `onlySelf`: When true, mark only this control. When false or not supplied, - * marks all direct ancestors. Default is false.. + * marks all direct ancestors. Default is false. * * `emitEvent`: When true or not supplied (the default), the `statusChanges` * observable emits an event with the latest status the control is marked pending. * When false, no events are emitted. * */ markAsPending(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void { - (this as{status: string}).status = PENDING; + (this as {status: string}).status = PENDING; if (opts.emitEvent !== false) { (this.statusChanges as EventEmitter<any>).emit(this.status); @@ -511,7 +534,7 @@ export abstract class AbstractControl { * @param opts Configuration options that determine how the control propagates * changes and emits events after the control is disabled. * * `onlySelf`: When true, mark only this control. When false or not supplied, - * marks all direct ancestors. Default is false.. + * marks all direct ancestors. Default is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control is disabled. @@ -522,10 +545,11 @@ export abstract class AbstractControl { // parent's dirtiness based on the children. const skipPristineCheck = this._parentMarkedDirty(opts.onlySelf); - (this as{status: string}).status = DISABLED; - (this as{errors: ValidationErrors | null}).errors = null; - this._forEachChild( - (control: AbstractControl) => { control.disable({...opts, onlySelf: true}); }); + (this as {status: string}).status = DISABLED; + (this as {errors: ValidationErrors | null}).errors = null; + this._forEachChild((control: AbstractControl) => { + control.disable({...opts, onlySelf: true}); + }); this._updateValue(); if (opts.emitEvent !== false) { @@ -549,7 +573,7 @@ export abstract class AbstractControl { * @param opts Configure options that control how the control propagates changes and * emits events when marked as untouched * * `onlySelf`: When true, mark only this control. When false or not supplied, - * marks all direct ancestors. Default is false.. + * marks all direct ancestors. Default is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control is enabled. @@ -560,9 +584,10 @@ export abstract class AbstractControl { // parent's dirtiness based on the children. const skipPristineCheck = this._parentMarkedDirty(opts.onlySelf); - (this as{status: string}).status = VALID; - this._forEachChild( - (control: AbstractControl) => { control.enable({...opts, onlySelf: true}); }); + (this as {status: string}).status = VALID; + this._forEachChild((control: AbstractControl) => { + control.enable({...opts, onlySelf: true}); + }); this.updateValueAndValidity({onlySelf: true, emitEvent: opts.emitEvent}); this._updateAncestors({...opts, skipPristineCheck}); @@ -583,7 +608,9 @@ export abstract class AbstractControl { /** * @param parent Sets the parent of the control */ - setParent(parent: FormGroup|FormArray): void { this._parent = parent; } + setParent(parent: FormGroup|FormArray): void { + this._parent = parent; + } /** * Sets the value of the control. Abstract method (implemented in sub-classes). @@ -608,7 +635,7 @@ export abstract class AbstractControl { * @param opts Configuration options determine how the control propagates changes and emits events * after updates and validity checks are applied. * * `onlySelf`: When true, only update this control. When false or not supplied, - * update all direct ancestors. Default is false.. + * update all direct ancestors. Default is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control is updated. @@ -620,8 +647,8 @@ export abstract class AbstractControl { if (this.enabled) { this._cancelExistingSubscription(); - (this as{errors: ValidationErrors | null}).errors = this._runValidator(); - (this as{status: string}).status = this._calculateStatus(); + (this as {errors: ValidationErrors | null}).errors = this._runValidator(); + (this as {status: string}).status = this._calculateStatus(); if (this.status === VALID || this.status === PENDING) { this._runAsyncValidator(opts.emitEvent); @@ -645,7 +672,7 @@ export abstract class AbstractControl { } private _setInitialStatus() { - (this as{status: string}).status = this._allControlsDisabled() ? DISABLED : VALID; + (this as {status: string}).status = this._allControlsDisabled() ? DISABLED : VALID; } private _runValidator(): ValidationErrors|null { @@ -654,10 +681,10 @@ export abstract class AbstractControl { private _runAsyncValidator(emitEvent?: boolean): void { if (this.asyncValidator) { - (this as{status: string}).status = PENDING; + (this as {status: string}).status = PENDING; const obs = toObservable(this.asyncValidator(this)); this._asyncValidationSubscription = - obs.subscribe((errors: ValidationErrors | null) => this.setErrors(errors, {emitEvent})); + obs.subscribe((errors: ValidationErrors|null) => this.setErrors(errors, {emitEvent})); } } @@ -673,6 +700,7 @@ export abstract class AbstractControl { * Calling `setErrors` also updates the validity of the parent control. * * @usageNotes + * * ### Manually set the errors for a control * * ``` @@ -690,7 +718,7 @@ export abstract class AbstractControl { * ``` */ setErrors(errors: ValidationErrors|null, opts: {emitEvent?: boolean} = {}): void { - (this as{errors: ValidationErrors | null}).errors = errors; + (this as {errors: ValidationErrors | null}).errors = errors; this._updateControlsErrors(opts.emitEvent !== false); } @@ -711,7 +739,9 @@ export abstract class AbstractControl { * * * `this.form.get(['person', 'name']);` */ - get(path: Array<string|number>|string): AbstractControl|null { return _find(this, path, '.'); } + get(path: Array<string|number>|string): AbstractControl|null { + return _find(this, path, '.'); + } /** * @description @@ -794,7 +824,7 @@ export abstract class AbstractControl { /** @internal */ _updateControlsErrors(emitEvent: boolean): void { - (this as{status: string}).status = this._calculateStatus(); + (this as {status: string}).status = this._calculateStatus(); if (emitEvent) { (this.statusChanges as EventEmitter<string>).emit(this.status); @@ -807,8 +837,8 @@ export abstract class AbstractControl { /** @internal */ _initObservables() { - (this as{valueChanges: Observable<any>}).valueChanges = new EventEmitter(); - (this as{statusChanges: Observable<any>}).statusChanges = new EventEmitter(); + (this as {valueChanges: Observable<any>}).valueChanges = new EventEmitter(); + (this as {statusChanges: Observable<any>}).statusChanges = new EventEmitter(); } @@ -852,7 +882,7 @@ export abstract class AbstractControl { /** @internal */ _updatePristine(opts: {onlySelf?: boolean} = {}): void { - (this as{pristine: boolean}).pristine = !this._anyControlsDirty(); + (this as {pristine: boolean}).pristine = !this._anyControlsDirty(); if (this._parent && !opts.onlySelf) { this._parent._updatePristine(opts); @@ -861,7 +891,7 @@ export abstract class AbstractControl { /** @internal */ _updateTouched(opts: {onlySelf?: boolean} = {}): void { - (this as{touched: boolean}).touched = this._anyControlsTouched(); + (this as {touched: boolean}).touched = this._anyControlsTouched(); if (this._parent && !opts.onlySelf) { this._parent._updateTouched(opts); @@ -878,12 +908,14 @@ export abstract class AbstractControl { } /** @internal */ - _registerOnCollectionChange(fn: () => void): void { this._onCollectionChange = fn; } + _registerOnCollectionChange(fn: () => void): void { + this._onCollectionChange = fn; + } /** @internal */ _setUpdateStrategy(opts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null): void { if (isOptionsObj(opts) && (opts as AbstractControlOptions).updateOn != null) { - this._updateOn = (opts as AbstractControlOptions).updateOn !; + this._updateOn = (opts as AbstractControlOptions).updateOn!; } } @@ -904,7 +936,7 @@ export abstract class AbstractControl { * This is one of the three fundamental building blocks of Angular forms, along with * `FormGroup` and `FormArray`. It extends the `AbstractControl` class that * implements most of the base functionality for accessing the value, validation status, - * user interactions and events. + * user interactions and events. See [usage examples below](#usage-notes). * * @see `AbstractControl` * @see [Reactive Forms Guide](guide/reactive-forms) @@ -1006,18 +1038,18 @@ export class FormControl extends AbstractControl { _pendingChange: any; /** - * Creates a new `FormControl` instance. - * - * @param formState Initializes the control with an initial value, - * or an object that defines the initial value and disabled state. - * - * @param validatorOrOpts A synchronous validator function, or an array of - * such functions, or an `AbstractControlOptions` object that contains validation functions - * and a validation trigger. - * - * @param asyncValidator A single async validator or array of async validator functions - * - */ + * Creates a new `FormControl` instance. + * + * @param formState Initializes the control with an initial value, + * or an object that defines the initial value and disabled state. + * + * @param validatorOrOpts A synchronous validator function, or an array of + * such functions, or an `AbstractControlOptions` object that contains validation functions + * and a validation trigger. + * + * @param asyncValidator A single async validator or array of async validator functions + * + */ constructor( formState: any = null, validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null, @@ -1060,7 +1092,7 @@ export class FormControl extends AbstractControl { emitModelToViewChange?: boolean, emitViewToModelChange?: boolean } = {}): void { - (this as{value: any}).value = this._pendingValue = value; + (this as {value: any}).value = this._pendingValue = value; if (this._onChange.length && options.emitModelToViewChange !== false) { this._onChange.forEach( (changeFn) => changeFn(this.value, options.emitViewToModelChange !== false)); @@ -1120,19 +1152,25 @@ export class FormControl extends AbstractControl { /** * @internal */ - _anyControls(condition: Function): boolean { return false; } + _anyControls(condition: Function): boolean { + return false; + } /** * @internal */ - _allControlsDisabled(): boolean { return this.disabled; } + _allControlsDisabled(): boolean { + return this.disabled; + } /** * Register a listener for change events. * * @param fn The method that is called when the value changes */ - registerOnChange(fn: Function): void { this._onChange.push(fn); } + registerOnChange(fn: Function): void { + this._onChange.push(fn); + } /** * @internal @@ -1172,11 +1210,11 @@ export class FormControl extends AbstractControl { private _applyFormState(formState: any) { if (this._isBoxedValue(formState)) { - (this as{value: any}).value = this._pendingValue = formState.value; + (this as {value: any}).value = this._pendingValue = formState.value; formState.disabled ? this.disable({onlySelf: true, emitEvent: false}) : this.enable({onlySelf: true, emitEvent: false}); } else { - (this as{value: any}).value = this._pendingValue = formState; + (this as {value: any}).value = this._pendingValue = formState; } } } @@ -1255,18 +1293,18 @@ export class FormControl extends AbstractControl { */ export class FormGroup extends AbstractControl { /** - * Creates a new `FormGroup` instance. - * - * @param controls A collection of child controls. The key for each child is the name - * under which it is registered. - * - * @param validatorOrOpts A synchronous validator function, or an array of - * such functions, or an `AbstractControlOptions` object that contains validation functions - * and a validation trigger. - * - * @param asyncValidator A single async validator or array of async validator functions - * - */ + * Creates a new `FormGroup` instance. + * + * @param controls A collection of child controls. The key for each child is the name + * under which it is registered. + * + * @param validatorOrOpts A synchronous validator function, or an array of + * such functions, or an `AbstractControlOptions` object that contains validation functions + * and a validation trigger. + * + * @param asyncValidator A single async validator or array of async validator functions + * + */ constructor( public controls: {[key: string]: AbstractControl}, validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null, @@ -1556,7 +1594,9 @@ export class FormGroup extends AbstractControl { } /** @internal */ - _updateValue(): void { (this as{value: any}).value = this._reduceValue(); } + _updateValue(): void { + (this as {value: any}).value = this._reduceValue(); + } /** @internal */ _anyControls(condition: Function): boolean { @@ -1581,8 +1621,9 @@ export class FormGroup extends AbstractControl { /** @internal */ _reduceChildren(initValue: any, fn: Function) { let res = initValue; - this._forEachChild( - (control: AbstractControl, name: string) => { res = fn(res, control, name); }); + this._forEachChild((control: AbstractControl, name: string) => { + res = fn(res, control, name); + }); return res; } @@ -1647,7 +1688,7 @@ export class FormGroup extends AbstractControl { * ], {validators: myValidator, asyncValidators: myAsyncValidator}); * ``` * - * ### Set the updateOn property for all controls in a form array + * ### Set the updateOn property for all controls in a form array * * The options object is used to set a default value for each child * control's `updateOn` property. If you set `updateOn` to `'blur'` at the @@ -1672,18 +1713,18 @@ export class FormGroup extends AbstractControl { */ export class FormArray extends AbstractControl { /** - * Creates a new `FormArray` instance. - * - * @param controls An array of child controls. Each child control is given an index - * where it is registered. - * - * @param validatorOrOpts A synchronous validator function, or an array of - * such functions, or an `AbstractControlOptions` object that contains validation functions - * and a validation trigger. - * - * @param asyncValidator A single async validator or array of async validator functions - * - */ + * Creates a new `FormArray` instance. + * + * @param controls An array of child controls. Each child control is given an index + * where it is registered. + * + * @param validatorOrOpts A synchronous validator function, or an array of + * such functions, or an `AbstractControlOptions` object that contains validation functions + * and a validation trigger. + * + * @param asyncValidator A single async validator or array of async validator functions + * + */ constructor( public controls: AbstractControl[], validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null, @@ -1702,7 +1743,9 @@ export class FormArray extends AbstractControl { * * @param index Index in the array to retrieve the control */ - at(index: number): AbstractControl { return this.controls[index]; } + at(index: number): AbstractControl { + return this.controls[index]; + } /** * Insert a new `AbstractControl` at the end of the array. @@ -1762,7 +1805,9 @@ export class FormArray extends AbstractControl { /** * Length of the control array. */ - get length(): number { return this.controls.length; } + get length(): number { + return this.controls.length; + } /** * Sets the value of the `FormArray`. It accepts an array that matches @@ -1979,12 +2024,14 @@ export class FormArray extends AbstractControl { /** @internal */ _forEachChild(cb: Function): void { - this.controls.forEach((control: AbstractControl, index: number) => { cb(control, index); }); + this.controls.forEach((control: AbstractControl, index: number) => { + cb(control, index); + }); } /** @internal */ _updateValue(): void { - (this as{value: any}).value = + (this as {value: any}).value = this.controls.filter((control) => control.enabled || this.disabled) .map((control) => control.value); } diff --git a/packages/forms/src/validators.ts b/packages/forms/src/validators.ts index 0b7a171bf3d84..a1e8f8a157e8e 100644 --- a/packages/forms/src/validators.ts +++ b/packages/forms/src/validators.ts @@ -7,8 +7,9 @@ */ import {InjectionToken, ɵisObservable as isObservable, ɵisPromise as isPromise} from '@angular/core'; -import {Observable, forkJoin, from} from 'rxjs'; +import {forkJoin, from, Observable} from 'rxjs'; import {map} from 'rxjs/operators'; + import {AsyncValidatorFn, ValidationErrors, Validator, ValidatorFn} from './directives/validators'; import {AbstractControl, FormControl} from './model'; @@ -19,7 +20,8 @@ function isEmptyInputValue(value: any): boolean { /** * @description - * An `InjectionToken` for registering additional synchronous validators used with `AbstractControl`s. + * An `InjectionToken` for registering additional synchronous validators used with + * `AbstractControl`s. * * @see `NG_ASYNC_VALIDATORS` * @@ -48,7 +50,8 @@ export const NG_VALIDATORS = new InjectionToken<Array<Validator|Function>>('NgVa /** * @description - * An `InjectionToken` for registering additional asynchronous validators used with `AbstractControl`s. + * An `InjectionToken` for registering additional asynchronous validators used with + * `AbstractControl`s. * * @see `NG_VALIDATORS` * @@ -124,7 +127,7 @@ export class Validators { * */ static min(min: number): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { + return (control: AbstractControl): ValidationErrors|null => { if (isEmptyInputValue(control.value) || isEmptyInputValue(min)) { return null; // don't validate empty values to allow optional controls } @@ -157,7 +160,7 @@ export class Validators { * */ static max(max: number): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { + return (control: AbstractControl): ValidationErrors|null => { if (isEmptyInputValue(control.value) || isEmptyInputValue(max)) { return null; // don't validate empty values to allow optional controls } @@ -221,11 +224,13 @@ 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) + * 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). + * 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 (`.`). @@ -262,7 +267,11 @@ export class Validators { * @description * Validator that requires the length of the control's value to be greater than or equal * to the provided minimum length. This validator is also provided by default if you use the - * the HTML5 `minlength` attribute. + * the HTML5 `minlength` attribute. Note that the `minLength` validator is intended to be used + * only for types that have a numeric `length` property, such as strings or arrays. The + * `minLength` validator logic is also not invoked for values when their `length` property is 0 + * (for example in case of an empty string or an empty array), to support optional controls. You + * can use the standard `required` validator if empty values should not be considered valid. * * @usageNotes * @@ -285,7 +294,7 @@ export class Validators { * */ static minLength(minLength: number): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { + return (control: AbstractControl): ValidationErrors|null => { if (isEmptyInputValue(control.value)) { return null; // don't validate empty values to allow optional controls } @@ -300,7 +309,8 @@ export class Validators { * @description * Validator that requires the length of the control's value to be less than or equal * to the provided maximum length. This validator is also provided by default if you use the - * the HTML5 `maxlength` attribute. + * the HTML5 `maxlength` attribute. Note that the `maxLength` validator is intended to be used + * only for types that have a numeric `length` property, such as strings or arrays. * * @usageNotes * @@ -323,7 +333,7 @@ export class Validators { * */ static maxLength(maxLength: number): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { + return (control: AbstractControl): ValidationErrors|null => { const length: number = control.value ? control.value.length : 0; return length > maxLength ? {'maxlength': {'requiredLength': maxLength, 'actualLength': length}} : @@ -379,7 +389,7 @@ export class Validators { regexStr = pattern.toString(); regex = pattern; } - return (control: AbstractControl): ValidationErrors | null => { + return (control: AbstractControl): ValidationErrors|null => { if (isEmptyInputValue(control.value)) { return null; // don't validate empty values to allow optional controls } @@ -396,7 +406,9 @@ export class Validators { * @see `updateValueAndValidity()` * */ - static nullValidator(control: AbstractControl): ValidationErrors|null { return null; } + static nullValidator(control: AbstractControl): ValidationErrors|null { + return null; + } /** * @description @@ -469,8 +481,8 @@ function _mergeErrors(arrayOfErrors: ValidationErrors[]): ValidationErrors|null // Not using Array.reduce here due to a Chrome 80 bug // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982 - arrayOfErrors.forEach((errors: ValidationErrors | null) => { - res = errors != null ? {...res !, ...errors} : res !; + arrayOfErrors.forEach((errors: ValidationErrors|null) => { + res = errors != null ? {...res!, ...errors} : res!; }); return Object.keys(res).length === 0 ? null : res; diff --git a/packages/forms/test/BUILD.bazel b/packages/forms/test/BUILD.bazel index f9a3325516b2f..8be33e239ce48 100644 --- a/packages/forms/test/BUILD.bazel +++ b/packages/forms/test/BUILD.bazel @@ -35,6 +35,15 @@ jasmine_node_test( karma_web_test_suite( name = "test_web", + tags = [ + # disabled on 2020-04-14 due to failure on saucelabs monitor job + # https://app.circleci.com/pipelines/github/angular/angular/13320/workflows/9ca3527a-d448-4a64-880a-fb4de9d1fece/jobs/680645 + # ``` + # IE 11.0.0 (Windows 8.1.0.0) template-driven forms integration tests basic functionality should report properties which are written outside of template bindings FAILED + # InvalidStateError: InvalidStateError + # ``` + "fixme-saucelabs-ivy", + ], deps = [ ":test_lib", ], diff --git a/packages/forms/test/directives_spec.ts b/packages/forms/test/directives_spec.ts index 0876818c2a489..06e72afbe53a5 100644 --- a/packages/forms/test/directives_spec.ts +++ b/packages/forms/test/directives_spec.ts @@ -19,22 +19,30 @@ class DummyControlValueAccessor implements ControlValueAccessor { registerOnChange(fn: any) {} registerOnTouched(fn: any) {} - writeValue(obj: any): void { this.writtenValue = obj; } + writeValue(obj: any): void { + this.writtenValue = obj; + } } class CustomValidatorDirective implements Validator { - validate(c: FormControl): ValidationErrors { return {'custom': true}; } + validate(c: FormControl): ValidationErrors { + return {'custom': true}; + } } function asyncValidator(expected: any, timeout = 0) { return (c: AbstractControl): any => { - let resolve: (result: any) => void = undefined !; - const promise = new Promise(res => { resolve = res; }); + let resolve: (result: any) => void = undefined!; + const promise = new Promise(res => { + resolve = res; + }); const res = c.value != expected ? {'async': true} : null; if (timeout == 0) { resolve(res); } else { - setTimeout(() => { resolve(res); }, timeout); + setTimeout(() => { + resolve(res); + }, timeout); } return promise; }; @@ -44,16 +52,21 @@ function asyncValidator(expected: any, timeout = 0) { describe('Form Directives', () => { let defaultAccessor: DefaultValueAccessor; - beforeEach(() => { defaultAccessor = new DefaultValueAccessor(null !, null !, null !); }); + beforeEach(() => { + defaultAccessor = new DefaultValueAccessor(null!, null!, null!); + }); describe('shared', () => { describe('selectValueAccessor', () => { let dir: NgControl; - beforeEach(() => { dir = <any>new SpyNgControl(); }); + beforeEach(() => { + dir = <any>new SpyNgControl(); + }); - it('should throw when given an empty array', - () => { expect(() => selectValueAccessor(dir, [])).toThrowError(); }); + it('should throw when given an empty array', () => { + expect(() => selectValueAccessor(dir, [])).toThrowError(); + }); it('should throw when accessor is not provided as array', () => { expect(() => selectValueAccessor(dir, {} as any[])) @@ -61,49 +74,51 @@ function asyncValidator(expected: any, timeout = 0) { `Value accessor was not provided as an array for form control with unspecified name attribute`); }); - it('should return the default value accessor when no other provided', - () => { expect(selectValueAccessor(dir, [defaultAccessor])).toEqual(defaultAccessor); }); + it('should return the default value accessor when no other provided', () => { + expect(selectValueAccessor(dir, [defaultAccessor])).toEqual(defaultAccessor); + }); it('should return checkbox accessor when provided', () => { - const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !); + const checkboxAccessor = new CheckboxControlValueAccessor(null!, null!); expect(selectValueAccessor(dir, [ defaultAccessor, checkboxAccessor ])).toEqual(checkboxAccessor); }); it('should return select accessor when provided', () => { - const selectAccessor = new SelectControlValueAccessor(null !, null !); + const selectAccessor = new SelectControlValueAccessor(null!, null!); expect(selectValueAccessor(dir, [ defaultAccessor, selectAccessor ])).toEqual(selectAccessor); }); it('should return select multiple accessor when provided', () => { - const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null !, null !); + const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null!, null!); expect(selectValueAccessor(dir, [ defaultAccessor, selectMultipleAccessor ])).toEqual(selectMultipleAccessor); }); it('should throw when more than one build-in accessor is provided', () => { - const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !); - const selectAccessor = new SelectControlValueAccessor(null !, null !); + const checkboxAccessor = new CheckboxControlValueAccessor(null!, null!); + const selectAccessor = new SelectControlValueAccessor(null!, null!); expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError(); }); it('should return custom accessor when provided', () => { const customAccessor: ControlValueAccessor = new SpyValueAccessor() as any; - const checkboxAccessor = new CheckboxControlValueAccessor(null !, null !); - expect(selectValueAccessor(dir, <any>[defaultAccessor, customAccessor, checkboxAccessor])) - .toEqual(customAccessor); + const checkboxAccessor = new CheckboxControlValueAccessor(null!, null!); + expect(selectValueAccessor(dir, <any>[ + defaultAccessor, customAccessor, checkboxAccessor + ])).toEqual(customAccessor); }); it('should return custom accessor when provided with select multiple', () => { const customAccessor: ControlValueAccessor = new SpyValueAccessor() as any; - const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null !, null !); - expect(selectValueAccessor( - dir, <any>[defaultAccessor, customAccessor, selectMultipleAccessor])) - .toEqual(customAccessor); + const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null!, null!); + expect(selectValueAccessor(dir, <any>[ + defaultAccessor, customAccessor, selectMultipleAccessor + ])).toEqual(customAccessor); }); it('should throw when more than one custom accessor is provided', () => { @@ -116,13 +131,13 @@ function asyncValidator(expected: any, timeout = 0) { it('should compose functions', () => { const dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true}); const dummy2 = (_: any /** TODO #9100 */) => ({'dummy2': true}); - const v = composeValidators([dummy1, dummy2]) !; + const v = composeValidators([dummy1, dummy2])!; expect(v(new FormControl(''))).toEqual({'dummy1': true, 'dummy2': true}); }); it('should compose validator directives', () => { const dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true}); - const v = composeValidators([dummy1, new CustomValidatorDirective()]) !; + const v = composeValidators([dummy1, new CustomValidatorDirective()])!; expect(v(new FormControl(''))).toEqual({'dummy1': true, 'custom': true}); }); }); @@ -137,8 +152,8 @@ function asyncValidator(expected: any, timeout = 0) { form = new FormGroupDirective([], []); formModel = new FormGroup({ 'login': new FormControl(), - 'passwords': new FormGroup( - {'password': new FormControl(), 'passwordConfirm': new FormControl()}) + 'passwords': + new FormGroup({'password': new FormControl(), 'passwordConfirm': new FormControl()}) }); form.form = formModel; @@ -174,7 +189,7 @@ function asyncValidator(expected: any, timeout = 0) { describe('addControl', () => { it('should throw when no control found', () => { - const dir = new FormControlName(form, null !, null !, [defaultAccessor], null); + const dir = new FormControlName(form, null!, null!, [defaultAccessor], null); dir.name = 'invalidName'; expect(() => form.addControl(dir)) @@ -182,7 +197,7 @@ function asyncValidator(expected: any, timeout = 0) { }); it('should throw for a named control when no value accessor', () => { - const dir = new FormControlName(form, null !, null !, null !, null); + const dir = new FormControlName(form, null!, null!, null!, null); dir.name = 'login'; expect(() => form.addControl(dir)) @@ -190,8 +205,8 @@ function asyncValidator(expected: any, timeout = 0) { }); it('should throw when no value accessor with path', () => { - const group = new FormGroupName(form, null !, null !); - const dir = new FormControlName(group, null !, null !, null !, null); + const group = new FormGroupName(form, null!, null!); + const dir = new FormControlName(group, null!, null!, null!, null); group.name = 'passwords'; dir.name = 'password'; @@ -321,7 +336,7 @@ function asyncValidator(expected: any, timeout = 0) { personControlGroupDir = new NgModelGroup(form, [], []); personControlGroupDir.name = 'person'; - loginControlDir = new NgModel(personControlGroupDir, null !, null !, [defaultAccessor]); + loginControlDir = new NgModel(personControlGroupDir, null!, null!, [defaultAccessor]); loginControlDir.name = 'login'; loginControlDir.valueAccessor = new DummyControlValueAccessor(); }); @@ -509,7 +524,9 @@ function asyncValidator(expected: any, timeout = 0) { controlDir.form = control; }); - it('should reexport control properties', () => { checkProperties(control); }); + it('should reexport control properties', () => { + checkProperties(control); + }); it('should reexport control methods', () => { expect(controlDir.hasError('required')).toBe(control.hasError('required')); @@ -544,7 +561,7 @@ function asyncValidator(expected: any, timeout = 0) { beforeEach(() => { ngModel = new NgModel( - null !, [Validators.required], [asyncValidator('expected')], [defaultAccessor]); + null!, [Validators.required], [asyncValidator('expected')], [defaultAccessor]); ngModel.valueAccessor = new DummyControlValueAccessor(); control = ngModel.control; }); @@ -577,7 +594,7 @@ function asyncValidator(expected: any, timeout = 0) { }); it('should throw when no value accessor with named control', () => { - const namedDir = new NgModel(null !, null !, null !, null !); + const namedDir = new NgModel(null!, null!, null!, null!); namedDir.name = 'one'; expect(() => namedDir.ngOnChanges({})) @@ -585,7 +602,7 @@ function asyncValidator(expected: any, timeout = 0) { }); it('should throw when no value accessor with unnamed control', () => { - const unnamedDir = new NgModel(null !, null !, null !, null !); + const unnamedDir = new NgModel(null!, null!, null!, null!); expect(() => unnamedDir.ngOnChanges({})) .toThrowError( @@ -641,7 +658,6 @@ function asyncValidator(expected: any, timeout = 0) { ngModel.ngOnChanges({isDisabled: new SimpleChange(null, 'anything else', false)}); tick(); expect(ngModel.control.disabled).toEqual(true); - })); }); @@ -656,7 +672,7 @@ function asyncValidator(expected: any, timeout = 0) { parent.form = new FormGroup({'name': formModel}); controlNameDir = new FormControlName(parent, [], [], [defaultAccessor], null); controlNameDir.name = 'name'; - (controlNameDir as{control: FormControl}).control = formModel; + (controlNameDir as {control: FormControl}).control = formModel; }); it('should reexport control properties', () => { diff --git a/packages/forms/test/form_array_spec.ts b/packages/forms/test/form_array_spec.ts index 6bac92e453617..1bdc729233621 100644 --- a/packages/forms/test/form_array_spec.ts +++ b/packages/forms/test/form_array_spec.ts @@ -10,1229 +10,1263 @@ import {fakeAsync, tick} from '@angular/core/testing'; import {AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; import {AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, ValidatorFn} from '@angular/forms'; import {Validators} from '@angular/forms/src/validators'; -import {of } from 'rxjs'; +import {of} from 'rxjs'; (function() { - function asyncValidator(expected: string, timeouts = {}) { - return (c: AbstractControl) => { - let resolve: (result: any) => void = undefined !; - const promise = new Promise<ValidationErrors|null>(res => { resolve = res; }); - const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; - const res = c.value != expected ? {'async': true} : null; - - if (t == 0) { +function asyncValidator(expected: string, timeouts = {}) { + return (c: AbstractControl) => { + let resolve: (result: any) => void = undefined!; + const promise = new Promise<ValidationErrors|null>(res => { + resolve = res; + }); + const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; + const res = c.value != expected ? {'async': true} : null; + + if (t == 0) { + resolve(res); + } else { + setTimeout(() => { resolve(res); - } else { - setTimeout(() => { resolve(res); }, t); - } + }, t); + } + + return promise; + }; +} + +describe('FormArray', () => { + describe('adding/removing', () => { + let a: FormArray; + let c1: FormControl, c2: FormControl, c3: FormControl; + + beforeEach(() => { + a = new FormArray([]); + c1 = new FormControl(1); + c2 = new FormControl(2); + c3 = new FormControl(3); + }); - return promise; - }; - } + it('should support pushing', () => { + a.push(c1); + expect(a.length).toEqual(1); + expect(a.controls).toEqual([c1]); + }); - describe('FormArray', () => { + it('should support removing', () => { + a.push(c1); + a.push(c2); + a.push(c3); - describe('adding/removing', () => { - let a: FormArray; - let c1: FormControl, c2: FormControl, c3: FormControl; + a.removeAt(1); - beforeEach(() => { - a = new FormArray([]); - c1 = new FormControl(1); - c2 = new FormControl(2); - c3 = new FormControl(3); - }); + expect(a.controls).toEqual([c1, c3]); + }); - it('should support pushing', () => { - a.push(c1); - expect(a.length).toEqual(1); - expect(a.controls).toEqual([c1]); - }); + it('should support clearing', () => { + a.push(c1); + a.push(c2); + a.push(c3); - it('should support removing', () => { - a.push(c1); - a.push(c2); - a.push(c3); + a.clear(); - a.removeAt(1); + expect(a.controls).toEqual([]); - expect(a.controls).toEqual([c1, c3]); - }); + a.clear(); - it('should support clearing', () => { - a.push(c1); - a.push(c2); - a.push(c3); + expect(a.controls).toEqual([]); + }); - a.clear(); + it('should support inserting', () => { + a.push(c1); + a.push(c3); - expect(a.controls).toEqual([]); + a.insert(1, c2); - a.clear(); + expect(a.controls).toEqual([c1, c2, c3]); + }); + }); - expect(a.controls).toEqual([]); - }); + describe('value', () => { + it('should be the reduced value of the child controls', () => { + const a = new FormArray([new FormControl(1), new FormControl(2)]); + expect(a.value).toEqual([1, 2]); + }); + + it('should be an empty array when there are no child controls', () => { + const a = new FormArray([]); + expect(a.value).toEqual([]); + }); + }); - it('should support inserting', () => { - a.push(c1); - a.push(c3); + describe('getRawValue()', () => { + let a: FormArray; - a.insert(1, c2); + it('should work with nested form groups/arrays', () => { + a = new FormArray([ + new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}), + new FormArray([new FormControl('v4'), new FormControl('v5')]) + ]); + a.at(0).get('c3')!.disable(); + (a.at(1) as FormArray).at(1).disable(); - expect(a.controls).toEqual([c1, c2, c3]); - }); + expect(a.getRawValue()).toEqual([{'c2': 'v2', 'c3': 'v3'}, ['v4', 'v5']]); }); + }); - describe('value', () => { - it('should be the reduced value of the child controls', () => { - const a = new FormArray([new FormControl(1), new FormControl(2)]); - expect(a.value).toEqual([1, 2]); - }); + describe('markAllAsTouched', () => { + it('should mark all descendants as touched', () => { + const formArray: FormArray = new FormArray([ + new FormControl('v1'), new FormControl('v2'), new FormGroup({'c1': new FormControl('v1')}), + new FormArray([new FormGroup({'c2': new FormControl('v2')})]) + ]); - it('should be an empty array when there are no child controls', () => { - const a = new FormArray([]); - expect(a.value).toEqual([]); - }); - }); + expect(formArray.touched).toBe(false); - describe('getRawValue()', () => { - let a: FormArray; + const control1 = formArray.at(0) as FormControl; - it('should work with nested form groups/arrays', () => { - a = new FormArray([ - new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}), - new FormArray([new FormControl('v4'), new FormControl('v5')]) - ]); - a.at(0).get('c3') !.disable(); - (a.at(1) as FormArray).at(1).disable(); + expect(control1.touched).toBe(false); - expect(a.getRawValue()).toEqual([{'c2': 'v2', 'c3': 'v3'}, ['v4', 'v5']]); - }); - }); + const group1 = formArray.at(2) as FormGroup; + + expect(group1.touched).toBe(false); + + const group1Control1 = group1.get('c1') as FormControl; + + expect(group1Control1.touched).toBe(false); + + const innerFormArray = formArray.at(3) as FormArray; - describe('markAllAsTouched', () => { - it('should mark all descendants as touched', () => { - const formArray: FormArray = new FormArray([ - new FormControl('v1'), new FormControl('v2'), - new FormGroup({'c1': new FormControl('v1')}), - new FormArray([new FormGroup({'c2': new FormControl('v2')})]) - ]); + expect(innerFormArray.touched).toBe(false); - expect(formArray.touched).toBe(false); + const innerFormArrayGroup = innerFormArray.at(0) as FormGroup; - const control1 = formArray.at(0) as FormControl; + expect(innerFormArrayGroup.touched).toBe(false); - expect(control1.touched).toBe(false); + const innerFormArrayGroupControl1 = innerFormArrayGroup.get('c2') as FormControl; - const group1 = formArray.at(2) as FormGroup; + expect(innerFormArrayGroupControl1.touched).toBe(false); - expect(group1.touched).toBe(false); + formArray.markAllAsTouched(); - const group1Control1 = group1.get('c1') as FormControl; + expect(formArray.touched).toBe(true); - expect(group1Control1.touched).toBe(false); + expect(control1.touched).toBe(true); - const innerFormArray = formArray.at(3) as FormArray; + expect(group1.touched).toBe(true); - expect(innerFormArray.touched).toBe(false); + expect(group1Control1.touched).toBe(true); - const innerFormArrayGroup = innerFormArray.at(0) as FormGroup; + expect(innerFormArray.touched).toBe(true); - expect(innerFormArrayGroup.touched).toBe(false); + expect(innerFormArrayGroup.touched).toBe(true); - const innerFormArrayGroupControl1 = innerFormArrayGroup.get('c2') as FormControl; + expect(innerFormArrayGroupControl1.touched).toBe(true); + }); + }); - expect(innerFormArrayGroupControl1.touched).toBe(false); + describe('setValue', () => { + let c: FormControl, c2: FormControl, a: FormArray; - formArray.markAllAsTouched(); + beforeEach(() => { + c = new FormControl(''); + c2 = new FormControl(''); + a = new FormArray([c, c2]); + }); - expect(formArray.touched).toBe(true); + it('should set its own value', () => { + a.setValue(['one', 'two']); + expect(a.value).toEqual(['one', 'two']); + }); - expect(control1.touched).toBe(true); + it('should set child values', () => { + a.setValue(['one', 'two']); + expect(c.value).toEqual('one'); + expect(c2.value).toEqual('two'); + }); - expect(group1.touched).toBe(true); + it('should set values for disabled child controls', () => { + c2.disable(); + a.setValue(['one', 'two']); + expect(c2.value).toEqual('two'); + expect(a.value).toEqual(['one']); + expect(a.getRawValue()).toEqual(['one', 'two']); + }); - expect(group1Control1.touched).toBe(true); + it('should set value for disabled arrays', () => { + a.disable(); + a.setValue(['one', 'two']); + expect(c.value).toEqual('one'); + expect(c2.value).toEqual('two'); + expect(a.value).toEqual(['one', 'two']); + }); - expect(innerFormArray.touched).toBe(true); + it('should set parent values', () => { + const form = new FormGroup({'parent': a}); + a.setValue(['one', 'two']); + expect(form.value).toEqual({'parent': ['one', 'two']}); + }); - expect(innerFormArrayGroup.touched).toBe(true); + it('should not update the parent explicitly specified', () => { + const form = new FormGroup({'parent': a}); + a.setValue(['one', 'two'], {onlySelf: true}); - expect(innerFormArrayGroupControl1.touched).toBe(true); - }); + expect(form.value).toEqual({parent: ['', '']}); + }); + + it('should throw if fields are missing from supplied value (subset)', () => { + expect(() => a.setValue([, 'two'])) + .toThrowError(new RegExp(`Must supply a value for form control at index: 0`)); + }); + + it('should throw if a value is provided for a missing control (superset)', () => { + expect(() => a.setValue([ + 'one', 'two', 'three' + ])).toThrowError(new RegExp(`Cannot find form control at index 2`)); }); - describe('setValue', () => { - let c: FormControl, c2: FormControl, a: FormArray; + it('should throw if a value is not provided for a disabled control', () => { + c2.disable(); + expect(() => a.setValue(['one'])) + .toThrowError(new RegExp(`Must supply a value for form control at index: 1`)); + }); + + it('should throw if no controls are set yet', () => { + const empty = new FormArray([]); + expect(() => empty.setValue(['one'])) + .toThrowError(new RegExp(`no form controls registered with this array`)); + }); + + describe('setValue() events', () => { + let form: FormGroup; + let logger: any[]; beforeEach(() => { - c = new FormControl(''); - c2 = new FormControl(''); - a = new FormArray([c, c2]); + form = new FormGroup({'parent': a}); + logger = []; }); - it('should set its own value', () => { - a.setValue(['one', 'two']); - expect(a.value).toEqual(['one', 'two']); - }); + it('should emit one valueChange event per control', () => { + form.valueChanges.subscribe(() => logger.push('form')); + a.valueChanges.subscribe(() => logger.push('array')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); - it('should set child values', () => { a.setValue(['one', 'two']); - expect(c.value).toEqual('one'); - expect(c2.value).toEqual('two'); + expect(logger).toEqual(['control1', 'control2', 'array', 'form']); }); - it('should set values for disabled child controls', () => { - c2.disable(); - a.setValue(['one', 'two']); - expect(c2.value).toEqual('two'); - expect(a.value).toEqual(['one']); - expect(a.getRawValue()).toEqual(['one', 'two']); - }); + it('should not fire an event when explicitly specified', fakeAsync(() => { + form.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + a.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c2.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); - it('should set value for disabled arrays', () => { - a.disable(); - a.setValue(['one', 'two']); - expect(c.value).toEqual('one'); - expect(c2.value).toEqual('two'); - expect(a.value).toEqual(['one', 'two']); - }); + a.setValue(['one', 'two'], {emitEvent: false}); + tick(); + })); + + it('should emit one statusChange event per control', () => { + form.statusChanges.subscribe(() => logger.push('form')); + a.statusChanges.subscribe(() => logger.push('array')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); - it('should set parent values', () => { - const form = new FormGroup({'parent': a}); a.setValue(['one', 'two']); - expect(form.value).toEqual({'parent': ['one', 'two']}); + expect(logger).toEqual(['control1', 'control2', 'array', 'form']); }); + }); + }); - it('should not update the parent explicitly specified', () => { - const form = new FormGroup({'parent': a}); - a.setValue(['one', 'two'], {onlySelf: true}); + describe('patchValue', () => { + let c: FormControl, c2: FormControl, a: FormArray; - expect(form.value).toEqual({parent: ['', '']}); - }); + beforeEach(() => { + c = new FormControl(''); + c2 = new FormControl(''); + a = new FormArray([c, c2]); + }); - it('should throw if fields are missing from supplied value (subset)', () => { - expect(() => a.setValue([, 'two'])) - .toThrowError(new RegExp(`Must supply a value for form control at index: 0`)); - }); + it('should set its own value', () => { + a.patchValue(['one', 'two']); + expect(a.value).toEqual(['one', 'two']); + }); - it('should throw if a value is provided for a missing control (superset)', () => { - expect(() => a.setValue([ - 'one', 'two', 'three' - ])).toThrowError(new RegExp(`Cannot find form control at index 2`)); - }); + it('should set child values', () => { + a.patchValue(['one', 'two']); + expect(c.value).toEqual('one'); + expect(c2.value).toEqual('two'); + }); - it('should throw if a value is not provided for a disabled control', () => { - c2.disable(); - expect(() => a.setValue(['one'])) - .toThrowError(new RegExp(`Must supply a value for form control at index: 1`)); - }); + it('should patch disabled control values', () => { + c2.disable(); + a.patchValue(['one', 'two']); + expect(c2.value).toEqual('two'); + expect(a.value).toEqual(['one']); + expect(a.getRawValue()).toEqual(['one', 'two']); + }); - it('should throw if no controls are set yet', () => { - const empty = new FormArray([]); - expect(() => empty.setValue(['one'])) - .toThrowError(new RegExp(`no form controls registered with this array`)); - }); + it('should patch disabled control arrays', () => { + a.disable(); + a.patchValue(['one', 'two']); + expect(c.value).toEqual('one'); + expect(c2.value).toEqual('two'); + expect(a.value).toEqual(['one', 'two']); + }); - describe('setValue() events', () => { - let form: FormGroup; - let logger: any[]; - - beforeEach(() => { - form = new FormGroup({'parent': a}); - logger = []; - }); - - it('should emit one valueChange event per control', () => { - form.valueChanges.subscribe(() => logger.push('form')); - a.valueChanges.subscribe(() => logger.push('array')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); - - a.setValue(['one', 'two']); - expect(logger).toEqual(['control1', 'control2', 'array', 'form']); - }); - - it('should not fire an event when explicitly specified', fakeAsync(() => { - form.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - a.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c2.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - - a.setValue(['one', 'two'], {emitEvent: false}); - tick(); - })); - - it('should emit one statusChange event per control', () => { - form.statusChanges.subscribe(() => logger.push('form')); - a.statusChanges.subscribe(() => logger.push('array')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); - - a.setValue(['one', 'two']); - expect(logger).toEqual(['control1', 'control2', 'array', 'form']); - }); - }); + it('should set parent values', () => { + const form = new FormGroup({'parent': a}); + a.patchValue(['one', 'two']); + expect(form.value).toEqual({'parent': ['one', 'two']}); + }); + + it('should not update the parent explicitly specified', () => { + const form = new FormGroup({'parent': a}); + a.patchValue(['one', 'two'], {onlySelf: true}); + + expect(form.value).toEqual({parent: ['', '']}); + }); + + it('should ignore fields that are missing from supplied value (subset)', () => { + a.patchValue([, 'two']); + expect(a.value).toEqual(['', 'two']); + }); + + it('should not ignore fields that are null', () => { + a.patchValue([null]); + expect(a.value).toEqual([null, '']); }); - describe('patchValue', () => { - let c: FormControl, c2: FormControl, a: FormArray; + it('should ignore any value provided for a missing control (superset)', () => { + a.patchValue([, , 'three']); + expect(a.value).toEqual(['', '']); + }); + + describe('patchValue() events', () => { + let form: FormGroup; + let logger: any[]; beforeEach(() => { - c = new FormControl(''); - c2 = new FormControl(''); - a = new FormArray([c, c2]); + form = new FormGroup({'parent': a}); + logger = []; }); - it('should set its own value', () => { - a.patchValue(['one', 'two']); - expect(a.value).toEqual(['one', 'two']); - }); + it('should emit one valueChange event per control', () => { + form.valueChanges.subscribe(() => logger.push('form')); + a.valueChanges.subscribe(() => logger.push('array')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); - it('should set child values', () => { a.patchValue(['one', 'two']); - expect(c.value).toEqual('one'); - expect(c2.value).toEqual('two'); + expect(logger).toEqual(['control1', 'control2', 'array', 'form']); }); - it('should patch disabled control values', () => { - c2.disable(); - a.patchValue(['one', 'two']); - expect(c2.value).toEqual('two'); - expect(a.value).toEqual(['one']); - expect(a.getRawValue()).toEqual(['one', 'two']); - }); + it('should not emit valueChange events for skipped controls', () => { + form.valueChanges.subscribe(() => logger.push('form')); + a.valueChanges.subscribe(() => logger.push('array')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); - it('should patch disabled control arrays', () => { - a.disable(); - a.patchValue(['one', 'two']); - expect(c.value).toEqual('one'); - expect(c2.value).toEqual('two'); - expect(a.value).toEqual(['one', 'two']); + a.patchValue(['one']); + expect(logger).toEqual(['control1', 'array', 'form']); }); - it('should set parent values', () => { - const form = new FormGroup({'parent': a}); + it('should not fire an event when explicitly specified', fakeAsync(() => { + form.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + a.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c2.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + + a.patchValue(['one', 'two'], {emitEvent: false}); + tick(); + })); + + it('should emit one statusChange event per control', () => { + form.statusChanges.subscribe(() => logger.push('form')); + a.statusChanges.subscribe(() => logger.push('array')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); + a.patchValue(['one', 'two']); - expect(form.value).toEqual({'parent': ['one', 'two']}); + expect(logger).toEqual(['control1', 'control2', 'array', 'form']); }); + }); + }); - it('should not update the parent explicitly specified', () => { - const form = new FormGroup({'parent': a}); - a.patchValue(['one', 'two'], {onlySelf: true}); + describe('reset()', () => { + let c: FormControl, c2: FormControl, a: FormArray; - expect(form.value).toEqual({parent: ['', '']}); - }); + beforeEach(() => { + c = new FormControl('initial value'); + c2 = new FormControl(''); + a = new FormArray([c, c2]); + }); - it('should ignore fields that are missing from supplied value (subset)', () => { - a.patchValue([, 'two']); - expect(a.value).toEqual(['', 'two']); - }); + it('should set its own value if value passed', () => { + a.setValue(['new value', 'new value']); - it('should not ignore fields that are null', () => { - a.patchValue([null]); - expect(a.value).toEqual([null, '']); - }); + a.reset(['initial value', '']); + expect(a.value).toEqual(['initial value', '']); + }); - it('should ignore any value provided for a missing control (superset)', () => { - a.patchValue([, , 'three']); - expect(a.value).toEqual(['', '']); - }); + it('should not update the parent when explicitly specified', () => { + const form = new FormGroup({'a': a}); + a.reset(['one', 'two'], {onlySelf: true}); - describe('patchValue() events', () => { - let form: FormGroup; - let logger: any[]; - - beforeEach(() => { - form = new FormGroup({'parent': a}); - logger = []; - }); - - it('should emit one valueChange event per control', () => { - form.valueChanges.subscribe(() => logger.push('form')); - a.valueChanges.subscribe(() => logger.push('array')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); - - a.patchValue(['one', 'two']); - expect(logger).toEqual(['control1', 'control2', 'array', 'form']); - }); - - it('should not emit valueChange events for skipped controls', () => { - form.valueChanges.subscribe(() => logger.push('form')); - a.valueChanges.subscribe(() => logger.push('array')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); - - a.patchValue(['one']); - expect(logger).toEqual(['control1', 'array', 'form']); - }); - - it('should not fire an event when explicitly specified', fakeAsync(() => { - form.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - a.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c2.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - - a.patchValue(['one', 'two'], {emitEvent: false}); - tick(); - })); - - it('should emit one statusChange event per control', () => { - form.statusChanges.subscribe(() => logger.push('form')); - a.statusChanges.subscribe(() => logger.push('array')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); - - a.patchValue(['one', 'two']); - expect(logger).toEqual(['control1', 'control2', 'array', 'form']); - }); - }); + expect(form.value).toEqual({a: ['initial value', '']}); }); - describe('reset()', () => { - let c: FormControl, c2: FormControl, a: FormArray; + it('should set its own value if boxed value passed', () => { + a.setValue(['new value', 'new value']); - beforeEach(() => { - c = new FormControl('initial value'); - c2 = new FormControl(''); - a = new FormArray([c, c2]); - }); + a.reset([{value: 'initial value', disabled: false}, '']); + expect(a.value).toEqual(['initial value', '']); + }); - it('should set its own value if value passed', () => { - a.setValue(['new value', 'new value']); + it('should clear its own value if no value passed', () => { + a.setValue(['new value', 'new value']); - a.reset(['initial value', '']); - expect(a.value).toEqual(['initial value', '']); - }); + a.reset(); + expect(a.value).toEqual([null, null]); + }); - it('should not update the parent when explicitly specified', () => { - const form = new FormGroup({'a': a}); - a.reset(['one', 'two'], {onlySelf: true}); + it('should set the value of each of its child controls if value passed', () => { + a.setValue(['new value', 'new value']); - expect(form.value).toEqual({a: ['initial value', '']}); - }); + a.reset(['initial value', '']); + expect(c.value).toBe('initial value'); + expect(c2.value).toBe(''); + }); - it('should set its own value if boxed value passed', () => { - a.setValue(['new value', 'new value']); + it('should clear the value of each of its child controls if no value', () => { + a.setValue(['new value', 'new value']); - a.reset([{value: 'initial value', disabled: false}, '']); - expect(a.value).toEqual(['initial value', '']); - }); + a.reset(); + expect(c.value).toBe(null); + expect(c2.value).toBe(null); + }); - it('should clear its own value if no value passed', () => { - a.setValue(['new value', 'new value']); + it('should set the value of its parent if value passed', () => { + const form = new FormGroup({'a': a}); + a.setValue(['new value', 'new value']); - a.reset(); - expect(a.value).toEqual([null, null]); - }); + a.reset(['initial value', '']); + expect(form.value).toEqual({'a': ['initial value', '']}); + }); - it('should set the value of each of its child controls if value passed', () => { - a.setValue(['new value', 'new value']); + it('should clear the value of its parent if no value passed', () => { + const form = new FormGroup({'a': a}); + a.setValue(['new value', 'new value']); - a.reset(['initial value', '']); - expect(c.value).toBe('initial value'); - expect(c2.value).toBe(''); - }); + a.reset(); + expect(form.value).toEqual({'a': [null, null]}); + }); - it('should clear the value of each of its child controls if no value', () => { - a.setValue(['new value', 'new value']); + it('should mark itself as pristine', () => { + a.markAsDirty(); + expect(a.pristine).toBe(false); - a.reset(); - expect(c.value).toBe(null); - expect(c2.value).toBe(null); - }); + a.reset(); + expect(a.pristine).toBe(true); + }); - it('should set the value of its parent if value passed', () => { - const form = new FormGroup({'a': a}); - a.setValue(['new value', 'new value']); + it('should mark all child controls as pristine', () => { + c.markAsDirty(); + c2.markAsDirty(); + expect(c.pristine).toBe(false); + expect(c2.pristine).toBe(false); - a.reset(['initial value', '']); - expect(form.value).toEqual({'a': ['initial value', '']}); - }); + a.reset(); + expect(c.pristine).toBe(true); + expect(c2.pristine).toBe(true); + }); - it('should clear the value of its parent if no value passed', () => { - const form = new FormGroup({'a': a}); - a.setValue(['new value', 'new value']); + it('should mark the parent as pristine if all siblings pristine', () => { + const c3 = new FormControl(''); + const form = new FormGroup({'a': a, 'c3': c3}); - a.reset(); - expect(form.value).toEqual({'a': [null, null]}); - }); + a.markAsDirty(); + expect(form.pristine).toBe(false); - it('should mark itself as pristine', () => { - a.markAsDirty(); - expect(a.pristine).toBe(false); + a.reset(); + expect(form.pristine).toBe(true); + }); - a.reset(); - expect(a.pristine).toBe(true); - }); + it('should not mark the parent pristine if any dirty siblings', () => { + const c3 = new FormControl(''); + const form = new FormGroup({'a': a, 'c3': c3}); - it('should mark all child controls as pristine', () => { - c.markAsDirty(); - c2.markAsDirty(); - expect(c.pristine).toBe(false); - expect(c2.pristine).toBe(false); + a.markAsDirty(); + c3.markAsDirty(); + expect(form.pristine).toBe(false); - a.reset(); - expect(c.pristine).toBe(true); - expect(c2.pristine).toBe(true); - }); + a.reset(); + expect(form.pristine).toBe(false); + }); - it('should mark the parent as pristine if all siblings pristine', () => { - const c3 = new FormControl(''); - const form = new FormGroup({'a': a, 'c3': c3}); + it('should mark itself as untouched', () => { + a.markAsTouched(); + expect(a.untouched).toBe(false); - a.markAsDirty(); - expect(form.pristine).toBe(false); + a.reset(); + expect(a.untouched).toBe(true); + }); - a.reset(); - expect(form.pristine).toBe(true); - }); + it('should mark all child controls as untouched', () => { + c.markAsTouched(); + c2.markAsTouched(); + expect(c.untouched).toBe(false); + expect(c2.untouched).toBe(false); + + a.reset(); + expect(c.untouched).toBe(true); + expect(c2.untouched).toBe(true); + }); - it('should not mark the parent pristine if any dirty siblings', () => { - const c3 = new FormControl(''); - const form = new FormGroup({'a': a, 'c3': c3}); + it('should mark the parent untouched if all siblings untouched', () => { + const c3 = new FormControl(''); + const form = new FormGroup({'a': a, 'c3': c3}); - a.markAsDirty(); - c3.markAsDirty(); - expect(form.pristine).toBe(false); + a.markAsTouched(); + expect(form.untouched).toBe(false); - a.reset(); - expect(form.pristine).toBe(false); - }); + a.reset(); + expect(form.untouched).toBe(true); + }); - it('should mark itself as untouched', () => { - a.markAsTouched(); - expect(a.untouched).toBe(false); + it('should not mark the parent untouched if any touched siblings', () => { + const c3 = new FormControl(''); + const form = new FormGroup({'a': a, 'c3': c3}); - a.reset(); - expect(a.untouched).toBe(true); + a.markAsTouched(); + c3.markAsTouched(); + expect(form.untouched).toBe(false); + + a.reset(); + expect(form.untouched).toBe(false); + }); + + it('should retain previous disabled state', () => { + a.disable(); + a.reset(); + + expect(a.disabled).toBe(true); + }); + + it('should set child disabled state if boxed value passed', () => { + a.disable(); + a.reset([{value: '', disabled: false}, '']); + + expect(c.disabled).toBe(false); + expect(a.disabled).toBe(false); + }); + + + describe('reset() events', () => { + let form: FormGroup, c3: FormControl, logger: any[]; + + beforeEach(() => { + c3 = new FormControl(''); + form = new FormGroup({'a': a, 'c3': c3}); + logger = []; }); - it('should mark all child controls as untouched', () => { - c.markAsTouched(); - c2.markAsTouched(); - expect(c.untouched).toBe(false); - expect(c2.untouched).toBe(false); + it('should emit one valueChange event per reset control', () => { + form.valueChanges.subscribe(() => logger.push('form')); + a.valueChanges.subscribe(() => logger.push('array')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); + c3.valueChanges.subscribe(() => logger.push('control3')); a.reset(); - expect(c.untouched).toBe(true); - expect(c2.untouched).toBe(true); + expect(logger).toEqual(['control1', 'control2', 'array', 'form']); }); - it('should mark the parent untouched if all siblings untouched', () => { - const c3 = new FormControl(''); - const form = new FormGroup({'a': a, 'c3': c3}); + it('should not fire an event when explicitly specified', fakeAsync(() => { + form.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + a.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c2.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c3.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + + a.reset([], {emitEvent: false}); + tick(); + })); - a.markAsTouched(); - expect(form.untouched).toBe(false); + it('should emit one statusChange event per reset control', () => { + form.statusChanges.subscribe(() => logger.push('form')); + a.statusChanges.subscribe(() => logger.push('array')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); + c3.statusChanges.subscribe(() => logger.push('control3')); a.reset(); - expect(form.untouched).toBe(true); + expect(logger).toEqual(['control1', 'control2', 'array', 'form']); }); - it('should not mark the parent untouched if any touched siblings', () => { - const c3 = new FormControl(''); - const form = new FormGroup({'a': a, 'c3': c3}); + it('should mark as pristine and not dirty before emitting valueChange and statusChange events when resetting', + () => { + const pristineAndNotDirty = () => { + expect(a.pristine).toBe(true); + expect(a.dirty).toBe(false); + }; - a.markAsTouched(); - c3.markAsTouched(); - expect(form.untouched).toBe(false); + c2.markAsDirty(); + expect(a.pristine).toBe(false); + expect(a.dirty).toBe(true); - a.reset(); - expect(form.untouched).toBe(false); - }); + a.valueChanges.subscribe(pristineAndNotDirty); + a.statusChanges.subscribe(pristineAndNotDirty); - it('should retain previous disabled state', () => { - a.disable(); - a.reset(); + a.reset(); + }); + }); + }); - expect(a.disabled).toBe(true); - }); + describe('errors', () => { + it('should run the validator when the value changes', () => { + const simpleValidator = (c: FormArray) => + c.controls[0].value != 'correct' ? {'broken': true} : null; - it('should set child disabled state if boxed value passed', () => { - a.disable(); - a.reset([{value: '', disabled: false}, '']); + const c = new FormControl(null); + const g = new FormArray([c], simpleValidator as ValidatorFn); - expect(c.disabled).toBe(false); - expect(a.disabled).toBe(false); - }); + c.setValue('correct'); + expect(g.valid).toEqual(true); + expect(g.errors).toEqual(null); - describe('reset() events', () => { - let form: FormGroup, c3: FormControl, logger: any[]; - - beforeEach(() => { - c3 = new FormControl(''); - form = new FormGroup({'a': a, 'c3': c3}); - logger = []; - }); - - it('should emit one valueChange event per reset control', () => { - form.valueChanges.subscribe(() => logger.push('form')); - a.valueChanges.subscribe(() => logger.push('array')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); - c3.valueChanges.subscribe(() => logger.push('control3')); - - a.reset(); - expect(logger).toEqual(['control1', 'control2', 'array', 'form']); - }); - - it('should not fire an event when explicitly specified', fakeAsync(() => { - form.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - a.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c2.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c3.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - - a.reset([], {emitEvent: false}); - tick(); - })); - - it('should emit one statusChange event per reset control', () => { - form.statusChanges.subscribe(() => logger.push('form')); - a.statusChanges.subscribe(() => logger.push('array')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); - c3.statusChanges.subscribe(() => logger.push('control3')); - - a.reset(); - expect(logger).toEqual(['control1', 'control2', 'array', 'form']); - }); - - it('should mark as pristine and not dirty before emitting valueChange and statusChange events when resetting', - () => { - const pristineAndNotDirty = () => { - expect(a.pristine).toBe(true); - expect(a.dirty).toBe(false); - }; - - c2.markAsDirty(); - expect(a.pristine).toBe(false); - expect(a.dirty).toBe(true); - - a.valueChanges.subscribe(pristineAndNotDirty); - a.statusChanges.subscribe(pristineAndNotDirty); - - a.reset(); - }); - }); + c.setValue('incorrect'); + + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({'broken': true}); }); + }); - describe('errors', () => { - it('should run the validator when the value changes', () => { - const simpleValidator = (c: FormArray) => - c.controls[0].value != 'correct' ? {'broken': true} : null; - const c = new FormControl(null); - const g = new FormArray([c], simpleValidator as ValidatorFn); + describe('dirty', () => { + let c: FormControl; + let a: FormArray; - c.setValue('correct'); + beforeEach(() => { + c = new FormControl('value'); + a = new FormArray([c]); + }); - expect(g.valid).toEqual(true); - expect(g.errors).toEqual(null); + it('should be false after creating a control', () => { + expect(a.dirty).toEqual(false); + }); - c.setValue('incorrect'); + it('should be true after changing the value of the control', () => { + c.markAsDirty(); - expect(g.valid).toEqual(false); - expect(g.errors).toEqual({'broken': true}); - }); + expect(a.dirty).toEqual(true); }); + }); + describe('touched', () => { + let c: FormControl; + let a: FormArray; - describe('dirty', () => { - let c: FormControl; - let a: FormArray; - - beforeEach(() => { - c = new FormControl('value'); - a = new FormArray([c]); - }); + beforeEach(() => { + c = new FormControl('value'); + a = new FormArray([c]); + }); - it('should be false after creating a control', () => { expect(a.dirty).toEqual(false); }); + it('should be false after creating a control', () => { + expect(a.touched).toEqual(false); + }); - it('should be true after changing the value of the control', () => { - c.markAsDirty(); + it('should be true after child control is marked as touched', () => { + c.markAsTouched(); - expect(a.dirty).toEqual(true); - }); + expect(a.touched).toEqual(true); }); + }); - describe('touched', () => { - let c: FormControl; - let a: FormArray; - beforeEach(() => { - c = new FormControl('value'); - a = new FormArray([c]); - }); + describe('pending', () => { + let c: FormControl; + let a: FormArray; - it('should be false after creating a control', () => { expect(a.touched).toEqual(false); }); + beforeEach(() => { + c = new FormControl('value'); + a = new FormArray([c]); + }); - it('should be true after child control is marked as touched', () => { - c.markAsTouched(); + it('should be false after creating a control', () => { + expect(c.pending).toEqual(false); + expect(a.pending).toEqual(false); + }); - expect(a.touched).toEqual(true); - }); + it('should be true after changing the value of the control', () => { + c.markAsPending(); + + expect(c.pending).toEqual(true); + expect(a.pending).toEqual(true); }); + it('should not update the parent when onlySelf = true', () => { + c.markAsPending({onlySelf: true}); - describe('pending', () => { - let c: FormControl; - let a: FormArray; + expect(c.pending).toEqual(true); + expect(a.pending).toEqual(false); + }); - beforeEach(() => { - c = new FormControl('value'); - a = new FormArray([c]); - }); + describe('status change events', () => { + let logger: string[]; - it('should be false after creating a control', () => { - expect(c.pending).toEqual(false); - expect(a.pending).toEqual(false); + beforeEach(() => { + logger = []; + a.statusChanges.subscribe((status) => logger.push(status)); }); - it('should be true after changing the value of the control', () => { + it('should emit event after marking control as pending', () => { c.markAsPending(); - - expect(c.pending).toEqual(true); - expect(a.pending).toEqual(true); + expect(logger).toEqual(['PENDING']); }); - it('should not update the parent when onlySelf = true', () => { + it('should not emit event from parent when onlySelf is true', () => { c.markAsPending({onlySelf: true}); - - expect(c.pending).toEqual(true); - expect(a.pending).toEqual(false); + expect(logger).toEqual([]); }); - describe('status change events', () => { - let logger: string[]; - - beforeEach(() => { - logger = []; - a.statusChanges.subscribe((status) => logger.push(status)); - }); - - it('should emit event after marking control as pending', () => { - c.markAsPending(); - expect(logger).toEqual(['PENDING']); - }); - - it('should not emit event from parent when onlySelf is true', () => { - c.markAsPending({onlySelf: true}); - expect(logger).toEqual([]); - }); - - it('should not emit event when emitEvent = false', () => { - c.markAsPending({emitEvent: false}); - expect(logger).toEqual([]); - }); - - it('should emit event when parent is markedAsPending', () => { - a.markAsPending(); - expect(logger).toEqual(['PENDING']); - }); + it('should not emit event when emitEvent = false', () => { + c.markAsPending({emitEvent: false}); + expect(logger).toEqual([]); }); + it('should emit event when parent is markedAsPending', () => { + a.markAsPending(); + expect(logger).toEqual(['PENDING']); + }); }); + }); - describe('valueChanges', () => { - let a: FormArray; - let c1: any /** TODO #9100 */, c2: any /** TODO #9100 */; + describe('valueChanges', () => { + let a: FormArray; + let c1: any /** TODO #9100 */, c2: any /** TODO #9100 */; - beforeEach(() => { - c1 = new FormControl('old1'); - c2 = new FormControl('old2'); - a = new FormArray([c1, c2]); - }); + beforeEach(() => { + c1 = new FormControl('old1'); + c2 = new FormControl('old2'); + a = new FormArray([c1, c2]); + }); - it('should fire an event after the value has been updated', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - a.valueChanges.subscribe({ - next: (value: any) => { - expect(a.value).toEqual(['new1', 'old2']); - expect(value).toEqual(['new1', 'old2']); - async.done(); - } - }); - c1.setValue('new1'); - })); + it('should fire an event after the value has been updated', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + a.valueChanges.subscribe({ + next: (value: any) => { + expect(a.value).toEqual(['new1', 'old2']); + expect(value).toEqual(['new1', 'old2']); + async.done(); + } + }); + c1.setValue('new1'); + })); + + it('should fire an event after the control\'s observable fired an event', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + let controlCallbackIsCalled = false; + + + c1.valueChanges.subscribe({ + next: (value: any) => { + controlCallbackIsCalled = true; + } + }); + + a.valueChanges.subscribe({ + next: (value: any) => { + expect(controlCallbackIsCalled).toBe(true); + async.done(); + } + }); + + c1.setValue('new1'); + })); + + it('should fire an event when a control is removed', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + a.valueChanges.subscribe({ + next: (value: any) => { + expect(value).toEqual(['old1']); + async.done(); + } + }); + + a.removeAt(1); + })); + + it('should fire an event when a control is added', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + a.removeAt(1); + + a.valueChanges.subscribe({ + next: (value: any) => { + expect(value).toEqual(['old1', 'old2']); + async.done(); + } + }); + + a.push(c2); + })); + }); - it('should fire an event after the control\'s observable fired an event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - let controlCallbackIsCalled = false; + describe('get', () => { + it('should return null when path is null', () => { + const g = new FormGroup({}); + expect(g.get(null!)).toEqual(null); + }); + it('should return null when path is empty', () => { + const g = new FormGroup({}); + expect(g.get([])).toEqual(null); + }); - c1.valueChanges.subscribe({next: (value: any) => { controlCallbackIsCalled = true; }}); + it('should return null when path is invalid', () => { + const g = new FormGroup({}); + expect(g.get('invalid')).toEqual(null); + }); - a.valueChanges.subscribe({ - next: (value: any) => { - expect(controlCallbackIsCalled).toBe(true); - async.done(); - } - }); + it('should return a child of a control group', () => { + const g = new FormGroup({ + 'one': new FormControl('111'), + 'nested': new FormGroup({'two': new FormControl('222')}) + }); - c1.setValue('new1'); - })); + expect(g.get(['one'])!.value).toEqual('111'); + expect(g.get('one')!.value).toEqual('111'); + expect(g.get(['nested', 'two'])!.value).toEqual('222'); + expect(g.get('nested.two')!.value).toEqual('222'); + }); - it('should fire an event when a control is removed', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - a.valueChanges.subscribe({ - next: (value: any) => { - expect(value).toEqual(['old1']); - async.done(); - } - }); + it('should return an element of an array', () => { + const g = new FormGroup({'array': new FormArray([new FormControl('111')])}); - a.removeAt(1); - })); + expect(g.get(['array', 0])!.value).toEqual('111'); + }); + }); - it('should fire an event when a control is added', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - a.removeAt(1); + describe('validator', () => { + function simpleValidator(c: AbstractControl): ValidationErrors|null { + return c.get([0])!.value === 'correct' ? null : {'broken': true}; + } - a.valueChanges.subscribe({ - next: (value: any) => { - expect(value).toEqual(['old1', 'old2']); - async.done(); - } - }); + function arrayRequiredValidator(c: AbstractControl): ValidationErrors|null { + return Validators.required(c.get([0]) as AbstractControl); + } - a.push(c2); - })); + it('should set a single validator', () => { + const a = new FormArray([new FormControl()], simpleValidator); + expect(a.valid).toBe(false); + expect(a.errors).toEqual({'broken': true}); + + a.setValue(['correct']); + expect(a.valid).toBe(true); }); - describe('get', () => { - it('should return null when path is null', () => { - const g = new FormGroup({}); - expect(g.get(null !)).toEqual(null); - }); + it('should set a single validator from options obj', () => { + const a = new FormArray([new FormControl()], {validators: simpleValidator}); + expect(a.valid).toBe(false); + expect(a.errors).toEqual({'broken': true}); - it('should return null when path is empty', () => { - const g = new FormGroup({}); - expect(g.get([])).toEqual(null); - }); + a.setValue(['correct']); + expect(a.valid).toBe(true); + }); - it('should return null when path is invalid', () => { - const g = new FormGroup({}); - expect(g.get('invalid')).toEqual(null); - }); + it('should set multiple validators from an array', () => { + const a = new FormArray([new FormControl()], [simpleValidator, arrayRequiredValidator]); + expect(a.valid).toBe(false); + expect(a.errors).toEqual({'required': true, 'broken': true}); - it('should return a child of a control group', () => { - const g = new FormGroup({ - 'one': new FormControl('111'), - 'nested': new FormGroup({'two': new FormControl('222')}) - }); + a.setValue(['c']); + expect(a.valid).toBe(false); + expect(a.errors).toEqual({'broken': true}); - expect(g.get(['one']) !.value).toEqual('111'); - expect(g.get('one') !.value).toEqual('111'); - expect(g.get(['nested', 'two']) !.value).toEqual('222'); - expect(g.get('nested.two') !.value).toEqual('222'); - }); + a.setValue(['correct']); + expect(a.valid).toBe(true); + }); - it('should return an element of an array', () => { - const g = new FormGroup({'array': new FormArray([new FormControl('111')])}); + it('should set multiple validators from options obj', () => { + const a = new FormArray( + [new FormControl()], {validators: [simpleValidator, arrayRequiredValidator]}); + expect(a.valid).toBe(false); + expect(a.errors).toEqual({'required': true, 'broken': true}); - expect(g.get(['array', 0]) !.value).toEqual('111'); - }); + a.setValue(['c']); + expect(a.valid).toBe(false); + expect(a.errors).toEqual({'broken': true}); + + a.setValue(['correct']); + expect(a.valid).toBe(true); }); + }); - describe('validator', () => { - function simpleValidator(c: AbstractControl): ValidationErrors|null { - return c.get([0]) !.value === 'correct' ? null : {'broken': true}; - } + describe('asyncValidator', () => { + function otherObservableValidator() { + return of({'other': true}); + } - function arrayRequiredValidator(c: AbstractControl): ValidationErrors|null { - return Validators.required(c.get([0]) as AbstractControl); - } + it('should run the async validator', fakeAsync(() => { + const c = new FormControl('value'); + const g = new FormArray([c], null!, asyncValidator('expected')); - it('should set a single validator', () => { - const a = new FormArray([new FormControl()], simpleValidator); - expect(a.valid).toBe(false); - expect(a.errors).toEqual({'broken': true}); + expect(g.pending).toEqual(true); - a.setValue(['correct']); - expect(a.valid).toBe(true); - }); + tick(); - it('should set a single validator from options obj', () => { - const a = new FormArray([new FormControl()], {validators: simpleValidator}); - expect(a.valid).toBe(false); - expect(a.errors).toEqual({'broken': true}); + expect(g.errors).toEqual({'async': true}); + expect(g.pending).toEqual(false); + })); - a.setValue(['correct']); - expect(a.valid).toBe(true); - }); + it('should set a single async validator from options obj', fakeAsync(() => { + const g = new FormArray( + [new FormControl('value')], {asyncValidators: asyncValidator('expected')}); - it('should set multiple validators from an array', () => { - const a = new FormArray([new FormControl()], [simpleValidator, arrayRequiredValidator]); - expect(a.valid).toBe(false); - expect(a.errors).toEqual({'required': true, 'broken': true}); + expect(g.pending).toEqual(true); - a.setValue(['c']); - expect(a.valid).toBe(false); - expect(a.errors).toEqual({'broken': true}); + tick(); - a.setValue(['correct']); - expect(a.valid).toBe(true); - }); + expect(g.errors).toEqual({'async': true}); + expect(g.pending).toEqual(false); + })); - it('should set multiple validators from options obj', () => { - const a = new FormArray( - [new FormControl()], {validators: [simpleValidator, arrayRequiredValidator]}); - expect(a.valid).toBe(false); - expect(a.errors).toEqual({'required': true, 'broken': true}); + it('should set multiple async validators from an array', fakeAsync(() => { + const g = new FormArray( + [new FormControl('value')], null!, + [asyncValidator('expected'), otherObservableValidator]); - a.setValue(['c']); - expect(a.valid).toBe(false); - expect(a.errors).toEqual({'broken': true}); + expect(g.pending).toEqual(true); - a.setValue(['correct']); - expect(a.valid).toBe(true); - }); - }); + tick(); - describe('asyncValidator', () => { - function otherObservableValidator() { return of ({'other': true}); } + expect(g.errors).toEqual({'async': true, 'other': true}); + expect(g.pending).toEqual(false); + })); - it('should run the async validator', fakeAsync(() => { - const c = new FormControl('value'); - const g = new FormArray([c], null !, asyncValidator('expected')); + it('should set multiple async validators from options obj', fakeAsync(() => { + const g = new FormArray( + [new FormControl('value')], + {asyncValidators: [asyncValidator('expected'), otherObservableValidator]}); - expect(g.pending).toEqual(true); + expect(g.pending).toEqual(true); - tick(); - - expect(g.errors).toEqual({'async': true}); - expect(g.pending).toEqual(false); - })); + tick(); - it('should set a single async validator from options obj', fakeAsync(() => { - const g = new FormArray( - [new FormControl('value')], {asyncValidators: asyncValidator('expected')}); + expect(g.errors).toEqual({'async': true, 'other': true}); + expect(g.pending).toEqual(false); + })); + }); - expect(g.pending).toEqual(true); + describe('disable() & enable()', () => { + let a: FormArray; + let c: FormControl; + let c2: FormControl; - tick(); + beforeEach(() => { + c = new FormControl(null); + c2 = new FormControl(null); + a = new FormArray([c, c2]); + }); - expect(g.errors).toEqual({'async': true}); - expect(g.pending).toEqual(false); - })); + it('should mark the array as disabled', () => { + expect(a.disabled).toBe(false); + expect(a.valid).toBe(true); - it('should set multiple async validators from an array', fakeAsync(() => { - const g = new FormArray( - [new FormControl('value')], null !, - [asyncValidator('expected'), otherObservableValidator]); + a.disable(); + expect(a.disabled).toBe(true); + expect(a.valid).toBe(false); - expect(g.pending).toEqual(true); + a.enable(); + expect(a.disabled).toBe(false); + expect(a.valid).toBe(true); + }); - tick(); + it('should set the array status as disabled', () => { + expect(a.status).toBe('VALID'); - expect(g.errors).toEqual({'async': true, 'other': true}); - expect(g.pending).toEqual(false); - })); + a.disable(); + expect(a.status).toBe('DISABLED'); - it('should set multiple async validators from options obj', fakeAsync(() => { - const g = new FormArray( - [new FormControl('value')], - {asyncValidators: [asyncValidator('expected'), otherObservableValidator]}); + a.enable(); + expect(a.status).toBe('VALID'); + }); - expect(g.pending).toEqual(true); + it('should mark children of the array as disabled', () => { + expect(c.disabled).toBe(false); + expect(c2.disabled).toBe(false); - tick(); + a.disable(); + expect(c.disabled).toBe(true); + expect(c2.disabled).toBe(true); - expect(g.errors).toEqual({'async': true, 'other': true}); - expect(g.pending).toEqual(false); - })); + a.enable(); + expect(c.disabled).toBe(false); + expect(c2.disabled).toBe(false); }); - describe('disable() & enable()', () => { - let a: FormArray; - let c: FormControl; - let c2: FormControl; - - beforeEach(() => { - c = new FormControl(null); - c2 = new FormControl(null); - a = new FormArray([c, c2]); + it('should ignore disabled controls in validation', () => { + const g = new FormGroup({ + nested: new FormArray([new FormControl(null, Validators.required)]), + two: new FormControl('two') }); + expect(g.valid).toBe(false); - it('should mark the array as disabled', () => { - expect(a.disabled).toBe(false); - expect(a.valid).toBe(true); + g.get('nested')!.disable(); + expect(g.valid).toBe(true); - a.disable(); - expect(a.disabled).toBe(true); - expect(a.valid).toBe(false); + g.get('nested')!.enable(); + expect(g.valid).toBe(false); + }); - a.enable(); - expect(a.disabled).toBe(false); - expect(a.valid).toBe(true); - }); + it('should ignore disabled controls when serializing value', () => { + const g = new FormGroup( + {nested: new FormArray([new FormControl('one')]), two: new FormControl('two')}); + expect(g.value).toEqual({'nested': ['one'], 'two': 'two'}); - it('should set the array status as disabled', () => { - expect(a.status).toBe('VALID'); + g.get('nested')!.disable(); + expect(g.value).toEqual({'two': 'two'}); - a.disable(); - expect(a.status).toBe('DISABLED'); + g.get('nested')!.enable(); + expect(g.value).toEqual({'nested': ['one'], 'two': 'two'}); + }); - a.enable(); - expect(a.status).toBe('VALID'); - }); + it('should ignore disabled controls when determining dirtiness', () => { + const g = new FormGroup({nested: a, two: new FormControl('two')}); + g.get(['nested', 0])!.markAsDirty(); + expect(g.dirty).toBe(true); - it('should mark children of the array as disabled', () => { - expect(c.disabled).toBe(false); - expect(c2.disabled).toBe(false); + g.get('nested')!.disable(); + expect(g.get('nested')!.dirty).toBe(true); + expect(g.dirty).toEqual(false); - a.disable(); - expect(c.disabled).toBe(true); - expect(c2.disabled).toBe(true); + g.get('nested')!.enable(); + expect(g.dirty).toEqual(true); + }); - a.enable(); - expect(c.disabled).toBe(false); - expect(c2.disabled).toBe(false); - }); + it('should ignore disabled controls when determining touched state', () => { + const g = new FormGroup({nested: a, two: new FormControl('two')}); + g.get(['nested', 0])!.markAsTouched(); + expect(g.touched).toBe(true); - it('should ignore disabled controls in validation', () => { - const g = new FormGroup({ - nested: new FormArray([new FormControl(null, Validators.required)]), - two: new FormControl('two') - }); - expect(g.valid).toBe(false); + g.get('nested')!.disable(); + expect(g.get('nested')!.touched).toBe(true); + expect(g.touched).toEqual(false); - g.get('nested') !.disable(); - expect(g.valid).toBe(true); + g.get('nested')!.enable(); + expect(g.touched).toEqual(true); + }); - g.get('nested') !.enable(); - expect(g.valid).toBe(false); - }); + it('should keep empty, disabled arrays disabled when updating validity', () => { + const arr = new FormArray([]); + expect(arr.status).toEqual('VALID'); - it('should ignore disabled controls when serializing value', () => { - const g = new FormGroup( - {nested: new FormArray([new FormControl('one')]), two: new FormControl('two')}); - expect(g.value).toEqual({'nested': ['one'], 'two': 'two'}); + arr.disable(); + expect(arr.status).toEqual('DISABLED'); - g.get('nested') !.disable(); - expect(g.value).toEqual({'two': 'two'}); + arr.updateValueAndValidity(); + expect(arr.status).toEqual('DISABLED'); - g.get('nested') !.enable(); - expect(g.value).toEqual({'nested': ['one'], 'two': 'two'}); - }); + arr.push(new FormControl({value: '', disabled: true})); + expect(arr.status).toEqual('DISABLED'); - it('should ignore disabled controls when determining dirtiness', () => { - const g = new FormGroup({nested: a, two: new FormControl('two')}); - g.get(['nested', 0]) !.markAsDirty(); - expect(g.dirty).toBe(true); + arr.push(new FormControl()); + expect(arr.status).toEqual('VALID'); + }); - g.get('nested') !.disable(); - expect(g.get('nested') !.dirty).toBe(true); - expect(g.dirty).toEqual(false); + it('should re-enable empty, disabled arrays', () => { + const arr = new FormArray([]); + arr.disable(); + expect(arr.status).toEqual('DISABLED'); - g.get('nested') !.enable(); - expect(g.dirty).toEqual(true); - }); + arr.enable(); + expect(arr.status).toEqual('VALID'); + }); - it('should ignore disabled controls when determining touched state', () => { - const g = new FormGroup({nested: a, two: new FormControl('two')}); - g.get(['nested', 0]) !.markAsTouched(); - expect(g.touched).toBe(true); + it('should not run validators on disabled controls', () => { + const validator = jasmine.createSpy('validator'); + const arr = new FormArray([new FormControl()], validator); + expect(validator.calls.count()).toEqual(1); - g.get('nested') !.disable(); - expect(g.get('nested') !.touched).toBe(true); - expect(g.touched).toEqual(false); + arr.disable(); + expect(validator.calls.count()).toEqual(1); - g.get('nested') !.enable(); - expect(g.touched).toEqual(true); - }); + arr.setValue(['value']); + expect(validator.calls.count()).toEqual(1); - it('should keep empty, disabled arrays disabled when updating validity', () => { - const arr = new FormArray([]); - expect(arr.status).toEqual('VALID'); + arr.enable(); + expect(validator.calls.count()).toEqual(2); + }); + + describe('disabled errors', () => { + it('should clear out array errors when disabled', () => { + const arr = new FormArray([new FormControl()], () => ({'expected': true})); + expect(arr.errors).toEqual({'expected': true}); arr.disable(); - expect(arr.status).toEqual('DISABLED'); + expect(arr.errors).toEqual(null); - arr.updateValueAndValidity(); - expect(arr.status).toEqual('DISABLED'); + arr.enable(); + expect(arr.errors).toEqual({'expected': true}); + }); - arr.push(new FormControl({value: '', disabled: true})); - expect(arr.status).toEqual('DISABLED'); + it('should re-populate array errors when enabled from a child', () => { + const arr = new FormArray([new FormControl()], () => ({'expected': true})); + arr.disable(); + expect(arr.errors).toEqual(null); arr.push(new FormControl()); - expect(arr.status).toEqual('VALID'); + expect(arr.errors).toEqual({'expected': true}); }); - it('should re-enable empty, disabled arrays', () => { - const arr = new FormArray([]); - arr.disable(); - expect(arr.status).toEqual('DISABLED'); + it('should clear out async array errors when disabled', fakeAsync(() => { + const arr = new FormArray([new FormControl()], null!, asyncValidator('expected')); + tick(); + expect(arr.errors).toEqual({'async': true}); - arr.enable(); - expect(arr.status).toEqual('VALID'); - }); + arr.disable(); + expect(arr.errors).toEqual(null); + + arr.enable(); + tick(); + expect(arr.errors).toEqual({'async': true}); + })); - it('should not run validators on disabled controls', () => { - const validator = jasmine.createSpy('validator'); - const arr = new FormArray([new FormControl()], validator); - expect(validator.calls.count()).toEqual(1); + it('should re-populate async array errors when enabled from a child', fakeAsync(() => { + const arr = new FormArray([new FormControl()], null!, asyncValidator('expected')); + tick(); + expect(arr.errors).toEqual({'async': true}); - arr.disable(); - expect(validator.calls.count()).toEqual(1); + arr.disable(); + expect(arr.errors).toEqual(null); - arr.setValue(['value']); - expect(validator.calls.count()).toEqual(1); + arr.push(new FormControl()); + tick(); + expect(arr.errors).toEqual({'async': true}); + })); + }); - arr.enable(); - expect(validator.calls.count()).toEqual(2); - }); + describe('disabled events', () => { + let logger: string[]; + let c: FormControl; + let a: FormArray; + let form: FormGroup; - describe('disabled errors', () => { - it('should clear out array errors when disabled', () => { - const arr = new FormArray([new FormControl()], () => ({'expected': true})); - expect(arr.errors).toEqual({'expected': true}); + beforeEach(() => { + logger = []; + c = new FormControl('', Validators.required); + a = new FormArray([c]); + form = new FormGroup({a: a}); + }); - arr.disable(); - expect(arr.errors).toEqual(null); + it('should emit value change events in the right order', () => { + c.valueChanges.subscribe(() => logger.push('control')); + a.valueChanges.subscribe(() => logger.push('array')); + form.valueChanges.subscribe(() => logger.push('form')); - arr.enable(); - expect(arr.errors).toEqual({'expected': true}); - }); + a.disable(); + expect(logger).toEqual(['control', 'array', 'form']); + }); - it('should re-populate array errors when enabled from a child', () => { - const arr = new FormArray([new FormControl()], () => ({'expected': true})); - arr.disable(); - expect(arr.errors).toEqual(null); + it('should emit status change events in the right order', () => { + c.statusChanges.subscribe(() => logger.push('control')); + a.statusChanges.subscribe(() => logger.push('array')); + form.statusChanges.subscribe(() => logger.push('form')); - arr.push(new FormControl()); - expect(arr.errors).toEqual({'expected': true}); - }); + a.disable(); + expect(logger).toEqual(['control', 'array', 'form']); + }); - it('should clear out async array errors when disabled', fakeAsync(() => { - const arr = new FormArray([new FormControl()], null !, asyncValidator('expected')); - tick(); - expect(arr.errors).toEqual({'async': true}); + it('should not emit value change events when emitEvent = false', () => { + c.valueChanges.subscribe(() => logger.push('control')); + a.valueChanges.subscribe(() => logger.push('array')); + form.valueChanges.subscribe(() => logger.push('form')); - arr.disable(); - expect(arr.errors).toEqual(null); + a.disable({emitEvent: false}); + expect(logger).toEqual([]); + a.enable({emitEvent: false}); + expect(logger).toEqual([]); + }); - arr.enable(); - tick(); - expect(arr.errors).toEqual({'async': true}); - })); + it('should not emit status change events when emitEvent = false', () => { + c.statusChanges.subscribe(() => logger.push('control')); + a.statusChanges.subscribe(() => logger.push('array')); + form.statusChanges.subscribe(() => logger.push('form')); - it('should re-populate async array errors when enabled from a child', fakeAsync(() => { - const arr = new FormArray([new FormControl()], null !, asyncValidator('expected')); - tick(); - expect(arr.errors).toEqual({'async': true}); + a.disable({emitEvent: false}); + expect(logger).toEqual([]); + a.enable({emitEvent: false}); + expect(logger).toEqual([]); + }); + }); - arr.disable(); - expect(arr.errors).toEqual(null); + describe('setControl()', () => { + let c: FormControl; + let a: FormArray; - arr.push(new FormControl()); - tick(); - expect(arr.errors).toEqual({'async': true}); - })); + beforeEach(() => { + c = new FormControl('one'); + a = new FormArray([c]); }); - describe('disabled events', () => { - let logger: string[]; - let c: FormControl; - let a: FormArray; - let form: FormGroup; - - beforeEach(() => { - logger = []; - c = new FormControl('', Validators.required); - a = new FormArray([c]); - form = new FormGroup({a: a}); - }); - - it('should emit value change events in the right order', () => { - c.valueChanges.subscribe(() => logger.push('control')); - a.valueChanges.subscribe(() => logger.push('array')); - form.valueChanges.subscribe(() => logger.push('form')); - - a.disable(); - expect(logger).toEqual(['control', 'array', 'form']); - }); - - it('should emit status change events in the right order', () => { - c.statusChanges.subscribe(() => logger.push('control')); - a.statusChanges.subscribe(() => logger.push('array')); - form.statusChanges.subscribe(() => logger.push('form')); - - a.disable(); - expect(logger).toEqual(['control', 'array', 'form']); - }); - - it('should not emit value change events when emitEvent = false', () => { - c.valueChanges.subscribe(() => logger.push('control')); - a.valueChanges.subscribe(() => logger.push('array')); - form.valueChanges.subscribe(() => logger.push('form')); - - a.disable({emitEvent: false}); - expect(logger).toEqual([]); - a.enable({emitEvent: false}); - expect(logger).toEqual([]); - }); - - it('should not emit status change events when emitEvent = false', () => { - c.statusChanges.subscribe(() => logger.push('control')); - a.statusChanges.subscribe(() => logger.push('array')); - form.statusChanges.subscribe(() => logger.push('form')); - - a.disable({emitEvent: false}); - expect(logger).toEqual([]); - a.enable({emitEvent: false}); - expect(logger).toEqual([]); - }); + it('should replace existing control with new control', () => { + const c2 = new FormControl('new!', Validators.minLength(10)); + a.setControl(0, c2); + expect(a.controls[0]).toEqual(c2); + expect(a.value).toEqual(['new!']); + expect(a.valid).toBe(false); }); - describe('setControl()', () => { - let c: FormControl; - let a: FormArray; - - beforeEach(() => { - c = new FormControl('one'); - a = new FormArray([c]); - }); - - it('should replace existing control with new control', () => { - const c2 = new FormControl('new!', Validators.minLength(10)); - a.setControl(0, c2); - - expect(a.controls[0]).toEqual(c2); - expect(a.value).toEqual(['new!']); - expect(a.valid).toBe(false); - }); - - it('should add control if control did not exist before', () => { - const c2 = new FormControl('new!', Validators.minLength(10)); - a.setControl(1, c2); - - expect(a.controls[1]).toEqual(c2); - expect(a.value).toEqual(['one', 'new!']); - expect(a.valid).toBe(false); - }); - - it('should remove control if new control is null', () => { - a.setControl(0, null !); - expect(a.controls[0]).not.toBeDefined(); - expect(a.value).toEqual([]); - }); - - it('should only emit value change event once', () => { - const logger: string[] = []; - const c2 = new FormControl('new!'); - a.valueChanges.subscribe(() => logger.push('change!')); - a.setControl(0, c2); - expect(logger).toEqual(['change!']); - }); + it('should add control if control did not exist before', () => { + const c2 = new FormControl('new!', Validators.minLength(10)); + a.setControl(1, c2); + expect(a.controls[1]).toEqual(c2); + expect(a.value).toEqual(['one', 'new!']); + expect(a.valid).toBe(false); }); + it('should remove control if new control is null', () => { + a.setControl(0, null!); + expect(a.controls[0]).not.toBeDefined(); + expect(a.value).toEqual([]); + }); + + it('should only emit value change event once', () => { + const logger: string[] = []; + const c2 = new FormControl('new!'); + a.valueChanges.subscribe(() => logger.push('change!')); + a.setControl(0, c2); + expect(logger).toEqual(['change!']); + }); }); }); +}); })(); diff --git a/packages/forms/test/form_builder_spec.ts b/packages/forms/test/form_builder_spec.ts index e04cd74677b15..05e2a735ce62b 100644 --- a/packages/forms/test/form_builder_spec.ts +++ b/packages/forms/test/form_builder_spec.ts @@ -8,195 +8,208 @@ import {fakeAsync, tick} from '@angular/core/testing'; import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {FormBuilder, Validators} from '@angular/forms'; -import {of } from 'rxjs'; +import {of} from 'rxjs'; (function() { - function syncValidator(_: any /** TODO #9100 */): any /** TODO #9100 */ { return null; } - function asyncValidator(_: any /** TODO #9100 */) { return Promise.resolve(null); } - - describe('Form Builder', () => { - let b: FormBuilder; - - beforeEach(() => { b = new FormBuilder(); }); - - it('should create controls from a value', () => { - const g = b.group({'login': 'some value'}); - - expect(g.controls['login'].value).toEqual('some value'); - }); +function syncValidator(_: any /** TODO #9100 */): any /** TODO #9100 */ { + return null; +} +function asyncValidator(_: any /** TODO #9100 */) { + return Promise.resolve(null); +} + +describe('Form Builder', () => { + let b: FormBuilder; + + beforeEach(() => { + b = new FormBuilder(); + }); - it('should create controls from a boxed value', () => { - const g = b.group({'login': {value: 'some value', disabled: true}}); + it('should create controls from a value', () => { + const g = b.group({'login': 'some value'}); - expect(g.controls['login'].value).toEqual('some value'); - expect(g.controls['login'].disabled).toEqual(true); - }); + expect(g.controls['login'].value).toEqual('some value'); + }); - it('should create controls from an array', () => { - const g = b.group( - {'login': ['some value'], 'password': ['some value', syncValidator, asyncValidator]}); + it('should create controls from a boxed value', () => { + const g = b.group({'login': {value: 'some value', disabled: true}}); - expect(g.controls['login'].value).toEqual('some value'); - expect(g.controls['password'].value).toEqual('some value'); - expect(g.controls['password'].validator).toEqual(syncValidator); - expect(g.controls['password'].asyncValidator).toEqual(asyncValidator); - }); + expect(g.controls['login'].value).toEqual('some value'); + expect(g.controls['login'].disabled).toEqual(true); + }); - it('should use controls whose form state is a primitive value', () => { - const g = b.group({'login': b.control('some value', syncValidator, asyncValidator)}); + it('should create controls from an array', () => { + const g = b.group( + {'login': ['some value'], 'password': ['some value', syncValidator, asyncValidator]}); - expect(g.controls['login'].value).toEqual('some value'); - expect(g.controls['login'].validator).toBe(syncValidator); - expect(g.controls['login'].asyncValidator).toBe(asyncValidator); - }); + expect(g.controls['login'].value).toEqual('some value'); + expect(g.controls['password'].value).toEqual('some value'); + expect(g.controls['password'].validator).toEqual(syncValidator); + expect(g.controls['password'].asyncValidator).toEqual(asyncValidator); + }); - it('should support controls with no validators and whose form state is null', () => { - const g = b.group({'login': b.control(null)}); - expect(g.controls['login'].value).toBeNull(); - expect(g.controls['login'].validator).toBeNull(); - expect(g.controls['login'].asyncValidator).toBeNull(); - }); + it('should use controls whose form state is a primitive value', () => { + const g = b.group({'login': b.control('some value', syncValidator, asyncValidator)}); - it('should support controls with validators and whose form state is null', () => { - const g = b.group({'login': b.control(null, syncValidator, asyncValidator)}); - expect(g.controls['login'].value).toBeNull(); - expect(g.controls['login'].validator).toBe(syncValidator); - expect(g.controls['login'].asyncValidator).toBe(asyncValidator); - }); + expect(g.controls['login'].value).toEqual('some value'); + expect(g.controls['login'].validator).toBe(syncValidator); + expect(g.controls['login'].asyncValidator).toBe(asyncValidator); + }); - it('should support controls with no validators and whose form state is undefined', () => { - const g = b.group({'login': b.control(undefined)}); - expect(g.controls['login'].value).toBeNull(); - expect(g.controls['login'].validator).toBeNull(); - expect(g.controls['login'].asyncValidator).toBeNull(); - }); + it('should support controls with no validators and whose form state is null', () => { + const g = b.group({'login': b.control(null)}); + expect(g.controls['login'].value).toBeNull(); + expect(g.controls['login'].validator).toBeNull(); + expect(g.controls['login'].asyncValidator).toBeNull(); + }); - it('should support controls with validators and whose form state is undefined', () => { - const g = b.group({'login': b.control(undefined, syncValidator, asyncValidator)}); - expect(g.controls['login'].value).toBeNull(); - expect(g.controls['login'].validator).toBe(syncValidator); - expect(g.controls['login'].asyncValidator).toBe(asyncValidator); - }); + it('should support controls with validators and whose form state is null', () => { + const g = b.group({'login': b.control(null, syncValidator, asyncValidator)}); + expect(g.controls['login'].value).toBeNull(); + expect(g.controls['login'].validator).toBe(syncValidator); + expect(g.controls['login'].asyncValidator).toBe(asyncValidator); + }); - it('should create groups with a custom validator', () => { - const g = b.group( - {'login': 'some value'}, {'validator': syncValidator, 'asyncValidator': asyncValidator}); + it('should support controls with no validators and whose form state is undefined', () => { + const g = b.group({'login': b.control(undefined)}); + expect(g.controls['login'].value).toBeNull(); + expect(g.controls['login'].validator).toBeNull(); + expect(g.controls['login'].asyncValidator).toBeNull(); + }); - expect(g.validator).toBe(syncValidator); - expect(g.asyncValidator).toBe(asyncValidator); - }); + it('should support controls with validators and whose form state is undefined', () => { + const g = b.group({'login': b.control(undefined, syncValidator, asyncValidator)}); + expect(g.controls['login'].value).toBeNull(); + expect(g.controls['login'].validator).toBe(syncValidator); + expect(g.controls['login'].asyncValidator).toBe(asyncValidator); + }); - it('should create control arrays', () => { - const c = b.control('three'); - const e = b.control(null); - const f = b.control(undefined); - const a = b.array( - ['one', ['two', syncValidator], c, b.array(['four']), e, f], syncValidator, - asyncValidator); - - expect(a.value).toEqual(['one', 'two', 'three', ['four'], null, null]); - expect(a.validator).toBe(syncValidator); - expect(a.asyncValidator).toBe(asyncValidator); - }); + it('should create groups with a custom validator', () => { + const g = b.group( + {'login': 'some value'}, {'validator': syncValidator, 'asyncValidator': asyncValidator}); - it('should create control arrays with multiple async validators', fakeAsync(() => { - function asyncValidator1() { return of ({'async1': true}); } - function asyncValidator2() { return of ({'async2': true}); } + expect(g.validator).toBe(syncValidator); + expect(g.asyncValidator).toBe(asyncValidator); + }); - const a = b.array(['one', 'two'], null, [asyncValidator1, asyncValidator2]); - expect(a.value).toEqual(['one', 'two']); + it('should create control arrays', () => { + const c = b.control('three'); + const e = b.control(null); + const f = b.control(undefined); + const a = b.array( + ['one', ['two', syncValidator], c, b.array(['four']), e, f], syncValidator, asyncValidator); - tick(); + expect(a.value).toEqual(['one', 'two', 'three', ['four'], null, null]); + expect(a.validator).toBe(syncValidator); + expect(a.asyncValidator).toBe(asyncValidator); + }); - expect(a.errors).toEqual({'async1': true, 'async2': true}); - })); + it('should create control arrays with multiple async validators', fakeAsync(() => { + function asyncValidator1() { + return of({'async1': true}); + } + function asyncValidator2() { + return of({'async2': true}); + } + + const a = b.array(['one', 'two'], null, [asyncValidator1, asyncValidator2]); + expect(a.value).toEqual(['one', 'two']); + + tick(); + + expect(a.errors).toEqual({'async1': true, 'async2': true}); + })); + + it('should create control arrays with multiple sync validators', () => { + function syncValidator1() { + return {'sync1': true}; + } + function syncValidator2() { + return {'sync2': true}; + } + + const a = b.array(['one', 'two'], [syncValidator1, syncValidator2]); + expect(a.value).toEqual(['one', 'two']); + expect(a.errors).toEqual({'sync1': true, 'sync2': true}); + }); - it('should create control arrays with multiple sync validators', () => { - function syncValidator1() { return {'sync1': true}; } - function syncValidator2() { return {'sync2': true}; } + describe('updateOn', () => { + it('should default to on change', () => { + const c = b.control(''); + expect(c.updateOn).toEqual('change'); + }); - const a = b.array(['one', 'two'], [syncValidator1, syncValidator2]); - expect(a.value).toEqual(['one', 'two']); - expect(a.errors).toEqual({'sync1': true, 'sync2': true}); + it('should default to on change with an options obj', () => { + const c = b.control('', {validators: Validators.required}); + expect(c.updateOn).toEqual('change'); }); - describe('updateOn', () => { - it('should default to on change', () => { - const c = b.control(''); - expect(c.updateOn).toEqual('change'); - }); + it('should set updateOn when updating on blur', () => { + const c = b.control('', {updateOn: 'blur'}); + expect(c.updateOn).toEqual('blur'); + }); - it('should default to on change with an options obj', () => { - const c = b.control('', {validators: Validators.required}); - expect(c.updateOn).toEqual('change'); - }); + describe('in groups and arrays', () => { + it('should default to group updateOn when not set in control', () => { + const g = b.group({one: b.control(''), two: b.control('')}, {updateOn: 'blur'}); - it('should set updateOn when updating on blur', () => { - const c = b.control('', {updateOn: 'blur'}); - expect(c.updateOn).toEqual('blur'); + expect(g.get('one')!.updateOn).toEqual('blur'); + expect(g.get('two')!.updateOn).toEqual('blur'); }); - describe('in groups and arrays', () => { - it('should default to group updateOn when not set in control', () => { - const g = b.group({one: b.control(''), two: b.control('')}, {updateOn: 'blur'}); + it('should default to array updateOn when not set in control', () => { + const a = b.array([b.control(''), b.control('')], {updateOn: 'blur'}); - expect(g.get('one') !.updateOn).toEqual('blur'); - expect(g.get('two') !.updateOn).toEqual('blur'); - }); + expect(a.get([0])!.updateOn).toEqual('blur'); + expect(a.get([1])!.updateOn).toEqual('blur'); + }); - it('should default to array updateOn when not set in control', () => { - const a = b.array([b.control(''), b.control('')], {updateOn: 'blur'}); + it('should set updateOn with nested groups', () => { + const g = b.group( + { + group: b.group({one: b.control(''), two: b.control('')}), + }, + {updateOn: 'blur'}); - expect(a.get([0]) !.updateOn).toEqual('blur'); - expect(a.get([1]) !.updateOn).toEqual('blur'); - }); - - it('should set updateOn with nested groups', () => { - const g = b.group( - { - group: b.group({one: b.control(''), two: b.control('')}), - }, - {updateOn: 'blur'}); + expect(g.get('group.one')!.updateOn).toEqual('blur'); + expect(g.get('group.two')!.updateOn).toEqual('blur'); + expect(g.get('group')!.updateOn).toEqual('blur'); + }); - expect(g.get('group.one') !.updateOn).toEqual('blur'); - expect(g.get('group.two') !.updateOn).toEqual('blur'); - expect(g.get('group') !.updateOn).toEqual('blur'); - }); + it('should set updateOn with nested arrays', () => { + const g = b.group( + { + arr: b.array([b.control(''), b.control('')]), + }, + {updateOn: 'blur'}); - it('should set updateOn with nested arrays', () => { - const g = b.group( - { - arr: b.array([b.control(''), b.control('')]), - }, - {updateOn: 'blur'}); + expect(g.get(['arr', 0])!.updateOn).toEqual('blur'); + expect(g.get(['arr', 1])!.updateOn).toEqual('blur'); + expect(g.get('arr')!.updateOn).toEqual('blur'); + }); - expect(g.get(['arr', 0]) !.updateOn).toEqual('blur'); - expect(g.get(['arr', 1]) !.updateOn).toEqual('blur'); - expect(g.get('arr') !.updateOn).toEqual('blur'); - }); + it('should allow control updateOn to override group updateOn', () => { + const g = b.group( + {one: b.control('', {updateOn: 'change'}), two: b.control('')}, {updateOn: 'blur'}); - it('should allow control updateOn to override group updateOn', () => { - const g = b.group( - {one: b.control('', {updateOn: 'change'}), two: b.control('')}, {updateOn: 'blur'}); + expect(g.get('one')!.updateOn).toEqual('change'); + expect(g.get('two')!.updateOn).toEqual('blur'); + }); - expect(g.get('one') !.updateOn).toEqual('change'); - expect(g.get('two') !.updateOn).toEqual('blur'); + it('should set updateOn with complex setup', () => { + const g = b.group({ + group: b.group( + {one: b.control('', {updateOn: 'change'}), two: b.control('')}, {updateOn: 'blur'}), + groupTwo: b.group({one: b.control('')}, {updateOn: 'submit'}), + three: b.control('') }); - it('should set updateOn with complex setup', () => { - const g = b.group({ - group: b.group( - {one: b.control('', {updateOn: 'change'}), two: b.control('')}, {updateOn: 'blur'}), - groupTwo: b.group({one: b.control('')}, {updateOn: 'submit'}), - three: b.control('') - }); - - expect(g.get('group.one') !.updateOn).toEqual('change'); - expect(g.get('group.two') !.updateOn).toEqual('blur'); - expect(g.get('groupTwo.one') !.updateOn).toEqual('submit'); - expect(g.get('three') !.updateOn).toEqual('change'); - }); + expect(g.get('group.one')!.updateOn).toEqual('change'); + expect(g.get('group.two')!.updateOn).toEqual('blur'); + expect(g.get('groupTwo.one')!.updateOn).toEqual('submit'); + expect(g.get('three')!.updateOn).toEqual('change'); }); }); }); +}); })(); diff --git a/packages/forms/test/form_control_spec.ts b/packages/forms/test/form_control_spec.ts index 0cf27f38cb7f1..2f40bc982efb4 100644 --- a/packages/forms/test/form_control_spec.ts +++ b/packages/forms/test/form_control_spec.ts @@ -14,1240 +14,1260 @@ import {AbstractControl, AsyncValidatorFn, FormControl, FormGroup, ValidationErr import {FormArray} from '@angular/forms/src/model'; (function() { - function asyncValidator(expected: string, timeouts = {}): AsyncValidatorFn { - return (c: AbstractControl) => { - let resolve: (result: any) => void = undefined !; - const promise = new Promise<ValidationErrors|null>(res => { resolve = res; }); - const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; - const res = c.value != expected ? {'async': true} : null; - - if (t == 0) { +function asyncValidator(expected: string, timeouts = {}): AsyncValidatorFn { + return (c: AbstractControl) => { + let resolve: (result: any) => void = undefined!; + const promise = new Promise<ValidationErrors|null>(res => { + resolve = res; + }); + const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; + const res = c.value != expected ? {'async': true} : null; + + if (t == 0) { + resolve(res); + } else { + setTimeout(() => { resolve(res); - } else { - setTimeout(() => { resolve(res); }, t); - } + }, t); + } - return promise; - }; - } + return promise; + }; +} - function asyncValidatorReturningObservable(c: AbstractControl) { - const e = new EventEmitter(); - Promise.resolve(null).then(() => { e.emit({'async': true}); }); - return e; - } +function asyncValidatorReturningObservable(c: AbstractControl) { + const e = new EventEmitter(); + Promise.resolve(null).then(() => { + e.emit({'async': true}); + }); + return e; +} - function otherAsyncValidator() { return Promise.resolve({'other': true}); } +function otherAsyncValidator() { + return Promise.resolve({'other': true}); +} - function syncValidator(_: any /** TODO #9100 */): any /** TODO #9100 */ { return null; } +function syncValidator(_: any /** TODO #9100 */): any /** TODO #9100 */ { + return null; +} - describe('FormControl', () => { - it('should default the value to null', () => { - const c = new FormControl(); - expect(c.value).toBe(null); - }); +describe('FormControl', () => { + it('should default the value to null', () => { + const c = new FormControl(); + expect(c.value).toBe(null); + }); - describe('markAllAsTouched', () => { - it('should mark only the control itself as touched', () => { - const control = new FormControl(''); - expect(control.touched).toBe(false); - control.markAllAsTouched(); - expect(control.touched).toBe(true); - }); + describe('markAllAsTouched', () => { + it('should mark only the control itself as touched', () => { + const control = new FormControl(''); + expect(control.touched).toBe(false); + control.markAllAsTouched(); + expect(control.touched).toBe(true); }); + }); - describe('boxed values', () => { - it('should support valid boxed values on creation', () => { - const c = new FormControl({value: 'some val', disabled: true}, null !, null !); - expect(c.disabled).toBe(true); - expect(c.value).toBe('some val'); - expect(c.status).toBe('DISABLED'); - }); + describe('boxed values', () => { + it('should support valid boxed values on creation', () => { + const c = new FormControl({value: 'some val', disabled: true}, null!, null!); + expect(c.disabled).toBe(true); + expect(c.value).toBe('some val'); + expect(c.status).toBe('DISABLED'); + }); - it('should honor boxed value with disabled control when validating', () => { - const c = new FormControl({value: '', disabled: true}, Validators.required); - expect(c.disabled).toBe(true); - expect(c.valid).toBe(false); - expect(c.status).toBe('DISABLED'); - }); + it('should honor boxed value with disabled control when validating', () => { + const c = new FormControl({value: '', disabled: true}, Validators.required); + expect(c.disabled).toBe(true); + expect(c.valid).toBe(false); + expect(c.status).toBe('DISABLED'); + }); - it('should not treat objects as boxed values if they have more than two props', () => { - const c = new FormControl({value: '', disabled: true, test: 'test'}, null !, null !); - expect(c.value).toEqual({value: '', disabled: true, test: 'test'}); - expect(c.disabled).toBe(false); - }); + it('should not treat objects as boxed values if they have more than two props', () => { + const c = new FormControl({value: '', disabled: true, test: 'test'}, null!, null!); + expect(c.value).toEqual({value: '', disabled: true, test: 'test'}); + expect(c.disabled).toBe(false); + }); - it('should not treat objects as boxed values if disabled is missing', () => { - const c = new FormControl({value: '', test: 'test'}, null !, null !); - expect(c.value).toEqual({value: '', test: 'test'}); - expect(c.disabled).toBe(false); - }); + it('should not treat objects as boxed values if disabled is missing', () => { + const c = new FormControl({value: '', test: 'test'}, null!, null!); + expect(c.value).toEqual({value: '', test: 'test'}); + expect(c.disabled).toBe(false); + }); + }); + describe('updateOn', () => { + it('should default to on change', () => { + const c = new FormControl(''); + expect(c.updateOn).toEqual('change'); }); - describe('updateOn', () => { + it('should default to on change with an options obj', () => { + const c = new FormControl('', {validators: Validators.required}); + expect(c.updateOn).toEqual('change'); + }); - it('should default to on change', () => { - const c = new FormControl(''); - expect(c.updateOn).toEqual('change'); - }); + it('should set updateOn when updating on blur', () => { + const c = new FormControl('', {updateOn: 'blur'}); + expect(c.updateOn).toEqual('blur'); + }); - it('should default to on change with an options obj', () => { - const c = new FormControl('', {validators: Validators.required}); - expect(c.updateOn).toEqual('change'); - }); + describe('in groups and arrays', () => { + it('should default to group updateOn when not set in control', () => { + const g = + new FormGroup({one: new FormControl(), two: new FormControl()}, {updateOn: 'blur'}); - it('should set updateOn when updating on blur', () => { - const c = new FormControl('', {updateOn: 'blur'}); - expect(c.updateOn).toEqual('blur'); + expect(g.get('one')!.updateOn).toEqual('blur'); + expect(g.get('two')!.updateOn).toEqual('blur'); }); - describe('in groups and arrays', () => { - it('should default to group updateOn when not set in control', () => { - const g = - new FormGroup({one: new FormControl(), two: new FormControl()}, {updateOn: 'blur'}); + it('should default to array updateOn when not set in control', () => { + const a = new FormArray([new FormControl(), new FormControl()], {updateOn: 'blur'}); - expect(g.get('one') !.updateOn).toEqual('blur'); - expect(g.get('two') !.updateOn).toEqual('blur'); - }); + expect(a.get([0])!.updateOn).toEqual('blur'); + expect(a.get([1])!.updateOn).toEqual('blur'); + }); - it('should default to array updateOn when not set in control', () => { - const a = new FormArray([new FormControl(), new FormControl()], {updateOn: 'blur'}); + it('should set updateOn with nested groups', () => { + const g = new FormGroup( + { + group: new FormGroup({one: new FormControl(), two: new FormControl()}), + }, + {updateOn: 'blur'}); - expect(a.get([0]) !.updateOn).toEqual('blur'); - expect(a.get([1]) !.updateOn).toEqual('blur'); - }); + expect(g.get('group.one')!.updateOn).toEqual('blur'); + expect(g.get('group.two')!.updateOn).toEqual('blur'); + expect(g.get('group')!.updateOn).toEqual('blur'); + }); - it('should set updateOn with nested groups', () => { - const g = new FormGroup( - { - group: new FormGroup({one: new FormControl(), two: new FormControl()}), - }, - {updateOn: 'blur'}); + it('should set updateOn with nested arrays', () => { + const g = new FormGroup( + { + arr: new FormArray([new FormControl(), new FormControl()]), + }, + {updateOn: 'blur'}); - expect(g.get('group.one') !.updateOn).toEqual('blur'); - expect(g.get('group.two') !.updateOn).toEqual('blur'); - expect(g.get('group') !.updateOn).toEqual('blur'); - }); + expect(g.get(['arr', 0])!.updateOn).toEqual('blur'); + expect(g.get(['arr', 1])!.updateOn).toEqual('blur'); + expect(g.get('arr')!.updateOn).toEqual('blur'); + }); - it('should set updateOn with nested arrays', () => { - const g = new FormGroup( - { - arr: new FormArray([new FormControl(), new FormControl()]), - }, - {updateOn: 'blur'}); + it('should allow control updateOn to override group updateOn', () => { + const g = new FormGroup( + {one: new FormControl('', {updateOn: 'change'}), two: new FormControl()}, + {updateOn: 'blur'}); - expect(g.get(['arr', 0]) !.updateOn).toEqual('blur'); - expect(g.get(['arr', 1]) !.updateOn).toEqual('blur'); - expect(g.get('arr') !.updateOn).toEqual('blur'); - }); + expect(g.get('one')!.updateOn).toEqual('change'); + expect(g.get('two')!.updateOn).toEqual('blur'); + }); - it('should allow control updateOn to override group updateOn', () => { - const g = new FormGroup( + it('should set updateOn with complex setup', () => { + const g = new FormGroup({ + group: new FormGroup( {one: new FormControl('', {updateOn: 'change'}), two: new FormControl()}, - {updateOn: 'blur'}); - - expect(g.get('one') !.updateOn).toEqual('change'); - expect(g.get('two') !.updateOn).toEqual('blur'); + {updateOn: 'blur'}), + groupTwo: new FormGroup({one: new FormControl()}, {updateOn: 'submit'}), + three: new FormControl() }); - it('should set updateOn with complex setup', () => { - const g = new FormGroup({ - group: new FormGroup( - {one: new FormControl('', {updateOn: 'change'}), two: new FormControl()}, - {updateOn: 'blur'}), - groupTwo: new FormGroup({one: new FormControl()}, {updateOn: 'submit'}), - three: new FormControl() - }); - - expect(g.get('group.one') !.updateOn).toEqual('change'); - expect(g.get('group.two') !.updateOn).toEqual('blur'); - expect(g.get('groupTwo.one') !.updateOn).toEqual('submit'); - expect(g.get('three') !.updateOn).toEqual('change'); - }); - - + expect(g.get('group.one')!.updateOn).toEqual('change'); + expect(g.get('group.two')!.updateOn).toEqual('blur'); + expect(g.get('groupTwo.one')!.updateOn).toEqual('submit'); + expect(g.get('three')!.updateOn).toEqual('change'); }); - }); + }); - describe('validator', () => { - - it('should run validator with the initial value', () => { - const c = new FormControl('value', Validators.required); - expect(c.valid).toEqual(true); - }); + describe('validator', () => { + it('should run validator with the initial value', () => { + const c = new FormControl('value', Validators.required); + expect(c.valid).toEqual(true); + }); - it('should rerun the validator when the value changes', () => { - const c = new FormControl('value', Validators.required); - c.setValue(null); - expect(c.valid).toEqual(false); - }); + it('should rerun the validator when the value changes', () => { + const c = new FormControl('value', Validators.required); + c.setValue(null); + expect(c.valid).toEqual(false); + }); - it('should support arrays of validator functions if passed', () => { - const c = new FormControl('value', [Validators.required, Validators.minLength(3)]); - c.setValue('a'); - expect(c.valid).toEqual(false); + it('should support arrays of validator functions if passed', () => { + const c = new FormControl('value', [Validators.required, Validators.minLength(3)]); + c.setValue('a'); + expect(c.valid).toEqual(false); - c.setValue('aaa'); - expect(c.valid).toEqual(true); - }); + c.setValue('aaa'); + expect(c.valid).toEqual(true); + }); - it('should support single validator from options obj', () => { - const c = new FormControl(null, {validators: Validators.required}); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({required: true}); + it('should support single validator from options obj', () => { + const c = new FormControl(null, {validators: Validators.required}); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({required: true}); - c.setValue('value'); - expect(c.valid).toEqual(true); - }); + c.setValue('value'); + expect(c.valid).toEqual(true); + }); - it('should support multiple validators from options obj', () => { - const c = - new FormControl(null, {validators: [Validators.required, Validators.minLength(3)]}); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({required: true}); + it('should support multiple validators from options obj', () => { + const c = new FormControl(null, {validators: [Validators.required, Validators.minLength(3)]}); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({required: true}); - c.setValue('aa'); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({minlength: {requiredLength: 3, actualLength: 2}}); + c.setValue('aa'); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({minlength: {requiredLength: 3, actualLength: 2}}); - c.setValue('aaa'); - expect(c.valid).toEqual(true); - }); + c.setValue('aaa'); + expect(c.valid).toEqual(true); + }); - it('should support a null validators value', () => { - const c = new FormControl(null, {validators: null}); - expect(c.valid).toEqual(true); - }); + it('should support a null validators value', () => { + const c = new FormControl(null, {validators: null}); + expect(c.valid).toEqual(true); + }); - it('should support an empty options obj', () => { - const c = new FormControl(null, {}); - expect(c.valid).toEqual(true); - }); + it('should support an empty options obj', () => { + const c = new FormControl(null, {}); + expect(c.valid).toEqual(true); + }); - it('should return errors', () => { - const c = new FormControl(null, Validators.required); - expect(c.errors).toEqual({'required': true}); - }); + it('should return errors', () => { + const c = new FormControl(null, Validators.required); + expect(c.errors).toEqual({'required': true}); + }); - it('should set single validator', () => { - const c = new FormControl(null); - expect(c.valid).toEqual(true); + it('should set single validator', () => { + const c = new FormControl(null); + expect(c.valid).toEqual(true); - c.setValidators(Validators.required); + c.setValidators(Validators.required); - c.setValue(null); - expect(c.valid).toEqual(false); + c.setValue(null); + expect(c.valid).toEqual(false); - c.setValue('abc'); - expect(c.valid).toEqual(true); - }); + c.setValue('abc'); + expect(c.valid).toEqual(true); + }); - it('should set multiple validators from array', () => { - const c = new FormControl(''); - expect(c.valid).toEqual(true); + it('should set multiple validators from array', () => { + const c = new FormControl(''); + expect(c.valid).toEqual(true); - c.setValidators([Validators.minLength(5), Validators.required]); + c.setValidators([Validators.minLength(5), Validators.required]); - c.setValue(''); - expect(c.valid).toEqual(false); + c.setValue(''); + expect(c.valid).toEqual(false); - c.setValue('abc'); - expect(c.valid).toEqual(false); + c.setValue('abc'); + expect(c.valid).toEqual(false); - c.setValue('abcde'); - expect(c.valid).toEqual(true); - }); + c.setValue('abcde'); + expect(c.valid).toEqual(true); + }); - it('should clear validators', () => { - const c = new FormControl('', Validators.required); - expect(c.valid).toEqual(false); + it('should clear validators', () => { + const c = new FormControl('', Validators.required); + expect(c.valid).toEqual(false); - c.clearValidators(); - expect(c.validator).toEqual(null); + c.clearValidators(); + expect(c.validator).toEqual(null); - c.setValue(''); - expect(c.valid).toEqual(true); - }); + c.setValue(''); + expect(c.valid).toEqual(true); + }); - it('should add after clearing', () => { - const c = new FormControl('', Validators.required); - expect(c.valid).toEqual(false); + it('should add after clearing', () => { + const c = new FormControl('', Validators.required); + expect(c.valid).toEqual(false); - c.clearValidators(); - expect(c.validator).toEqual(null); + c.clearValidators(); + expect(c.validator).toEqual(null); - c.setValidators([Validators.required]); - expect(c.validator).not.toBe(null); - }); + c.setValidators([Validators.required]); + expect(c.validator).not.toBe(null); }); + }); - describe('asyncValidator', () => { - it('should run validator with the initial value', fakeAsync(() => { - const c = new FormControl('value', null !, asyncValidator('expected')); - tick(); + describe('asyncValidator', () => { + it('should run validator with the initial value', fakeAsync(() => { + const c = new FormControl('value', null!, asyncValidator('expected')); + tick(); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({'async': true}); - })); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({'async': true}); + })); - it('should support validators returning observables', fakeAsync(() => { - const c = new FormControl('value', null !, asyncValidatorReturningObservable); - tick(); + it('should support validators returning observables', fakeAsync(() => { + const c = new FormControl('value', null!, asyncValidatorReturningObservable); + tick(); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({'async': true}); - })); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({'async': true}); + })); - it('should rerun the validator when the value changes', fakeAsync(() => { - const c = new FormControl('value', null !, asyncValidator('expected')); + it('should rerun the validator when the value changes', fakeAsync(() => { + const c = new FormControl('value', null!, asyncValidator('expected')); - c.setValue('expected'); - tick(); + c.setValue('expected'); + tick(); - expect(c.valid).toEqual(true); - })); + expect(c.valid).toEqual(true); + })); - it('should run the async validator only when the sync validator passes', fakeAsync(() => { - const c = new FormControl('', Validators.required, asyncValidator('expected')); - tick(); + it('should run the async validator only when the sync validator passes', fakeAsync(() => { + const c = new FormControl('', Validators.required, asyncValidator('expected')); + tick(); - expect(c.errors).toEqual({'required': true}); + expect(c.errors).toEqual({'required': true}); - c.setValue('some value'); - tick(); + c.setValue('some value'); + tick(); - expect(c.errors).toEqual({'async': true}); - })); + expect(c.errors).toEqual({'async': true}); + })); - it('should mark the control as pending while running the async validation', fakeAsync(() => { - const c = new FormControl('', null !, asyncValidator('expected')); + it('should mark the control as pending while running the async validation', fakeAsync(() => { + const c = new FormControl('', null!, asyncValidator('expected')); - expect(c.pending).toEqual(true); + expect(c.pending).toEqual(true); - tick(); + tick(); - expect(c.pending).toEqual(false); - })); + expect(c.pending).toEqual(false); + })); - it('should only use the latest async validation run', fakeAsync(() => { - const c = new FormControl( - '', null !, asyncValidator('expected', {'long': 200, 'expected': 100})); + it('should only use the latest async validation run', fakeAsync(() => { + const c = + new FormControl('', null!, asyncValidator('expected', {'long': 200, 'expected': 100})); - c.setValue('long'); - c.setValue('expected'); + c.setValue('long'); + c.setValue('expected'); - tick(300); + tick(300); - expect(c.valid).toEqual(true); - })); + expect(c.valid).toEqual(true); + })); - it('should support arrays of async validator functions if passed', fakeAsync(() => { - const c = - new FormControl('value', null !, [asyncValidator('expected'), otherAsyncValidator]); - tick(); + it('should support arrays of async validator functions if passed', fakeAsync(() => { + const c = + new FormControl('value', null!, [asyncValidator('expected'), otherAsyncValidator]); + tick(); - expect(c.errors).toEqual({'async': true, 'other': true}); - })); + expect(c.errors).toEqual({'async': true, 'other': true}); + })); - it('should support a single async validator from options obj', fakeAsync(() => { - const c = new FormControl('value', {asyncValidators: asyncValidator('expected')}); - expect(c.pending).toEqual(true); - tick(); + it('should support a single async validator from options obj', fakeAsync(() => { + const c = new FormControl('value', {asyncValidators: asyncValidator('expected')}); + expect(c.pending).toEqual(true); + tick(); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({'async': true}); - })); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({'async': true}); + })); - it('should support multiple async validators from options obj', fakeAsync(() => { - const c = new FormControl( - 'value', {asyncValidators: [asyncValidator('expected'), otherAsyncValidator]}); - expect(c.pending).toEqual(true); - tick(); + it('should support multiple async validators from options obj', fakeAsync(() => { + const c = new FormControl( + 'value', {asyncValidators: [asyncValidator('expected'), otherAsyncValidator]}); + expect(c.pending).toEqual(true); + tick(); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({'async': true, 'other': true}); - })); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({'async': true, 'other': true}); + })); - it('should support a mix of validators from options obj', fakeAsync(() => { - const c = new FormControl( - '', {validators: Validators.required, asyncValidators: asyncValidator('expected')}); - tick(); - expect(c.errors).toEqual({required: true}); + it('should support a mix of validators from options obj', fakeAsync(() => { + const c = new FormControl( + '', {validators: Validators.required, asyncValidators: asyncValidator('expected')}); + tick(); + expect(c.errors).toEqual({required: true}); - c.setValue('value'); - expect(c.pending).toBe(true); + c.setValue('value'); + expect(c.pending).toBe(true); - tick(); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({'async': true}); - })); + tick(); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({'async': true}); + })); - it('should add single async validator', fakeAsync(() => { - const c = new FormControl('value', null !); + it('should add single async validator', fakeAsync(() => { + const c = new FormControl('value', null!); - c.setAsyncValidators(asyncValidator('expected')); - expect(c.asyncValidator).not.toEqual(null); + c.setAsyncValidators(asyncValidator('expected')); + expect(c.asyncValidator).not.toEqual(null); - c.setValue('expected'); - tick(); + c.setValue('expected'); + tick(); - expect(c.valid).toEqual(true); - })); + expect(c.valid).toEqual(true); + })); - it('should add async validator from array', fakeAsync(() => { - const c = new FormControl('value', null !); + it('should add async validator from array', fakeAsync(() => { + const c = new FormControl('value', null!); - c.setAsyncValidators([asyncValidator('expected')]); - expect(c.asyncValidator).not.toEqual(null); + c.setAsyncValidators([asyncValidator('expected')]); + expect(c.asyncValidator).not.toEqual(null); - c.setValue('expected'); - tick(); + c.setValue('expected'); + tick(); - expect(c.valid).toEqual(true); - })); + expect(c.valid).toEqual(true); + })); - it('should clear async validators', fakeAsync(() => { - const c = new FormControl('value', [asyncValidator('expected'), otherAsyncValidator]); + it('should clear async validators', fakeAsync(() => { + const c = new FormControl('value', [asyncValidator('expected'), otherAsyncValidator]); - c.clearValidators(); + c.clearValidators(); - expect(c.asyncValidator).toEqual(null); - })); + expect(c.asyncValidator).toEqual(null); + })); - it('should not change validity state if control is disabled while async validating', - fakeAsync(() => { - const c = new FormControl('value', [asyncValidator('expected')]); - c.disable(); - tick(); - expect(c.status).toEqual('DISABLED'); - })); + it('should not change validity state if control is disabled while async validating', + fakeAsync(() => { + const c = new FormControl('value', [asyncValidator('expected')]); + c.disable(); + tick(); + expect(c.status).toEqual('DISABLED'); + })); + }); + + describe('dirty', () => { + it('should be false after creating a control', () => { + const c = new FormControl('value'); + expect(c.dirty).toEqual(false); }); - describe('dirty', () => { - it('should be false after creating a control', () => { - const c = new FormControl('value'); - expect(c.dirty).toEqual(false); - }); + it('should be true after changing the value of the control', () => { + const c = new FormControl('value'); + c.markAsDirty(); + expect(c.dirty).toEqual(true); + }); + }); - it('should be true after changing the value of the control', () => { - const c = new FormControl('value'); - c.markAsDirty(); - expect(c.dirty).toEqual(true); - }); + describe('touched', () => { + it('should be false after creating a control', () => { + const c = new FormControl('value'); + expect(c.touched).toEqual(false); }); - describe('touched', () => { - it('should be false after creating a control', () => { - const c = new FormControl('value'); - expect(c.touched).toEqual(false); - }); + it('should be true after markAsTouched runs', () => { + const c = new FormControl('value'); + c.markAsTouched(); + expect(c.touched).toEqual(true); + }); + }); - it('should be true after markAsTouched runs', () => { - const c = new FormControl('value'); - c.markAsTouched(); - expect(c.touched).toEqual(true); - }); + describe('setValue', () => { + let g: FormGroup, c: FormControl; + beforeEach(() => { + c = new FormControl('oldValue'); + g = new FormGroup({'one': c}); }); - describe('setValue', () => { - let g: FormGroup, c: FormControl; - beforeEach(() => { - c = new FormControl('oldValue'); - g = new FormGroup({'one': c}); - }); + it('should set the value of the control', () => { + c.setValue('newValue'); + expect(c.value).toEqual('newValue'); + }); - it('should set the value of the control', () => { - c.setValue('newValue'); - expect(c.value).toEqual('newValue'); - }); + it('should invoke ngOnChanges if it is present', () => { + let ngOnChanges: any; + c.registerOnChange((v: any) => ngOnChanges = ['invoked', v]); - it('should invoke ngOnChanges if it is present', () => { - let ngOnChanges: any; - c.registerOnChange((v: any) => ngOnChanges = ['invoked', v]); + c.setValue('newValue'); - c.setValue('newValue'); + expect(ngOnChanges).toEqual(['invoked', 'newValue']); + }); - expect(ngOnChanges).toEqual(['invoked', 'newValue']); - }); + it('should not invoke on change when explicitly specified', () => { + let onChange: any = null; + c.registerOnChange((v: any) => onChange = ['invoked', v]); - it('should not invoke on change when explicitly specified', () => { - let onChange: any = null; - c.registerOnChange((v: any) => onChange = ['invoked', v]); + c.setValue('newValue', {emitModelToViewChange: false}); - c.setValue('newValue', {emitModelToViewChange: false}); + expect(onChange).toBeNull(); + }); - expect(onChange).toBeNull(); - }); + it('should set the parent', () => { + c.setValue('newValue'); + expect(g.value).toEqual({'one': 'newValue'}); + }); - it('should set the parent', () => { - c.setValue('newValue'); - expect(g.value).toEqual({'one': 'newValue'}); - }); + it('should not set the parent when explicitly specified', () => { + c.setValue('newValue', {onlySelf: true}); + expect(g.value).toEqual({'one': 'oldValue'}); + }); - it('should not set the parent when explicitly specified', () => { - c.setValue('newValue', {onlySelf: true}); - expect(g.value).toEqual({'one': 'oldValue'}); - }); + it('should fire an event', fakeAsync(() => { + c.valueChanges.subscribe((value) => { + expect(value).toEqual('newValue'); + }); + + c.setValue('newValue'); + tick(); + })); + + it('should not fire an event when explicitly specified', fakeAsync(() => { + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + + c.setValue('newValue', {emitEvent: false}); + tick(); + })); + + it('should work on a disabled control', () => { + g.addControl('two', new FormControl('two')); + c.disable(); + c.setValue('new value'); + expect(c.value).toEqual('new value'); + expect(g.value).toEqual({'two': 'two'}); + }); + }); - it('should fire an event', fakeAsync(() => { - c.valueChanges.subscribe((value) => { expect(value).toEqual('newValue'); }); + describe('patchValue', () => { + let g: FormGroup, c: FormControl; + beforeEach(() => { + c = new FormControl('oldValue'); + g = new FormGroup({'one': c}); + }); - c.setValue('newValue'); - tick(); - })); + it('should set the value of the control', () => { + c.patchValue('newValue'); + expect(c.value).toEqual('newValue'); + }); - it('should not fire an event when explicitly specified', fakeAsync(() => { - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); + it('should invoke ngOnChanges if it is present', () => { + let ngOnChanges: any; + c.registerOnChange((v: any) => ngOnChanges = ['invoked', v]); - c.setValue('newValue', {emitEvent: false}); - tick(); - })); + c.patchValue('newValue'); - it('should work on a disabled control', () => { - g.addControl('two', new FormControl('two')); - c.disable(); - c.setValue('new value'); - expect(c.value).toEqual('new value'); - expect(g.value).toEqual({'two': 'two'}); - }); + expect(ngOnChanges).toEqual(['invoked', 'newValue']); }); - describe('patchValue', () => { - let g: FormGroup, c: FormControl; - beforeEach(() => { - c = new FormControl('oldValue'); - g = new FormGroup({'one': c}); - }); + it('should not invoke on change when explicitly specified', () => { + let onChange: any = null; + c.registerOnChange((v: any) => onChange = ['invoked', v]); - it('should set the value of the control', () => { - c.patchValue('newValue'); - expect(c.value).toEqual('newValue'); - }); + c.patchValue('newValue', {emitModelToViewChange: false}); - it('should invoke ngOnChanges if it is present', () => { - let ngOnChanges: any; - c.registerOnChange((v: any) => ngOnChanges = ['invoked', v]); + expect(onChange).toBeNull(); + }); - c.patchValue('newValue'); + it('should set the parent', () => { + c.patchValue('newValue'); + expect(g.value).toEqual({'one': 'newValue'}); + }); - expect(ngOnChanges).toEqual(['invoked', 'newValue']); - }); + it('should not set the parent when explicitly specified', () => { + c.patchValue('newValue', {onlySelf: true}); + expect(g.value).toEqual({'one': 'oldValue'}); + }); - it('should not invoke on change when explicitly specified', () => { - let onChange: any = null; - c.registerOnChange((v: any) => onChange = ['invoked', v]); + it('should fire an event', fakeAsync(() => { + c.valueChanges.subscribe((value) => { + expect(value).toEqual('newValue'); + }); - c.patchValue('newValue', {emitModelToViewChange: false}); + c.patchValue('newValue'); + tick(); + })); - expect(onChange).toBeNull(); - }); + it('should not fire an event when explicitly specified', fakeAsync(() => { + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); - it('should set the parent', () => { - c.patchValue('newValue'); - expect(g.value).toEqual({'one': 'newValue'}); - }); + c.patchValue('newValue', {emitEvent: false}); - it('should not set the parent when explicitly specified', () => { - c.patchValue('newValue', {onlySelf: true}); - expect(g.value).toEqual({'one': 'oldValue'}); - }); - - it('should fire an event', fakeAsync(() => { - c.valueChanges.subscribe((value) => { expect(value).toEqual('newValue'); }); + tick(); + })); - c.patchValue('newValue'); - tick(); - })); + it('should patch value on a disabled control', () => { + g.addControl('two', new FormControl('two')); + c.disable(); - it('should not fire an event when explicitly specified', fakeAsync(() => { - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); + c.patchValue('new value'); + expect(c.value).toEqual('new value'); + expect(g.value).toEqual({'two': 'two'}); + }); + }); - c.patchValue('newValue', {emitEvent: false}); + describe('reset()', () => { + let c: FormControl; - tick(); - })); + beforeEach(() => { + c = new FormControl('initial value'); + }); - it('should patch value on a disabled control', () => { - g.addControl('two', new FormControl('two')); - c.disable(); + it('should reset to a specific value if passed', () => { + c.setValue('new value'); + expect(c.value).toBe('new value'); - c.patchValue('new value'); - expect(c.value).toEqual('new value'); - expect(g.value).toEqual({'two': 'two'}); - }); + c.reset('initial value'); + expect(c.value).toBe('initial value'); }); - describe('reset()', () => { - let c: FormControl; + it('should not set the parent when explicitly specified', () => { + const g = new FormGroup({'one': c}); + c.patchValue('newValue', {onlySelf: true}); + expect(g.value).toEqual({'one': 'initial value'}); + }); - beforeEach(() => { c = new FormControl('initial value'); }); + it('should reset to a specific value if passed with boxed value', () => { + c.setValue('new value'); + expect(c.value).toBe('new value'); - it('should reset to a specific value if passed', () => { - c.setValue('new value'); - expect(c.value).toBe('new value'); + c.reset({value: 'initial value', disabled: false}); + expect(c.value).toBe('initial value'); + }); - c.reset('initial value'); - expect(c.value).toBe('initial value'); - }); + it('should clear the control value if no value is passed', () => { + c.setValue('new value'); + expect(c.value).toBe('new value'); - it('should not set the parent when explicitly specified', () => { - const g = new FormGroup({'one': c}); - c.patchValue('newValue', {onlySelf: true}); - expect(g.value).toEqual({'one': 'initial value'}); - }); + c.reset(); + expect(c.value).toBe(null); + }); - it('should reset to a specific value if passed with boxed value', () => { - c.setValue('new value'); - expect(c.value).toBe('new value'); + it('should update the value of any parent controls with passed value', () => { + const g = new FormGroup({'one': c}); + c.setValue('new value'); + expect(g.value).toEqual({'one': 'new value'}); - c.reset({value: 'initial value', disabled: false}); - expect(c.value).toBe('initial value'); - }); + c.reset('initial value'); + expect(g.value).toEqual({'one': 'initial value'}); + }); - it('should clear the control value if no value is passed', () => { - c.setValue('new value'); - expect(c.value).toBe('new value'); + it('should update the value of any parent controls with null value', () => { + const g = new FormGroup({'one': c}); + c.setValue('new value'); + expect(g.value).toEqual({'one': 'new value'}); - c.reset(); - expect(c.value).toBe(null); - }); + c.reset(); + expect(g.value).toEqual({'one': null}); + }); - it('should update the value of any parent controls with passed value', () => { - const g = new FormGroup({'one': c}); - c.setValue('new value'); - expect(g.value).toEqual({'one': 'new value'}); + it('should mark the control as pristine', () => { + c.markAsDirty(); + expect(c.pristine).toBe(false); - c.reset('initial value'); - expect(g.value).toEqual({'one': 'initial value'}); - }); + c.reset(); + expect(c.pristine).toBe(true); + }); - it('should update the value of any parent controls with null value', () => { - const g = new FormGroup({'one': c}); - c.setValue('new value'); - expect(g.value).toEqual({'one': 'new value'}); + it('should set the parent pristine state if all pristine', () => { + const g = new FormGroup({'one': c}); + c.markAsDirty(); + expect(g.pristine).toBe(false); - c.reset(); - expect(g.value).toEqual({'one': null}); - }); + c.reset(); + expect(g.pristine).toBe(true); + }); - it('should mark the control as pristine', () => { - c.markAsDirty(); - expect(c.pristine).toBe(false); + it('should not set the parent pristine state if it has other dirty controls', () => { + const c2 = new FormControl('two'); + const g = new FormGroup({'one': c, 'two': c2}); + c.markAsDirty(); + c2.markAsDirty(); - c.reset(); - expect(c.pristine).toBe(true); - }); + c.reset(); + expect(g.pristine).toBe(false); + }); - it('should set the parent pristine state if all pristine', () => { - const g = new FormGroup({'one': c}); - c.markAsDirty(); - expect(g.pristine).toBe(false); + it('should mark the control as untouched', () => { + c.markAsTouched(); + expect(c.untouched).toBe(false); - c.reset(); - expect(g.pristine).toBe(true); - }); + c.reset(); + expect(c.untouched).toBe(true); + }); - it('should not set the parent pristine state if it has other dirty controls', () => { - const c2 = new FormControl('two'); - const g = new FormGroup({'one': c, 'two': c2}); - c.markAsDirty(); - c2.markAsDirty(); + it('should set the parent untouched state if all untouched', () => { + const g = new FormGroup({'one': c}); + c.markAsTouched(); + expect(g.untouched).toBe(false); - c.reset(); - expect(g.pristine).toBe(false); - }); + c.reset(); + expect(g.untouched).toBe(true); + }); - it('should mark the control as untouched', () => { - c.markAsTouched(); - expect(c.untouched).toBe(false); + it('should not set the parent untouched state if other touched controls', () => { + const c2 = new FormControl('two'); + const g = new FormGroup({'one': c, 'two': c2}); + c.markAsTouched(); + c2.markAsTouched(); - c.reset(); - expect(c.untouched).toBe(true); - }); + c.reset(); + expect(g.untouched).toBe(false); + }); - it('should set the parent untouched state if all untouched', () => { - const g = new FormGroup({'one': c}); - c.markAsTouched(); - expect(g.untouched).toBe(false); + it('should retain the disabled state of the control', () => { + c.disable(); + c.reset(); - c.reset(); - expect(g.untouched).toBe(true); - }); + expect(c.disabled).toBe(true); + }); - it('should not set the parent untouched state if other touched controls', () => { - const c2 = new FormControl('two'); - const g = new FormGroup({'one': c, 'two': c2}); - c.markAsTouched(); - c2.markAsTouched(); + it('should set disabled state based on boxed value if passed', () => { + c.disable(); + c.reset({value: null, disabled: false}); - c.reset(); - expect(g.untouched).toBe(false); - }); + expect(c.disabled).toBe(false); + }); - it('should retain the disabled state of the control', () => { - c.disable(); - c.reset(); + describe('reset() events', () => { + let g: FormGroup, c2: FormControl, logger: any[]; - expect(c.disabled).toBe(true); + beforeEach(() => { + c2 = new FormControl('two'); + g = new FormGroup({'one': c, 'two': c2}); + logger = []; }); - it('should set disabled state based on boxed value if passed', () => { - c.disable(); - c.reset({value: null, disabled: false}); + it('should emit one valueChange event per reset control', () => { + g.valueChanges.subscribe(() => logger.push('group')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); - expect(c.disabled).toBe(false); + c.reset(); + expect(logger).toEqual(['control1', 'group']); }); - describe('reset() events', () => { - let g: FormGroup, c2: FormControl, logger: any[]; + it('should not fire an event when explicitly specified', fakeAsync(() => { + g.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c2.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); - beforeEach(() => { - c2 = new FormControl('two'); - g = new FormGroup({'one': c, 'two': c2}); - logger = []; - }); + c.reset(null, {emitEvent: false}); - it('should emit one valueChange event per reset control', () => { - g.valueChanges.subscribe(() => logger.push('group')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); + tick(); + })); - c.reset(); - expect(logger).toEqual(['control1', 'group']); - }); + it('should emit one statusChange event per reset control', () => { + g.statusChanges.subscribe(() => logger.push('group')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); - it('should not fire an event when explicitly specified', fakeAsync(() => { - g.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c2.valueChanges.subscribe((value) => { throw 'Should not happen'; }); + c.reset(); + expect(logger).toEqual(['control1', 'group']); + }); - c.reset(null, {emitEvent: false}); + it('should emit one statusChange event per disabled control', () => { + g.statusChanges.subscribe(() => logger.push('group')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); - tick(); - })); + c.reset({value: null, disabled: true}); + expect(logger).toEqual(['control1', 'group']); + }); + }); + }); - it('should emit one statusChange event per reset control', () => { - g.statusChanges.subscribe(() => logger.push('group')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); + describe('valueChanges & statusChanges', () => { + let c: FormControl; - c.reset(); - expect(logger).toEqual(['control1', 'group']); - }); + beforeEach(() => { + c = new FormControl('old', Validators.required); + }); - it('should emit one statusChange event per disabled control', () => { - g.statusChanges.subscribe(() => logger.push('group')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); + it('should fire an event after the value has been updated', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + c.valueChanges.subscribe({ + next: (value: any) => { + expect(c.value).toEqual('new'); + expect(value).toEqual('new'); + async.done(); + } + }); + c.setValue('new'); + })); + + it('should fire an event after the status has been updated to invalid', fakeAsync(() => { + c.statusChanges.subscribe({ + next: (status: any) => { + expect(c.status).toEqual('INVALID'); + expect(status).toEqual('INVALID'); + } + }); + + c.setValue(''); + tick(); + })); + + it('should fire an event after the status has been updated to pending', fakeAsync(() => { + const c = new FormControl('old', Validators.required, asyncValidator('expected')); + + const log: any[] /** TODO #9100 */ = []; + c.valueChanges.subscribe({next: (value: any) => log.push(`value: '${value}'`)}); + + c.statusChanges.subscribe({next: (status: any) => log.push(`status: '${status}'`)}); + + c.setValue(''); + tick(); + + c.setValue('nonEmpty'); + tick(); + + c.setValue('expected'); + tick(); + + expect(log).toEqual([ + 'value: \'\'', + 'status: \'INVALID\'', + 'value: \'nonEmpty\'', + 'status: \'PENDING\'', + 'status: \'INVALID\'', + 'value: \'expected\'', + 'status: \'PENDING\'', + 'status: \'VALID\'', + ]); + })); + + // TODO: remove the if statement after making observable delivery sync + it('should update set errors and status before emitting an event', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + c.valueChanges.subscribe((value: any /** TODO #9100 */) => { + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({'required': true}); + async.done(); + }); + c.setValue(''); + })); + + it('should return a cold observable', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + c.setValue('will be ignored'); + c.valueChanges.subscribe({ + next: (value: any) => { + expect(value).toEqual('new'); + async.done(); + } + }); + c.setValue('new'); + })); + }); - c.reset({value: null, disabled: true}); - expect(logger).toEqual(['control1', 'group']); - }); - }); + describe('setErrors', () => { + it('should set errors on a control', () => { + const c = new FormControl('someValue'); + + c.setErrors({'someError': true}); + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({'someError': true}); }); - describe('valueChanges & statusChanges', () => { - let c: FormControl; + it('should reset the errors and validity when the value changes', () => { + const c = new FormControl('someValue', Validators.required); - beforeEach(() => { c = new FormControl('old', Validators.required); }); + c.setErrors({'someError': true}); + c.setValue(''); - it('should fire an event after the value has been updated', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - c.valueChanges.subscribe({ - next: (value: any) => { - expect(c.value).toEqual('new'); - expect(value).toEqual('new'); - async.done(); - } - }); - c.setValue('new'); - })); + expect(c.errors).toEqual({'required': true}); + }); - it('should fire an event after the status has been updated to invalid', fakeAsync(() => { - c.statusChanges.subscribe({ - next: (status: any) => { - expect(c.status).toEqual('INVALID'); - expect(status).toEqual('INVALID'); - } - }); + it('should update the parent group\'s validity', () => { + const c = new FormControl('someValue'); + const g = new FormGroup({'one': c}); - c.setValue(''); - tick(); - })); + expect(g.valid).toEqual(true); - it('should fire an event after the status has been updated to pending', fakeAsync(() => { - const c = new FormControl('old', Validators.required, asyncValidator('expected')); + c.setErrors({'someError': true}); - const log: any[] /** TODO #9100 */ = []; - c.valueChanges.subscribe({next: (value: any) => log.push(`value: '${value}'`)}); + expect(g.valid).toEqual(false); + }); - c.statusChanges.subscribe({next: (status: any) => log.push(`status: '${status}'`)}); + it('should not reset parent\'s errors', () => { + const c = new FormControl('someValue'); + const g = new FormGroup({'one': c}); - c.setValue(''); - tick(); + g.setErrors({'someGroupError': true}); + c.setErrors({'someError': true}); - c.setValue('nonEmpty'); - tick(); + expect(g.errors).toEqual({'someGroupError': true}); + }); - c.setValue('expected'); - tick(); + it('should reset errors when updating a value', () => { + const c = new FormControl('oldValue'); + const g = new FormGroup({'one': c}); - expect(log).toEqual([ - 'value: \'\'', - 'status: \'INVALID\'', - 'value: \'nonEmpty\'', - 'status: \'PENDING\'', - 'status: \'INVALID\'', - 'value: \'expected\'', - 'status: \'PENDING\'', - 'status: \'VALID\'', - ]); - })); + g.setErrors({'someGroupError': true}); + c.setErrors({'someError': true}); - // TODO: remove the if statement after making observable delivery sync - it('should update set errors and status before emitting an event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - c.valueChanges.subscribe((value: any /** TODO #9100 */) => { - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({'required': true}); - async.done(); - }); - c.setValue(''); - })); + c.setValue('newValue'); - it('should return a cold observable', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - c.setValue('will be ignored'); - c.valueChanges.subscribe({ - next: (value: any) => { - expect(value).toEqual('new'); - async.done(); - } - }); - c.setValue('new'); - })); + expect(c.errors).toEqual(null); + expect(g.errors).toEqual(null); }); + }); - describe('setErrors', () => { - it('should set errors on a control', () => { - const c = new FormControl('someValue'); - - c.setErrors({'someError': true}); + describe('disable() & enable()', () => { + it('should mark the control as disabled', () => { + const c = new FormControl(null); + expect(c.disabled).toBe(false); + expect(c.valid).toBe(true); - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({'someError': true}); - }); + c.disable(); + expect(c.disabled).toBe(true); + expect(c.valid).toBe(false); - it('should reset the errors and validity when the value changes', () => { - const c = new FormControl('someValue', Validators.required); + c.enable(); + expect(c.disabled).toBe(false); + expect(c.valid).toBe(true); + }); - c.setErrors({'someError': true}); - c.setValue(''); + it('should set the control status as disabled', () => { + const c = new FormControl(null); + expect(c.status).toEqual('VALID'); - expect(c.errors).toEqual({'required': true}); - }); + c.disable(); + expect(c.status).toEqual('DISABLED'); - it('should update the parent group\'s validity', () => { - const c = new FormControl('someValue'); - const g = new FormGroup({'one': c}); + c.enable(); + expect(c.status).toEqual('VALID'); + }); - expect(g.valid).toEqual(true); + it('should retain the original value when disabled', () => { + const c = new FormControl('some value'); + expect(c.value).toEqual('some value'); - c.setErrors({'someError': true}); + c.disable(); + expect(c.value).toEqual('some value'); - expect(g.valid).toEqual(false); - }); + c.enable(); + expect(c.value).toEqual('some value'); + }); - it('should not reset parent\'s errors', () => { - const c = new FormControl('someValue'); - const g = new FormGroup({'one': c}); + it('should keep the disabled control in the group, but return false for contains()', () => { + const c = new FormControl(''); + const g = new FormGroup({'one': c}); - g.setErrors({'someGroupError': true}); - c.setErrors({'someError': true}); + expect(g.get('one')).toBeDefined(); + expect(g.contains('one')).toBe(true); - expect(g.errors).toEqual({'someGroupError': true}); - }); + c.disable(); + expect(g.get('one')).toBeDefined(); + expect(g.contains('one')).toBe(false); + }); - it('should reset errors when updating a value', () => { - const c = new FormControl('oldValue'); - const g = new FormGroup({'one': c}); + it('should mark the parent group disabled if all controls are disabled', () => { + const c = new FormControl(); + const c2 = new FormControl(); + const g = new FormGroup({'one': c, 'two': c2}); + expect(g.enabled).toBe(true); - g.setErrors({'someGroupError': true}); - c.setErrors({'someError': true}); + c.disable(); + expect(g.enabled).toBe(true); - c.setValue('newValue'); + c2.disable(); + expect(g.enabled).toBe(false); - expect(c.errors).toEqual(null); - expect(g.errors).toEqual(null); - }); + c.enable(); + expect(g.enabled).toBe(true); }); - describe('disable() & enable()', () => { + it('should update the parent group value when child control status changes', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const g = new FormGroup({'one': c, 'two': c2}); + expect(g.value).toEqual({'one': 'one', 'two': 'two'}); - it('should mark the control as disabled', () => { - const c = new FormControl(null); - expect(c.disabled).toBe(false); - expect(c.valid).toBe(true); + c.disable(); + expect(g.value).toEqual({'two': 'two'}); - c.disable(); - expect(c.disabled).toBe(true); - expect(c.valid).toBe(false); + c2.disable(); + expect(g.value).toEqual({'one': 'one', 'two': 'two'}); - c.enable(); - expect(c.disabled).toBe(false); - expect(c.valid).toBe(true); - }); + c.enable(); + expect(g.value).toEqual({'one': 'one'}); + }); - it('should set the control status as disabled', () => { - const c = new FormControl(null); - expect(c.status).toEqual('VALID'); + it('should mark the parent array disabled if all controls are disabled', () => { + const c = new FormControl(); + const c2 = new FormControl(); + const a = new FormArray([c, c2]); + expect(a.enabled).toBe(true); - c.disable(); - expect(c.status).toEqual('DISABLED'); + c.disable(); + expect(a.enabled).toBe(true); - c.enable(); - expect(c.status).toEqual('VALID'); - }); + c2.disable(); + expect(a.enabled).toBe(false); - it('should retain the original value when disabled', () => { - const c = new FormControl('some value'); - expect(c.value).toEqual('some value'); + c.enable(); + expect(a.enabled).toBe(true); + }); - c.disable(); - expect(c.value).toEqual('some value'); + it('should update the parent array value when child control status changes', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const a = new FormArray([c, c2]); + expect(a.value).toEqual(['one', 'two']); - c.enable(); - expect(c.value).toEqual('some value'); - }); + c.disable(); + expect(a.value).toEqual(['two']); - it('should keep the disabled control in the group, but return false for contains()', () => { - const c = new FormControl(''); - const g = new FormGroup({'one': c}); + c2.disable(); + expect(a.value).toEqual(['one', 'two']); - expect(g.get('one')).toBeDefined(); - expect(g.contains('one')).toBe(true); + c.enable(); + expect(a.value).toEqual(['one']); + }); - c.disable(); - expect(g.get('one')).toBeDefined(); - expect(g.contains('one')).toBe(false); - }); + it('should ignore disabled array controls when determining dirtiness', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const a = new FormArray([c, c2]); + c.markAsDirty(); + expect(a.dirty).toBe(true); - it('should mark the parent group disabled if all controls are disabled', () => { - const c = new FormControl(); - const c2 = new FormControl(); - const g = new FormGroup({'one': c, 'two': c2}); - expect(g.enabled).toBe(true); + c.disable(); + expect(c.dirty).toBe(true); + expect(a.dirty).toBe(false); - c.disable(); - expect(g.enabled).toBe(true); + c.enable(); + expect(a.dirty).toBe(true); + }); - c2.disable(); - expect(g.enabled).toBe(false); + it('should not make a dirty array not dirty when disabling controls', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const a = new FormArray([c, c2]); - c.enable(); - expect(g.enabled).toBe(true); - }); + a.markAsDirty(); + expect(a.dirty).toBe(true); + expect(c.dirty).toBe(false); - it('should update the parent group value when child control status changes', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const g = new FormGroup({'one': c, 'two': c2}); - expect(g.value).toEqual({'one': 'one', 'two': 'two'}); + c.disable(); + expect(a.dirty).toBe(true); - c.disable(); - expect(g.value).toEqual({'two': 'two'}); + c.enable(); + expect(a.dirty).toBe(true); + }); - c2.disable(); - expect(g.value).toEqual({'one': 'one', 'two': 'two'}); + it('should ignore disabled controls in validation', () => { + const c = new FormControl(null, Validators.required); + const c2 = new FormControl(null); + const g = new FormGroup({one: c, two: c2}); + expect(g.valid).toBe(false); - c.enable(); - expect(g.value).toEqual({'one': 'one'}); - }); + c.disable(); + expect(g.valid).toBe(true); - it('should mark the parent array disabled if all controls are disabled', () => { - const c = new FormControl(); - const c2 = new FormControl(); - const a = new FormArray([c, c2]); - expect(a.enabled).toBe(true); + c.enable(); + expect(g.valid).toBe(false); + }); - c.disable(); - expect(a.enabled).toBe(true); + it('should ignore disabled controls when serializing value in a group', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const g = new FormGroup({one: c, two: c2}); + expect(g.value).toEqual({one: 'one', two: 'two'}); - c2.disable(); - expect(a.enabled).toBe(false); + c.disable(); + expect(g.value).toEqual({two: 'two'}); - c.enable(); - expect(a.enabled).toBe(true); - }); + c.enable(); + expect(g.value).toEqual({one: 'one', two: 'two'}); + }); - it('should update the parent array value when child control status changes', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const a = new FormArray([c, c2]); - expect(a.value).toEqual(['one', 'two']); + it('should ignore disabled controls when serializing value in an array', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const a = new FormArray([c, c2]); + expect(a.value).toEqual(['one', 'two']); - c.disable(); - expect(a.value).toEqual(['two']); + c.disable(); + expect(a.value).toEqual(['two']); - c2.disable(); - expect(a.value).toEqual(['one', 'two']); + c.enable(); + expect(a.value).toEqual(['one', 'two']); + }); - c.enable(); - expect(a.value).toEqual(['one']); - }); + it('should ignore disabled controls when determining dirtiness', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const g = new FormGroup({one: c, two: c2}); + c.markAsDirty(); + expect(g.dirty).toBe(true); - it('should ignore disabled array controls when determining dirtiness', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const a = new FormArray([c, c2]); - c.markAsDirty(); - expect(a.dirty).toBe(true); + c.disable(); + expect(c.dirty).toBe(true); + expect(g.dirty).toBe(false); - c.disable(); - expect(c.dirty).toBe(true); - expect(a.dirty).toBe(false); + c.enable(); + expect(g.dirty).toBe(true); + }); - c.enable(); - expect(a.dirty).toBe(true); - }); + it('should not make a dirty group not dirty when disabling controls', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const g = new FormGroup({one: c, two: c2}); - it('should not make a dirty array not dirty when disabling controls', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const a = new FormArray([c, c2]); + g.markAsDirty(); + expect(g.dirty).toBe(true); + expect(c.dirty).toBe(false); - a.markAsDirty(); - expect(a.dirty).toBe(true); - expect(c.dirty).toBe(false); + c.disable(); + expect(g.dirty).toBe(true); - c.disable(); - expect(a.dirty).toBe(true); + c.enable(); + expect(g.dirty).toBe(true); + }); - c.enable(); - expect(a.dirty).toBe(true); - }); + it('should ignore disabled controls when determining touched state', () => { + const c = new FormControl('one'); + const c2 = new FormControl('two'); + const g = new FormGroup({one: c, two: c2}); + c.markAsTouched(); + expect(g.touched).toBe(true); - it('should ignore disabled controls in validation', () => { - const c = new FormControl(null, Validators.required); - const c2 = new FormControl(null); - const g = new FormGroup({one: c, two: c2}); - expect(g.valid).toBe(false); + c.disable(); + expect(c.touched).toBe(true); + expect(g.touched).toBe(false); - c.disable(); - expect(g.valid).toBe(true); + c.enable(); + expect(g.touched).toBe(true); + }); - c.enable(); - expect(g.valid).toBe(false); - }); + it('should not run validators on disabled controls', () => { + const validator = jasmine.createSpy('validator'); + const c = new FormControl('', validator); + expect(validator.calls.count()).toEqual(1); - it('should ignore disabled controls when serializing value in a group', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const g = new FormGroup({one: c, two: c2}); - expect(g.value).toEqual({one: 'one', two: 'two'}); + c.disable(); + expect(validator.calls.count()).toEqual(1); - c.disable(); - expect(g.value).toEqual({two: 'two'}); + c.setValue('value'); + expect(validator.calls.count()).toEqual(1); - c.enable(); - expect(g.value).toEqual({one: 'one', two: 'two'}); - }); + c.enable(); + expect(validator.calls.count()).toEqual(2); + }); - it('should ignore disabled controls when serializing value in an array', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const a = new FormArray([c, c2]); - expect(a.value).toEqual(['one', 'two']); + describe('disabled errors', () => { + it('should clear out the errors when disabled', () => { + const c = new FormControl('', Validators.required); + expect(c.errors).toEqual({required: true}); c.disable(); - expect(a.value).toEqual(['two']); + expect(c.errors).toEqual(null); c.enable(); - expect(a.value).toEqual(['one', 'two']); + expect(c.errors).toEqual({required: true}); }); - it('should ignore disabled controls when determining dirtiness', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const g = new FormGroup({one: c, two: c2}); - c.markAsDirty(); - expect(g.dirty).toBe(true); - - c.disable(); - expect(c.dirty).toBe(true); - expect(g.dirty).toBe(false); - - c.enable(); - expect(g.dirty).toBe(true); - }); + it('should clear out async errors when disabled', fakeAsync(() => { + const c = new FormControl('', null!, asyncValidator('expected')); + tick(); + expect(c.errors).toEqual({'async': true}); - it('should not make a dirty group not dirty when disabling controls', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const g = new FormGroup({one: c, two: c2}); + c.disable(); + expect(c.errors).toEqual(null); - g.markAsDirty(); - expect(g.dirty).toBe(true); - expect(c.dirty).toBe(false); + c.enable(); + tick(); + expect(c.errors).toEqual({'async': true}); + })); + }); - c.disable(); - expect(g.dirty).toBe(true); + describe('disabled events', () => { + let logger: string[]; + let c: FormControl; + let g: FormGroup; - c.enable(); - expect(g.dirty).toBe(true); + beforeEach(() => { + logger = []; + c = new FormControl('', Validators.required); + g = new FormGroup({one: c}); }); - it('should ignore disabled controls when determining touched state', () => { - const c = new FormControl('one'); - const c2 = new FormControl('two'); - const g = new FormGroup({one: c, two: c2}); - c.markAsTouched(); - expect(g.touched).toBe(true); + it('should emit a statusChange event when disabled status changes', () => { + c.statusChanges.subscribe((status: string) => logger.push(status)); c.disable(); - expect(c.touched).toBe(true); - expect(g.touched).toBe(false); + expect(logger).toEqual(['DISABLED']); c.enable(); - expect(g.touched).toBe(true); + expect(logger).toEqual(['DISABLED', 'INVALID']); }); - it('should not run validators on disabled controls', () => { - const validator = jasmine.createSpy('validator'); - const c = new FormControl('', validator); - expect(validator.calls.count()).toEqual(1); + it('should emit status change events in correct order', () => { + c.statusChanges.subscribe(() => logger.push('control')); + g.statusChanges.subscribe(() => logger.push('group')); c.disable(); - expect(validator.calls.count()).toEqual(1); - - c.setValue('value'); - expect(validator.calls.count()).toEqual(1); - - c.enable(); - expect(validator.calls.count()).toEqual(2); + expect(logger).toEqual(['control', 'group']); }); - describe('disabled errors', () => { - it('should clear out the errors when disabled', () => { - const c = new FormControl('', Validators.required); - expect(c.errors).toEqual({required: true}); - - c.disable(); - expect(c.errors).toEqual(null); - - c.enable(); - expect(c.errors).toEqual({required: true}); - }); - - it('should clear out async errors when disabled', fakeAsync(() => { - const c = new FormControl('', null !, asyncValidator('expected')); - tick(); - expect(c.errors).toEqual({'async': true}); - - c.disable(); - expect(c.errors).toEqual(null); - - c.enable(); - tick(); - expect(c.errors).toEqual({'async': true}); - })); + it('should throw when sync validator passed into async validator param', () => { + const fn = () => new FormControl('', syncValidator, syncValidator); + // test for the specific error since without the error check it would still throw an error + // but + // not a meaningful one + expect(fn).toThrowError(`Expected validator to return Promise or Observable.`); }); - describe('disabled events', () => { - let logger: string[]; - let c: FormControl; - let g: FormGroup; - - beforeEach(() => { - logger = []; - c = new FormControl('', Validators.required); - g = new FormGroup({one: c}); - }); - - it('should emit a statusChange event when disabled status changes', () => { - c.statusChanges.subscribe((status: string) => logger.push(status)); - - c.disable(); - expect(logger).toEqual(['DISABLED']); - - c.enable(); - expect(logger).toEqual(['DISABLED', 'INVALID']); - - }); - - it('should emit status change events in correct order', () => { - c.statusChanges.subscribe(() => logger.push('control')); - g.statusChanges.subscribe(() => logger.push('group')); + it('should not emit value change events when emitEvent = false', () => { + c.valueChanges.subscribe(() => logger.push('control')); + g.valueChanges.subscribe(() => logger.push('group')); - c.disable(); - expect(logger).toEqual(['control', 'group']); - }); - - it('should throw when sync validator passed into async validator param', () => { - const fn = () => new FormControl('', syncValidator, syncValidator); - // test for the specific error since without the error check it would still throw an error - // but - // not a meaningful one - expect(fn).toThrowError(`Expected validator to return Promise or Observable.`); - }); + c.disable({emitEvent: false}); + expect(logger).toEqual([]); + c.enable({emitEvent: false}); + expect(logger).toEqual([]); + }); - it('should not emit value change events when emitEvent = false', () => { - c.valueChanges.subscribe(() => logger.push('control')); - g.valueChanges.subscribe(() => logger.push('group')); + it('should not emit status change events when emitEvent = false', () => { + c.statusChanges.subscribe(() => logger.push('control')); + g.statusChanges.subscribe(() => logger.push('form')); - c.disable({emitEvent: false}); - expect(logger).toEqual([]); - c.enable({emitEvent: false}); - expect(logger).toEqual([]); - }); + c.disable({emitEvent: false}); + expect(logger).toEqual([]); + c.enable({emitEvent: false}); + expect(logger).toEqual([]); + }); + }); + }); + describe('pending', () => { + let c: FormControl; - it('should not emit status change events when emitEvent = false', () => { - c.statusChanges.subscribe(() => logger.push('control')); - g.statusChanges.subscribe(() => logger.push('form')); + beforeEach(() => { + c = new FormControl('value'); + }); - c.disable({emitEvent: false}); - expect(logger).toEqual([]); - c.enable({emitEvent: false}); - expect(logger).toEqual([]); - }); + it('should be false after creating a control', () => { + expect(c.pending).toEqual(false); + }); - }); + it('should be true after changing the value of the control', () => { + c.markAsPending(); + expect(c.pending).toEqual(true); }); - describe('pending', () => { - let c: FormControl; - beforeEach(() => { c = new FormControl('value'); }); + describe('status change events', () => { + let logger: string[]; - it('should be false after creating a control', () => { expect(c.pending).toEqual(false); }); + beforeEach(() => { + logger = []; + c.statusChanges.subscribe((status) => logger.push(status)); + }); - it('should be true after changing the value of the control', () => { + it('should emit event after marking control as pending', () => { c.markAsPending(); - expect(c.pending).toEqual(true); + expect(logger).toEqual(['PENDING']); }); - describe('status change events', () => { - let logger: string[]; - - beforeEach(() => { - logger = []; - c.statusChanges.subscribe((status) => logger.push(status)); - }); - - it('should emit event after marking control as pending', () => { - c.markAsPending(); - expect(logger).toEqual(['PENDING']); - }); - - it('should not emit event when emitEvent = false', () => { - c.markAsPending({emitEvent: false}); - expect(logger).toEqual([]); - }); - + it('should not emit event when emitEvent = false', () => { + c.markAsPending({emitEvent: false}); + expect(logger).toEqual([]); }); }); }); +}); })(); diff --git a/packages/forms/test/form_group_spec.ts b/packages/forms/test/form_group_spec.ts index e72e5cfb481e1..ec90568a2eb0c 100644 --- a/packages/forms/test/form_group_spec.ts +++ b/packages/forms/test/form_group_spec.ts @@ -10,1346 +10,1368 @@ import {EventEmitter} from '@angular/core'; import {async, fakeAsync, tick} from '@angular/core/testing'; import {AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; import {AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, Validators} from '@angular/forms'; -import {of } from 'rxjs'; +import {of} from 'rxjs'; (function() { - function simpleValidator(c: AbstractControl): ValidationErrors|null { - return c.get('one') !.value === 'correct' ? null : {'broken': true}; - } - - function asyncValidator(expected: string, timeouts = {}) { - return (c: AbstractControl) => { - let resolve: (result: any) => void = undefined !; - const promise = new Promise<ValidationErrors|null>(res => { resolve = res; }); - const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; - const res = c.value != expected ? {'async': true} : null; - - if (t == 0) { +function simpleValidator(c: AbstractControl): ValidationErrors|null { + return c.get('one')!.value === 'correct' ? null : {'broken': true}; +} + +function asyncValidator(expected: string, timeouts = {}) { + return (c: AbstractControl) => { + let resolve: (result: any) => void = undefined!; + const promise = new Promise<ValidationErrors|null>(res => { + resolve = res; + }); + const t = (timeouts as any)[c.value] != null ? (timeouts as any)[c.value] : 0; + const res = c.value != expected ? {'async': true} : null; + + if (t == 0) { + resolve(res); + } else { + setTimeout(() => { resolve(res); - } else { - setTimeout(() => { resolve(res); }, t); - } - - return promise; - }; - } - - function asyncValidatorReturningObservable(c: AbstractControl) { - const e = new EventEmitter(); - Promise.resolve(null).then(() => { e.emit({'async': true}); }); - return e; - } - - function otherObservableValidator() { return of ({'other': true}); } - - describe('FormGroup', () => { - describe('value', () => { - it('should be the reduced value of the child controls', () => { - const g = new FormGroup({'one': new FormControl('111'), 'two': new FormControl('222')}); - expect(g.value).toEqual({'one': '111', 'two': '222'}); - }); + }, t); + } - it('should be empty when there are no child controls', () => { - const g = new FormGroup({}); - expect(g.value).toEqual({}); - }); + return promise; + }; +} - it('should support nested groups', () => { - const g = new FormGroup({ - 'one': new FormControl('111'), - 'nested': new FormGroup({'two': new FormControl('222')}) - }); - expect(g.value).toEqual({'one': '111', 'nested': {'two': '222'}}); +function asyncValidatorReturningObservable(c: AbstractControl) { + const e = new EventEmitter(); + Promise.resolve(null).then(() => { + e.emit({'async': true}); + }); + return e; +} + +function otherObservableValidator() { + return of({'other': true}); +} + +describe('FormGroup', () => { + describe('value', () => { + it('should be the reduced value of the child controls', () => { + const g = new FormGroup({'one': new FormControl('111'), 'two': new FormControl('222')}); + expect(g.value).toEqual({'one': '111', 'two': '222'}); + }); - (<FormControl>(g.get('nested.two'))).setValue('333'); + it('should be empty when there are no child controls', () => { + const g = new FormGroup({}); + expect(g.value).toEqual({}); + }); - expect(g.value).toEqual({'one': '111', 'nested': {'two': '333'}}); + it('should support nested groups', () => { + const g = new FormGroup({ + 'one': new FormControl('111'), + 'nested': new FormGroup({'two': new FormControl('222')}) }); - }); + expect(g.value).toEqual({'one': '111', 'nested': {'two': '222'}}); + + (<FormControl>(g.get('nested.two'))).setValue('333'); - describe('getRawValue', () => { - let fg: FormGroup; + expect(g.value).toEqual({'one': '111', 'nested': {'two': '333'}}); + }); + }); - it('should work with nested form groups/arrays', () => { - fg = new FormGroup({ - 'c1': new FormControl('v1'), - 'group': new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}), - 'array': new FormArray([new FormControl('v4'), new FormControl('v5')]) - }); - fg.get('group') !.get('c3') !.disable(); - (fg.get('array') as FormArray).at(1).disable(); + describe('getRawValue', () => { + let fg: FormGroup; - expect(fg.getRawValue()) - .toEqual({'c1': 'v1', 'group': {'c2': 'v2', 'c3': 'v3'}, 'array': ['v4', 'v5']}); + it('should work with nested form groups/arrays', () => { + fg = new FormGroup({ + 'c1': new FormControl('v1'), + 'group': new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}), + 'array': new FormArray([new FormControl('v4'), new FormControl('v5')]) }); + fg.get('group')!.get('c3')!.disable(); + (fg.get('array') as FormArray).at(1).disable(); + expect(fg.getRawValue()) + .toEqual({'c1': 'v1', 'group': {'c2': 'v2', 'c3': 'v3'}, 'array': ['v4', 'v5']}); }); + }); - describe('markAllAsTouched', () => { - it('should mark all descendants as touched', () => { - const formGroup: FormGroup = new FormGroup({ - 'c1': new FormControl('v1'), - 'group': new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}), - 'array': new FormArray([ - new FormControl('v4'), new FormControl('v5'), - new FormGroup({'c4': new FormControl('v4')}) - ]) - }); + describe('markAllAsTouched', () => { + it('should mark all descendants as touched', () => { + const formGroup: FormGroup = new FormGroup({ + 'c1': new FormControl('v1'), + 'group': new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}), + 'array': new FormArray([ + new FormControl('v4'), new FormControl('v5'), new FormGroup({'c4': new FormControl('v4')}) + ]) + }); - expect(formGroup.touched).toBe(false); + expect(formGroup.touched).toBe(false); - const control1 = formGroup.get('c1') as FormControl; + const control1 = formGroup.get('c1') as FormControl; - expect(control1.touched).toBe(false); + expect(control1.touched).toBe(false); - const innerGroup = formGroup.get('group') as FormGroup; + const innerGroup = formGroup.get('group') as FormGroup; - expect(innerGroup.touched).toBe(false); + expect(innerGroup.touched).toBe(false); - const innerGroupFirstChildCtrl = innerGroup.get('c2') as FormControl; + const innerGroupFirstChildCtrl = innerGroup.get('c2') as FormControl; - expect(innerGroupFirstChildCtrl.touched).toBe(false); + expect(innerGroupFirstChildCtrl.touched).toBe(false); - formGroup.markAllAsTouched(); + formGroup.markAllAsTouched(); - expect(formGroup.touched).toBe(true); + expect(formGroup.touched).toBe(true); - expect(control1.touched).toBe(true); + expect(control1.touched).toBe(true); - expect(innerGroup.touched).toBe(true); + expect(innerGroup.touched).toBe(true); - expect(innerGroupFirstChildCtrl.touched).toBe(true); + expect(innerGroupFirstChildCtrl.touched).toBe(true); - const innerGroupSecondChildCtrl = innerGroup.get('c3') as FormControl; + const innerGroupSecondChildCtrl = innerGroup.get('c3') as FormControl; - expect(innerGroupSecondChildCtrl.touched).toBe(true); + expect(innerGroupSecondChildCtrl.touched).toBe(true); - const array = formGroup.get('array') as FormArray; + const array = formGroup.get('array') as FormArray; - expect(array.touched).toBe(true); + expect(array.touched).toBe(true); - const arrayFirstChildCtrl = array.at(0) as FormControl; + const arrayFirstChildCtrl = array.at(0) as FormControl; - expect(arrayFirstChildCtrl.touched).toBe(true); + expect(arrayFirstChildCtrl.touched).toBe(true); - const arraySecondChildCtrl = array.at(1) as FormControl; + const arraySecondChildCtrl = array.at(1) as FormControl; - expect(arraySecondChildCtrl.touched).toBe(true); + expect(arraySecondChildCtrl.touched).toBe(true); - const arrayFirstChildGroup = array.at(2) as FormGroup; + const arrayFirstChildGroup = array.at(2) as FormGroup; - expect(arrayFirstChildGroup.touched).toBe(true); + expect(arrayFirstChildGroup.touched).toBe(true); - const arrayFirstChildGroupFirstChildCtrl = arrayFirstChildGroup.get('c4') as FormControl; + const arrayFirstChildGroupFirstChildCtrl = arrayFirstChildGroup.get('c4') as FormControl; - expect(arrayFirstChildGroupFirstChildCtrl.touched).toBe(true); - }); + expect(arrayFirstChildGroupFirstChildCtrl.touched).toBe(true); }); + }); - describe('adding and removing controls', () => { - it('should update value and validity when control is added', () => { - const g = new FormGroup({'one': new FormControl('1')}); - expect(g.value).toEqual({'one': '1'}); - expect(g.valid).toBe(true); + describe('adding and removing controls', () => { + it('should update value and validity when control is added', () => { + const g = new FormGroup({'one': new FormControl('1')}); + expect(g.value).toEqual({'one': '1'}); + expect(g.valid).toBe(true); - g.addControl('two', new FormControl('2', Validators.minLength(10))); + g.addControl('two', new FormControl('2', Validators.minLength(10))); - expect(g.value).toEqual({'one': '1', 'two': '2'}); - expect(g.valid).toBe(false); - }); + expect(g.value).toEqual({'one': '1', 'two': '2'}); + expect(g.valid).toBe(false); + }); - it('should update value and validity when control is removed', () => { - const g = new FormGroup( - {'one': new FormControl('1'), 'two': new FormControl('2', Validators.minLength(10))}); - expect(g.value).toEqual({'one': '1', 'two': '2'}); - expect(g.valid).toBe(false); + it('should update value and validity when control is removed', () => { + const g = new FormGroup( + {'one': new FormControl('1'), 'two': new FormControl('2', Validators.minLength(10))}); + expect(g.value).toEqual({'one': '1', 'two': '2'}); + expect(g.valid).toBe(false); - g.removeControl('two'); + g.removeControl('two'); - expect(g.value).toEqual({'one': '1'}); - expect(g.valid).toBe(true); - }); + expect(g.value).toEqual({'one': '1'}); + expect(g.valid).toBe(true); }); + }); - describe('dirty', () => { - let c: FormControl, g: FormGroup; + describe('dirty', () => { + let c: FormControl, g: FormGroup; - beforeEach(() => { - c = new FormControl('value'); - g = new FormGroup({'one': c}); - }); + beforeEach(() => { + c = new FormControl('value'); + g = new FormGroup({'one': c}); + }); - it('should be false after creating a control', () => { expect(g.dirty).toEqual(false); }); + it('should be false after creating a control', () => { + expect(g.dirty).toEqual(false); + }); - it('should be true after changing the value of the control', () => { - c.markAsDirty(); + it('should be true after changing the value of the control', () => { + c.markAsDirty(); - expect(g.dirty).toEqual(true); - }); + expect(g.dirty).toEqual(true); }); + }); - describe('touched', () => { - let c: FormControl, g: FormGroup; + describe('touched', () => { + let c: FormControl, g: FormGroup; - beforeEach(() => { - c = new FormControl('value'); - g = new FormGroup({'one': c}); - }); + beforeEach(() => { + c = new FormControl('value'); + g = new FormGroup({'one': c}); + }); - it('should be false after creating a control', () => { expect(g.touched).toEqual(false); }); + it('should be false after creating a control', () => { + expect(g.touched).toEqual(false); + }); - it('should be true after control is marked as touched', () => { - c.markAsTouched(); + it('should be true after control is marked as touched', () => { + c.markAsTouched(); - expect(g.touched).toEqual(true); - }); + expect(g.touched).toEqual(true); + }); + }); + + describe('setValue', () => { + let c: FormControl, c2: FormControl, g: FormGroup; + + beforeEach(() => { + c = new FormControl(''); + c2 = new FormControl(''); + g = new FormGroup({'one': c, 'two': c2}); + }); + + it('should set its own value', () => { + g.setValue({'one': 'one', 'two': 'two'}); + expect(g.value).toEqual({'one': 'one', 'two': 'two'}); }); - describe('setValue', () => { - let c: FormControl, c2: FormControl, g: FormGroup; + it('should set child values', () => { + g.setValue({'one': 'one', 'two': 'two'}); + expect(c.value).toEqual('one'); + expect(c2.value).toEqual('two'); + }); + + it('should set child control values if disabled', () => { + c2.disable(); + g.setValue({'one': 'one', 'two': 'two'}); + expect(c2.value).toEqual('two'); + expect(g.value).toEqual({'one': 'one'}); + expect(g.getRawValue()).toEqual({'one': 'one', 'two': 'two'}); + }); + + it('should set group value if group is disabled', () => { + g.disable(); + g.setValue({'one': 'one', 'two': 'two'}); + expect(c.value).toEqual('one'); + expect(c2.value).toEqual('two'); + + expect(g.value).toEqual({'one': 'one', 'two': 'two'}); + }); + + it('should set parent values', () => { + const form = new FormGroup({'parent': g}); + g.setValue({'one': 'one', 'two': 'two'}); + expect(form.value).toEqual({'parent': {'one': 'one', 'two': 'two'}}); + }); + + it('should not update the parent when explicitly specified', () => { + const form = new FormGroup({'parent': g}); + g.setValue({'one': 'one', 'two': 'two'}, {onlySelf: true}); + + expect(form.value).toEqual({parent: {'one': '', 'two': ''}}); + }); + + it('should throw if fields are missing from supplied value (subset)', () => { + expect(() => g.setValue({ + 'one': 'one' + })).toThrowError(new RegExp(`Must supply a value for form control with name: 'two'`)); + }); + + it('should throw if a value is provided for a missing control (superset)', () => { + expect(() => g.setValue({'one': 'one', 'two': 'two', 'three': 'three'})) + .toThrowError(new RegExp(`Cannot find form control with name: three`)); + }); + + it('should throw if a value is not provided for a disabled control', () => { + c2.disable(); + expect(() => g.setValue({ + 'one': 'one' + })).toThrowError(new RegExp(`Must supply a value for form control with name: 'two'`)); + }); + + it('should throw if no controls are set yet', () => { + const empty = new FormGroup({}); + expect(() => empty.setValue({ + 'one': 'one' + })).toThrowError(new RegExp(`no form controls registered with this group`)); + }); + + describe('setValue() events', () => { + let form: FormGroup; + let logger: any[]; beforeEach(() => { - c = new FormControl(''); - c2 = new FormControl(''); - g = new FormGroup({'one': c, 'two': c2}); + form = new FormGroup({'parent': g}); + logger = []; }); - it('should set its own value', () => { - g.setValue({'one': 'one', 'two': 'two'}); - expect(g.value).toEqual({'one': 'one', 'two': 'two'}); - }); + it('should emit one valueChange event per control', () => { + form.valueChanges.subscribe(() => logger.push('form')); + g.valueChanges.subscribe(() => logger.push('group')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); - it('should set child values', () => { g.setValue({'one': 'one', 'two': 'two'}); - expect(c.value).toEqual('one'); - expect(c2.value).toEqual('two'); + expect(logger).toEqual(['control1', 'control2', 'group', 'form']); }); - it('should set child control values if disabled', () => { - c2.disable(); - g.setValue({'one': 'one', 'two': 'two'}); - expect(c2.value).toEqual('two'); - expect(g.value).toEqual({'one': 'one'}); - expect(g.getRawValue()).toEqual({'one': 'one', 'two': 'two'}); - }); + it('should not fire an event when explicitly specified', fakeAsync(() => { + form.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + g.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); - it('should set group value if group is disabled', () => { - g.disable(); - g.setValue({'one': 'one', 'two': 'two'}); - expect(c.value).toEqual('one'); - expect(c2.value).toEqual('two'); + g.setValue({'one': 'one', 'two': 'two'}, {emitEvent: false}); + tick(); + })); - expect(g.value).toEqual({'one': 'one', 'two': 'two'}); - }); + it('should emit one statusChange event per control', () => { + form.statusChanges.subscribe(() => logger.push('form')); + g.statusChanges.subscribe(() => logger.push('group')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); - it('should set parent values', () => { - const form = new FormGroup({'parent': g}); g.setValue({'one': 'one', 'two': 'two'}); - expect(form.value).toEqual({'parent': {'one': 'one', 'two': 'two'}}); + expect(logger).toEqual(['control1', 'control2', 'group', 'form']); }); + }); + }); - it('should not update the parent when explicitly specified', () => { - const form = new FormGroup({'parent': g}); - g.setValue({'one': 'one', 'two': 'two'}, {onlySelf: true}); + describe('patchValue', () => { + let c: FormControl, c2: FormControl, g: FormGroup; - expect(form.value).toEqual({parent: {'one': '', 'two': ''}}); - }); + beforeEach(() => { + c = new FormControl(''); + c2 = new FormControl(''); + g = new FormGroup({'one': c, 'two': c2}); + }); - it('should throw if fields are missing from supplied value (subset)', () => { - expect(() => g.setValue({ - 'one': 'one' - })).toThrowError(new RegExp(`Must supply a value for form control with name: 'two'`)); - }); + it('should set its own value', () => { + g.patchValue({'one': 'one', 'two': 'two'}); + expect(g.value).toEqual({'one': 'one', 'two': 'two'}); + }); - it('should throw if a value is provided for a missing control (superset)', () => { - expect(() => g.setValue({'one': 'one', 'two': 'two', 'three': 'three'})) - .toThrowError(new RegExp(`Cannot find form control with name: three`)); - }); + it('should set child values', () => { + g.patchValue({'one': 'one', 'two': 'two'}); + expect(c.value).toEqual('one'); + expect(c2.value).toEqual('two'); + }); - it('should throw if a value is not provided for a disabled control', () => { - c2.disable(); - expect(() => g.setValue({ - 'one': 'one' - })).toThrowError(new RegExp(`Must supply a value for form control with name: 'two'`)); - }); + it('should patch disabled control values', () => { + c2.disable(); + g.patchValue({'one': 'one', 'two': 'two'}); + expect(c2.value).toEqual('two'); + expect(g.value).toEqual({'one': 'one'}); + expect(g.getRawValue()).toEqual({'one': 'one', 'two': 'two'}); + }); - it('should throw if no controls are set yet', () => { - const empty = new FormGroup({}); - expect(() => empty.setValue({ - 'one': 'one' - })).toThrowError(new RegExp(`no form controls registered with this group`)); - }); + it('should patch disabled control groups', () => { + g.disable(); + g.patchValue({'one': 'one', 'two': 'two'}); + expect(c.value).toEqual('one'); + expect(c2.value).toEqual('two'); + expect(g.value).toEqual({'one': 'one', 'two': 'two'}); + }); - describe('setValue() events', () => { - let form: FormGroup; - let logger: any[]; - - beforeEach(() => { - form = new FormGroup({'parent': g}); - logger = []; - }); - - it('should emit one valueChange event per control', () => { - form.valueChanges.subscribe(() => logger.push('form')); - g.valueChanges.subscribe(() => logger.push('group')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); - - g.setValue({'one': 'one', 'two': 'two'}); - expect(logger).toEqual(['control1', 'control2', 'group', 'form']); - }); - - it('should not fire an event when explicitly specified', fakeAsync(() => { - form.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - g.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - - g.setValue({'one': 'one', 'two': 'two'}, {emitEvent: false}); - tick(); - })); - - it('should emit one statusChange event per control', () => { - form.statusChanges.subscribe(() => logger.push('form')); - g.statusChanges.subscribe(() => logger.push('group')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); - - g.setValue({'one': 'one', 'two': 'two'}); - expect(logger).toEqual(['control1', 'control2', 'group', 'form']); - }); - }); + it('should set parent values', () => { + const form = new FormGroup({'parent': g}); + g.patchValue({'one': 'one', 'two': 'two'}); + expect(form.value).toEqual({'parent': {'one': 'one', 'two': 'two'}}); + }); + + it('should not update the parent when explicitly specified', () => { + const form = new FormGroup({'parent': g}); + g.patchValue({'one': 'one', 'two': 'two'}, {onlySelf: true}); + + expect(form.value).toEqual({parent: {'one': '', 'two': ''}}); + }); + + it('should ignore fields that are missing from supplied value (subset)', () => { + g.patchValue({'one': 'one'}); + expect(g.value).toEqual({'one': 'one', 'two': ''}); + }); + + it('should not ignore fields that are null', () => { + g.patchValue({'one': null}); + expect(g.value).toEqual({'one': null, 'two': ''}); }); - describe('patchValue', () => { - let c: FormControl, c2: FormControl, g: FormGroup; + it('should ignore any value provided for a missing control (superset)', () => { + g.patchValue({'three': 'three'}); + expect(g.value).toEqual({'one': '', 'two': ''}); + }); + + describe('patchValue() events', () => { + let form: FormGroup; + let logger: any[]; beforeEach(() => { - c = new FormControl(''); - c2 = new FormControl(''); - g = new FormGroup({'one': c, 'two': c2}); + form = new FormGroup({'parent': g}); + logger = []; }); - it('should set its own value', () => { - g.patchValue({'one': 'one', 'two': 'two'}); - expect(g.value).toEqual({'one': 'one', 'two': 'two'}); - }); + it('should emit one valueChange event per control', () => { + form.valueChanges.subscribe(() => logger.push('form')); + g.valueChanges.subscribe(() => logger.push('group')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); - it('should set child values', () => { g.patchValue({'one': 'one', 'two': 'two'}); - expect(c.value).toEqual('one'); - expect(c2.value).toEqual('two'); + expect(logger).toEqual(['control1', 'control2', 'group', 'form']); }); - it('should patch disabled control values', () => { - c2.disable(); - g.patchValue({'one': 'one', 'two': 'two'}); - expect(c2.value).toEqual('two'); - expect(g.value).toEqual({'one': 'one'}); - expect(g.getRawValue()).toEqual({'one': 'one', 'two': 'two'}); - }); + it('should not emit valueChange events for skipped controls', () => { + form.valueChanges.subscribe(() => logger.push('form')); + g.valueChanges.subscribe(() => logger.push('group')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); - it('should patch disabled control groups', () => { - g.disable(); - g.patchValue({'one': 'one', 'two': 'two'}); - expect(c.value).toEqual('one'); - expect(c2.value).toEqual('two'); - expect(g.value).toEqual({'one': 'one', 'two': 'two'}); + g.patchValue({'one': 'one'}); + expect(logger).toEqual(['control1', 'group', 'form']); }); - it('should set parent values', () => { - const form = new FormGroup({'parent': g}); - g.patchValue({'one': 'one', 'two': 'two'}); - expect(form.value).toEqual({'parent': {'one': 'one', 'two': 'two'}}); - }); + it('should not fire an event when explicitly specified', fakeAsync(() => { + form.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + g.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); - it('should not update the parent when explicitly specified', () => { - const form = new FormGroup({'parent': g}); - g.patchValue({'one': 'one', 'two': 'two'}, {onlySelf: true}); + g.patchValue({'one': 'one', 'two': 'two'}, {emitEvent: false}); + tick(); + })); - expect(form.value).toEqual({parent: {'one': '', 'two': ''}}); - }); + it('should emit one statusChange event per control', () => { + form.statusChanges.subscribe(() => logger.push('form')); + g.statusChanges.subscribe(() => logger.push('group')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); - it('should ignore fields that are missing from supplied value (subset)', () => { - g.patchValue({'one': 'one'}); - expect(g.value).toEqual({'one': 'one', 'two': ''}); + g.patchValue({'one': 'one', 'two': 'two'}); + expect(logger).toEqual(['control1', 'control2', 'group', 'form']); }); + }); + }); - it('should not ignore fields that are null', () => { - g.patchValue({'one': null}); - expect(g.value).toEqual({'one': null, 'two': ''}); - }); + describe('reset()', () => { + let c: FormControl, c2: FormControl, g: FormGroup; - it('should ignore any value provided for a missing control (superset)', () => { - g.patchValue({'three': 'three'}); - expect(g.value).toEqual({'one': '', 'two': ''}); - }); + beforeEach(() => { + c = new FormControl('initial value'); + c2 = new FormControl(''); + g = new FormGroup({'one': c, 'two': c2}); + }); - describe('patchValue() events', () => { - let form: FormGroup; - let logger: any[]; - - beforeEach(() => { - form = new FormGroup({'parent': g}); - logger = []; - }); - - it('should emit one valueChange event per control', () => { - form.valueChanges.subscribe(() => logger.push('form')); - g.valueChanges.subscribe(() => logger.push('group')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); - - g.patchValue({'one': 'one', 'two': 'two'}); - expect(logger).toEqual(['control1', 'control2', 'group', 'form']); - }); - - it('should not emit valueChange events for skipped controls', () => { - form.valueChanges.subscribe(() => logger.push('form')); - g.valueChanges.subscribe(() => logger.push('group')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); - - g.patchValue({'one': 'one'}); - expect(logger).toEqual(['control1', 'group', 'form']); - }); - - it('should not fire an event when explicitly specified', fakeAsync(() => { - form.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - g.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - - g.patchValue({'one': 'one', 'two': 'two'}, {emitEvent: false}); - tick(); - })); - - it('should emit one statusChange event per control', () => { - form.statusChanges.subscribe(() => logger.push('form')); - g.statusChanges.subscribe(() => logger.push('group')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); - - g.patchValue({'one': 'one', 'two': 'two'}); - expect(logger).toEqual(['control1', 'control2', 'group', 'form']); - }); - }); + it('should set its own value if value passed', () => { + g.setValue({'one': 'new value', 'two': 'new value'}); + + g.reset({'one': 'initial value', 'two': ''}); + expect(g.value).toEqual({'one': 'initial value', 'two': ''}); }); - describe('reset()', () => { - let c: FormControl, c2: FormControl, g: FormGroup; + it('should set its own value if boxed value passed', () => { + g.setValue({'one': 'new value', 'two': 'new value'}); - beforeEach(() => { - c = new FormControl('initial value'); - c2 = new FormControl(''); - g = new FormGroup({'one': c, 'two': c2}); - }); + g.reset({'one': {value: 'initial value', disabled: false}, 'two': ''}); + expect(g.value).toEqual({'one': 'initial value', 'two': ''}); + }); - it('should set its own value if value passed', () => { - g.setValue({'one': 'new value', 'two': 'new value'}); + it('should clear its own value if no value passed', () => { + g.setValue({'one': 'new value', 'two': 'new value'}); - g.reset({'one': 'initial value', 'two': ''}); - expect(g.value).toEqual({'one': 'initial value', 'two': ''}); - }); + g.reset(); + expect(g.value).toEqual({'one': null, 'two': null}); + }); - it('should set its own value if boxed value passed', () => { - g.setValue({'one': 'new value', 'two': 'new value'}); + it('should set the value of each of its child controls if value passed', () => { + g.setValue({'one': 'new value', 'two': 'new value'}); - g.reset({'one': {value: 'initial value', disabled: false}, 'two': ''}); - expect(g.value).toEqual({'one': 'initial value', 'two': ''}); - }); + g.reset({'one': 'initial value', 'two': ''}); + expect(c.value).toBe('initial value'); + expect(c2.value).toBe(''); + }); - it('should clear its own value if no value passed', () => { - g.setValue({'one': 'new value', 'two': 'new value'}); + it('should clear the value of each of its child controls if no value passed', () => { + g.setValue({'one': 'new value', 'two': 'new value'}); - g.reset(); - expect(g.value).toEqual({'one': null, 'two': null}); - }); + g.reset(); + expect(c.value).toBe(null); + expect(c2.value).toBe(null); + }); - it('should set the value of each of its child controls if value passed', () => { - g.setValue({'one': 'new value', 'two': 'new value'}); + it('should set the value of its parent if value passed', () => { + const form = new FormGroup({'g': g}); + g.setValue({'one': 'new value', 'two': 'new value'}); - g.reset({'one': 'initial value', 'two': ''}); - expect(c.value).toBe('initial value'); - expect(c2.value).toBe(''); - }); + g.reset({'one': 'initial value', 'two': ''}); + expect(form.value).toEqual({'g': {'one': 'initial value', 'two': ''}}); + }); - it('should clear the value of each of its child controls if no value passed', () => { - g.setValue({'one': 'new value', 'two': 'new value'}); + it('should clear the value of its parent if no value passed', () => { + const form = new FormGroup({'g': g}); + g.setValue({'one': 'new value', 'two': 'new value'}); - g.reset(); - expect(c.value).toBe(null); - expect(c2.value).toBe(null); - }); + g.reset(); + expect(form.value).toEqual({'g': {'one': null, 'two': null}}); + }); - it('should set the value of its parent if value passed', () => { - const form = new FormGroup({'g': g}); - g.setValue({'one': 'new value', 'two': 'new value'}); + it('should not update the parent when explicitly specified', () => { + const form = new FormGroup({'g': g}); + g.reset({'one': 'new value', 'two': 'new value'}, {onlySelf: true}); - g.reset({'one': 'initial value', 'two': ''}); - expect(form.value).toEqual({'g': {'one': 'initial value', 'two': ''}}); - }); + expect(form.value).toEqual({g: {'one': 'initial value', 'two': ''}}); + }); - it('should clear the value of its parent if no value passed', () => { - const form = new FormGroup({'g': g}); - g.setValue({'one': 'new value', 'two': 'new value'}); + it('should mark itself as pristine', () => { + g.markAsDirty(); + expect(g.pristine).toBe(false); - g.reset(); - expect(form.value).toEqual({'g': {'one': null, 'two': null}}); - }); + g.reset(); + expect(g.pristine).toBe(true); + }); - it('should not update the parent when explicitly specified', () => { - const form = new FormGroup({'g': g}); - g.reset({'one': 'new value', 'two': 'new value'}, {onlySelf: true}); + it('should mark all child controls as pristine', () => { + c.markAsDirty(); + c2.markAsDirty(); + expect(c.pristine).toBe(false); + expect(c2.pristine).toBe(false); - expect(form.value).toEqual({g: {'one': 'initial value', 'two': ''}}); - }); + g.reset(); + expect(c.pristine).toBe(true); + expect(c2.pristine).toBe(true); + }); - it('should mark itself as pristine', () => { - g.markAsDirty(); - expect(g.pristine).toBe(false); + it('should mark the parent as pristine if all siblings pristine', () => { + const c3 = new FormControl(''); + const form = new FormGroup({'g': g, 'c3': c3}); - g.reset(); - expect(g.pristine).toBe(true); - }); + g.markAsDirty(); + expect(form.pristine).toBe(false); - it('should mark all child controls as pristine', () => { - c.markAsDirty(); - c2.markAsDirty(); - expect(c.pristine).toBe(false); - expect(c2.pristine).toBe(false); + g.reset(); + expect(form.pristine).toBe(true); + }); - g.reset(); - expect(c.pristine).toBe(true); - expect(c2.pristine).toBe(true); - }); + it('should not mark the parent pristine if any dirty siblings', () => { + const c3 = new FormControl(''); + const form = new FormGroup({'g': g, 'c3': c3}); - it('should mark the parent as pristine if all siblings pristine', () => { - const c3 = new FormControl(''); - const form = new FormGroup({'g': g, 'c3': c3}); + g.markAsDirty(); + c3.markAsDirty(); + expect(form.pristine).toBe(false); - g.markAsDirty(); - expect(form.pristine).toBe(false); + g.reset(); + expect(form.pristine).toBe(false); + }); - g.reset(); - expect(form.pristine).toBe(true); - }); + it('should mark itself as untouched', () => { + g.markAsTouched(); + expect(g.untouched).toBe(false); - it('should not mark the parent pristine if any dirty siblings', () => { - const c3 = new FormControl(''); - const form = new FormGroup({'g': g, 'c3': c3}); + g.reset(); + expect(g.untouched).toBe(true); + }); - g.markAsDirty(); - c3.markAsDirty(); - expect(form.pristine).toBe(false); + it('should mark all child controls as untouched', () => { + c.markAsTouched(); + c2.markAsTouched(); + expect(c.untouched).toBe(false); + expect(c2.untouched).toBe(false); - g.reset(); - expect(form.pristine).toBe(false); - }); + g.reset(); + expect(c.untouched).toBe(true); + expect(c2.untouched).toBe(true); + }); - it('should mark itself as untouched', () => { - g.markAsTouched(); - expect(g.untouched).toBe(false); + it('should mark the parent untouched if all siblings untouched', () => { + const c3 = new FormControl(''); + const form = new FormGroup({'g': g, 'c3': c3}); - g.reset(); - expect(g.untouched).toBe(true); - }); + g.markAsTouched(); + expect(form.untouched).toBe(false); - it('should mark all child controls as untouched', () => { - c.markAsTouched(); - c2.markAsTouched(); - expect(c.untouched).toBe(false); - expect(c2.untouched).toBe(false); + g.reset(); + expect(form.untouched).toBe(true); + }); - g.reset(); - expect(c.untouched).toBe(true); - expect(c2.untouched).toBe(true); - }); + it('should not mark the parent untouched if any touched siblings', () => { + const c3 = new FormControl(''); + const form = new FormGroup({'g': g, 'c3': c3}); - it('should mark the parent untouched if all siblings untouched', () => { - const c3 = new FormControl(''); - const form = new FormGroup({'g': g, 'c3': c3}); + g.markAsTouched(); + c3.markAsTouched(); + expect(form.untouched).toBe(false); - g.markAsTouched(); - expect(form.untouched).toBe(false); + g.reset(); + expect(form.untouched).toBe(false); + }); - g.reset(); - expect(form.untouched).toBe(true); - }); + it('should retain previous disabled state', () => { + g.disable(); + g.reset(); - it('should not mark the parent untouched if any touched siblings', () => { - const c3 = new FormControl(''); - const form = new FormGroup({'g': g, 'c3': c3}); + expect(g.disabled).toBe(true); + }); - g.markAsTouched(); - c3.markAsTouched(); - expect(form.untouched).toBe(false); + it('should set child disabled state if boxed value passed', () => { + g.disable(); + g.reset({'one': {value: '', disabled: false}, 'two': ''}); - g.reset(); - expect(form.untouched).toBe(false); - }); + expect(c.disabled).toBe(false); + expect(g.disabled).toBe(false); + }); - it('should retain previous disabled state', () => { - g.disable(); - g.reset(); + describe('reset() events', () => { + let form: FormGroup, c3: FormControl, logger: any[]; - expect(g.disabled).toBe(true); + beforeEach(() => { + c3 = new FormControl(''); + form = new FormGroup({'g': g, 'c3': c3}); + logger = []; }); - it('should set child disabled state if boxed value passed', () => { - g.disable(); - g.reset({'one': {value: '', disabled: false}, 'two': ''}); + it('should emit one valueChange event per reset control', () => { + form.valueChanges.subscribe(() => logger.push('form')); + g.valueChanges.subscribe(() => logger.push('group')); + c.valueChanges.subscribe(() => logger.push('control1')); + c2.valueChanges.subscribe(() => logger.push('control2')); + c3.valueChanges.subscribe(() => logger.push('control3')); - expect(c.disabled).toBe(false); - expect(g.disabled).toBe(false); + g.reset(); + expect(logger).toEqual(['control1', 'control2', 'group', 'form']); }); - describe('reset() events', () => { - let form: FormGroup, c3: FormControl, logger: any[]; - - beforeEach(() => { - c3 = new FormControl(''); - form = new FormGroup({'g': g, 'c3': c3}); - logger = []; - }); - - it('should emit one valueChange event per reset control', () => { - form.valueChanges.subscribe(() => logger.push('form')); - g.valueChanges.subscribe(() => logger.push('group')); - c.valueChanges.subscribe(() => logger.push('control1')); - c2.valueChanges.subscribe(() => logger.push('control2')); - c3.valueChanges.subscribe(() => logger.push('control3')); - - g.reset(); - expect(logger).toEqual(['control1', 'control2', 'group', 'form']); - }); - - it('should not fire an event when explicitly specified', fakeAsync(() => { - form.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - g.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - c.valueChanges.subscribe((value) => { throw 'Should not happen'; }); - - g.reset({}, {emitEvent: false}); - tick(); - })); - - it('should emit one statusChange event per reset control', () => { - form.statusChanges.subscribe(() => logger.push('form')); - g.statusChanges.subscribe(() => logger.push('group')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); - c3.statusChanges.subscribe(() => logger.push('control3')); - - g.reset(); - expect(logger).toEqual(['control1', 'control2', 'group', 'form']); - }); - - it('should emit one statusChange event per reset control', () => { - form.statusChanges.subscribe(() => logger.push('form')); - g.statusChanges.subscribe(() => logger.push('group')); - c.statusChanges.subscribe(() => logger.push('control1')); - c2.statusChanges.subscribe(() => logger.push('control2')); - c3.statusChanges.subscribe(() => logger.push('control3')); - - g.reset({'one': {value: '', disabled: true}}); - expect(logger).toEqual(['control1', 'control2', 'group', 'form']); - }); - - it('should mark as pristine and not dirty before emitting valueChange and statusChange events when resetting', - () => { - const pristineAndNotDirty = () => { - expect(form.pristine).toBe(true); - expect(form.dirty).toBe(false); - }; - - c3.markAsDirty(); - expect(form.pristine).toBe(false); - expect(form.dirty).toBe(true); - - form.valueChanges.subscribe(pristineAndNotDirty); - form.statusChanges.subscribe(pristineAndNotDirty); - - form.reset(); + it('should not fire an event when explicitly specified', fakeAsync(() => { + form.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + g.valueChanges.subscribe((value) => { + throw 'Should not happen'; + }); + c.valueChanges.subscribe((value) => { + throw 'Should not happen'; }); - }); - }); + g.reset({}, {emitEvent: false}); + tick(); + })); - describe('contains', () => { - let group: FormGroup; + it('should emit one statusChange event per reset control', () => { + form.statusChanges.subscribe(() => logger.push('form')); + g.statusChanges.subscribe(() => logger.push('group')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); + c3.statusChanges.subscribe(() => logger.push('control3')); - beforeEach(() => { - group = new FormGroup({ - 'required': new FormControl('requiredValue'), - 'optional': new FormControl({value: 'disabled value', disabled: true}) - }); + g.reset(); + expect(logger).toEqual(['control1', 'control2', 'group', 'form']); }); - it('should return false when the component is disabled', - () => { expect(group.contains('optional')).toEqual(false); }); - - it('should return false when there is no component with the given name', - () => { expect(group.contains('something else')).toEqual(false); }); + it('should emit one statusChange event per reset control', () => { + form.statusChanges.subscribe(() => logger.push('form')); + g.statusChanges.subscribe(() => logger.push('group')); + c.statusChanges.subscribe(() => logger.push('control1')); + c2.statusChanges.subscribe(() => logger.push('control2')); + c3.statusChanges.subscribe(() => logger.push('control3')); - it('should return true when the component is enabled', () => { - expect(group.contains('required')).toEqual(true); + g.reset({'one': {value: '', disabled: true}}); + expect(logger).toEqual(['control1', 'control2', 'group', 'form']); + }); - group.enable(); + it('should mark as pristine and not dirty before emitting valueChange and statusChange events when resetting', + () => { + const pristineAndNotDirty = () => { + expect(form.pristine).toBe(true); + expect(form.dirty).toBe(false); + }; - expect(group.contains('optional')).toEqual(true); - }); + c3.markAsDirty(); + expect(form.pristine).toBe(false); + expect(form.dirty).toBe(true); - it('should support controls with dots in their name', () => { - expect(group.contains('some.name')).toBe(false); - group.addControl('some.name', new FormControl()); + form.valueChanges.subscribe(pristineAndNotDirty); + form.statusChanges.subscribe(pristineAndNotDirty); - expect(group.contains('some.name')).toBe(true); - }); + form.reset(); + }); }); + }); - describe('retrieve', () => { - let group: FormGroup; + describe('contains', () => { + let group: FormGroup; - beforeEach(() => { - group = new FormGroup({ - 'required': new FormControl('requiredValue'), - }); + beforeEach(() => { + group = new FormGroup({ + 'required': new FormControl('requiredValue'), + 'optional': new FormControl({value: 'disabled value', disabled: true}) }); + }); - it('should not get inherited properties', - () => { expect(group.get('constructor')).toBe(null); }); + it('should return false when the component is disabled', () => { + expect(group.contains('optional')).toEqual(false); }); - describe('statusChanges', () => { - let control: FormControl; - let group: FormGroup; + it('should return false when there is no component with the given name', () => { + expect(group.contains('something else')).toEqual(false); + }); - beforeEach(async(() => { - control = new FormControl('', asyncValidatorReturningObservable); - group = new FormGroup({'one': control}); - })); + it('should return true when the component is enabled', () => { + expect(group.contains('required')).toEqual(true); + group.enable(); - // TODO(kara): update these tests to use fake Async - it('should fire a statusChange if child has async validation change', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const loggedValues: string[] = []; - group.statusChanges.subscribe({ - next: (status: string) => { - loggedValues.push(status); - if (loggedValues.length === 2) { - expect(loggedValues).toEqual(['PENDING', 'INVALID']); - } - async.done(); - } - }); - control.setValue(''); - })); + expect(group.contains('optional')).toEqual(true); }); - describe('getError', () => { - it('should return the error when it is present', () => { - const c = new FormControl('', Validators.required); - const g = new FormGroup({'one': c}); - expect(c.getError('required')).toEqual(true); - expect(g.getError('required', ['one'])).toEqual(true); - }); + it('should support controls with dots in their name', () => { + expect(group.contains('some.name')).toBe(false); + group.addControl('some.name', new FormControl()); - it('should return null otherwise', () => { - const c = new FormControl('not empty', Validators.required); - const g = new FormGroup({'one': c}); - expect(c.getError('invalid')).toEqual(null); - expect(g.getError('required', ['one'])).toEqual(null); - expect(g.getError('required', ['invalid'])).toEqual(null); - }); + expect(group.contains('some.name')).toBe(true); + }); + }); - it('should be able to traverse group with single string', () => { - const c = new FormControl('', Validators.required); - const g = new FormGroup({'one': c}); - expect(c.getError('required')).toEqual(true); - expect(g.getError('required', 'one')).toEqual(true); - }); + describe('retrieve', () => { + let group: FormGroup; - it('should be able to traverse group with string delimited by dots', () => { - const c = new FormControl('', Validators.required); - const g2 = new FormGroup({'two': c}); - const g1 = new FormGroup({'one': g2}); - expect(c.getError('required')).toEqual(true); - expect(g1.getError('required', 'one.two')).toEqual(true); + beforeEach(() => { + group = new FormGroup({ + 'required': new FormControl('requiredValue'), }); + }); - it('should traverse group with form array using string and numbers', () => { - const c = new FormControl('', Validators.required); - const g2 = new FormGroup({'two': c}); - const a = new FormArray([g2]); - const g1 = new FormGroup({'one': a}); - expect(c.getError('required')).toEqual(true); - expect(g1.getError('required', ['one', 0, 'two'])).toEqual(true); - }); + it('should not get inherited properties', () => { + expect(group.get('constructor')).toBe(null); }); + }); - describe('hasError', () => { - it('should return true when it is present', () => { - const c = new FormControl('', Validators.required); - const g = new FormGroup({'one': c}); - expect(c.hasError('required')).toEqual(true); - expect(g.hasError('required', ['one'])).toEqual(true); - }); + describe('statusChanges', () => { + let control: FormControl; + let group: FormGroup; + + beforeEach(async(() => { + control = new FormControl('', asyncValidatorReturningObservable); + group = new FormGroup({'one': control}); + })); + + + // TODO(kara): update these tests to use fake Async + it('should fire a statusChange if child has async validation change', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const loggedValues: string[] = []; + group.statusChanges.subscribe({ + next: (status: string) => { + loggedValues.push(status); + if (loggedValues.length === 2) { + expect(loggedValues).toEqual(['PENDING', 'INVALID']); + } + async.done(); + } + }); + control.setValue(''); + })); + }); - it('should return false otherwise', () => { - const c = new FormControl('not empty', Validators.required); - const g = new FormGroup({'one': c}); - expect(c.hasError('invalid')).toEqual(false); - expect(g.hasError('required', ['one'])).toEqual(false); - expect(g.hasError('required', ['invalid'])).toEqual(false); - }); + describe('getError', () => { + it('should return the error when it is present', () => { + const c = new FormControl('', Validators.required); + const g = new FormGroup({'one': c}); + expect(c.getError('required')).toEqual(true); + expect(g.getError('required', ['one'])).toEqual(true); + }); - it('should be able to traverse group with single string', () => { - const c = new FormControl('', Validators.required); - const g = new FormGroup({'one': c}); - expect(c.hasError('required')).toEqual(true); - expect(g.hasError('required', 'one')).toEqual(true); - }); + it('should return null otherwise', () => { + const c = new FormControl('not empty', Validators.required); + const g = new FormGroup({'one': c}); + expect(c.getError('invalid')).toEqual(null); + expect(g.getError('required', ['one'])).toEqual(null); + expect(g.getError('required', ['invalid'])).toEqual(null); + }); - it('should be able to traverse group with string delimited by dots', () => { - const c = new FormControl('', Validators.required); - const g2 = new FormGroup({'two': c}); - const g1 = new FormGroup({'one': g2}); - expect(c.hasError('required')).toEqual(true); - expect(g1.hasError('required', 'one.two')).toEqual(true); - }); - it('should traverse group with form array using string and numbers', () => { - const c = new FormControl('', Validators.required); - const g2 = new FormGroup({'two': c}); - const a = new FormArray([g2]); - const g1 = new FormGroup({'one': a}); - expect(c.getError('required')).toEqual(true); - expect(g1.getError('required', ['one', 0, 'two'])).toEqual(true); - }); + it('should be able to traverse group with single string', () => { + const c = new FormControl('', Validators.required); + const g = new FormGroup({'one': c}); + expect(c.getError('required')).toEqual(true); + expect(g.getError('required', 'one')).toEqual(true); }); - describe('validator', () => { + it('should be able to traverse group with string delimited by dots', () => { + const c = new FormControl('', Validators.required); + const g2 = new FormGroup({'two': c}); + const g1 = new FormGroup({'one': g2}); + expect(c.getError('required')).toEqual(true); + expect(g1.getError('required', 'one.two')).toEqual(true); + }); - function containsValidator(c: AbstractControl): ValidationErrors|null { - return c.get('one') !.value && c.get('one') !.value.indexOf('c') !== -1 ? null : - {'missing': true}; - } + it('should traverse group with form array using string and numbers', () => { + const c = new FormControl('', Validators.required); + const g2 = new FormGroup({'two': c}); + const a = new FormArray([g2]); + const g1 = new FormGroup({'one': a}); + expect(c.getError('required')).toEqual(true); + expect(g1.getError('required', ['one', 0, 'two'])).toEqual(true); + }); + }); - it('should run a single validator when the value changes', () => { - const c = new FormControl(null); - const g = new FormGroup({'one': c}, simpleValidator); + describe('hasError', () => { + it('should return true when it is present', () => { + const c = new FormControl('', Validators.required); + const g = new FormGroup({'one': c}); + expect(c.hasError('required')).toEqual(true); + expect(g.hasError('required', ['one'])).toEqual(true); + }); - c.setValue('correct'); + it('should return false otherwise', () => { + const c = new FormControl('not empty', Validators.required); + const g = new FormGroup({'one': c}); + expect(c.hasError('invalid')).toEqual(false); + expect(g.hasError('required', ['one'])).toEqual(false); + expect(g.hasError('required', ['invalid'])).toEqual(false); + }); - expect(g.valid).toEqual(true); - expect(g.errors).toEqual(null); + it('should be able to traverse group with single string', () => { + const c = new FormControl('', Validators.required); + const g = new FormGroup({'one': c}); + expect(c.hasError('required')).toEqual(true); + expect(g.hasError('required', 'one')).toEqual(true); + }); - c.setValue('incorrect'); + it('should be able to traverse group with string delimited by dots', () => { + const c = new FormControl('', Validators.required); + const g2 = new FormGroup({'two': c}); + const g1 = new FormGroup({'one': g2}); + expect(c.hasError('required')).toEqual(true); + expect(g1.hasError('required', 'one.two')).toEqual(true); + }); + it('should traverse group with form array using string and numbers', () => { + const c = new FormControl('', Validators.required); + const g2 = new FormGroup({'two': c}); + const a = new FormArray([g2]); + const g1 = new FormGroup({'one': a}); + expect(c.getError('required')).toEqual(true); + expect(g1.getError('required', ['one', 0, 'two'])).toEqual(true); + }); + }); - expect(g.valid).toEqual(false); - expect(g.errors).toEqual({'broken': true}); - }); + describe('validator', () => { + function containsValidator(c: AbstractControl): ValidationErrors|null { + return c.get('one')!.value && c.get('one')!.value.indexOf('c') !== -1 ? null : + {'missing': true}; + } - it('should support multiple validators from array', () => { - const g = new FormGroup({one: new FormControl()}, [simpleValidator, containsValidator]); - expect(g.valid).toEqual(false); - expect(g.errors).toEqual({missing: true, broken: true}); + it('should run a single validator when the value changes', () => { + const c = new FormControl(null); + const g = new FormGroup({'one': c}, simpleValidator); - g.setValue({one: 'c'}); - expect(g.valid).toEqual(false); - expect(g.errors).toEqual({broken: true}); + c.setValue('correct'); - g.setValue({one: 'correct'}); - expect(g.valid).toEqual(true); - }); + expect(g.valid).toEqual(true); + expect(g.errors).toEqual(null); - it('should set single validator from options obj', () => { - const g = new FormGroup({one: new FormControl()}, {validators: simpleValidator}); - expect(g.valid).toEqual(false); - expect(g.errors).toEqual({broken: true}); + c.setValue('incorrect'); - g.setValue({one: 'correct'}); - expect(g.valid).toEqual(true); - }); + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({'broken': true}); + }); - it('should set multiple validators from options obj', () => { - const g = new FormGroup( - {one: new FormControl()}, {validators: [simpleValidator, containsValidator]}); - expect(g.valid).toEqual(false); - expect(g.errors).toEqual({missing: true, broken: true}); + it('should support multiple validators from array', () => { + const g = new FormGroup({one: new FormControl()}, [simpleValidator, containsValidator]); + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({missing: true, broken: true}); - g.setValue({one: 'c'}); - expect(g.valid).toEqual(false); - expect(g.errors).toEqual({broken: true}); + g.setValue({one: 'c'}); + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({broken: true}); - g.setValue({one: 'correct'}); - expect(g.valid).toEqual(true); - }); + g.setValue({one: 'correct'}); + expect(g.valid).toEqual(true); + }); + it('should set single validator from options obj', () => { + const g = new FormGroup({one: new FormControl()}, {validators: simpleValidator}); + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({broken: true}); + + g.setValue({one: 'correct'}); + expect(g.valid).toEqual(true); }); - describe('asyncValidator', () => { - it('should run the async validator', fakeAsync(() => { - const c = new FormControl('value'); - const g = new FormGroup({'one': c}, null !, asyncValidator('expected')); + it('should set multiple validators from options obj', () => { + const g = new FormGroup( + {one: new FormControl()}, {validators: [simpleValidator, containsValidator]}); + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({missing: true, broken: true}); - expect(g.pending).toEqual(true); + g.setValue({one: 'c'}); + expect(g.valid).toEqual(false); + expect(g.errors).toEqual({broken: true}); - tick(1); + g.setValue({one: 'correct'}); + expect(g.valid).toEqual(true); + }); + }); - expect(g.errors).toEqual({'async': true}); - expect(g.pending).toEqual(false); - })); + describe('asyncValidator', () => { + it('should run the async validator', fakeAsync(() => { + const c = new FormControl('value'); + const g = new FormGroup({'one': c}, null!, asyncValidator('expected')); - it('should set multiple async validators from array', fakeAsync(() => { - const g = new FormGroup( - {'one': new FormControl('value')}, null !, - [asyncValidator('expected'), otherObservableValidator]); - expect(g.pending).toEqual(true); + expect(g.pending).toEqual(true); - tick(); - expect(g.errors).toEqual({'async': true, 'other': true}); - expect(g.pending).toEqual(false); - })); + tick(1); - it('should set single async validator from options obj', fakeAsync(() => { - const g = new FormGroup( - {'one': new FormControl('value')}, {asyncValidators: asyncValidator('expected')}); - expect(g.pending).toEqual(true); + expect(g.errors).toEqual({'async': true}); + expect(g.pending).toEqual(false); + })); - tick(); - expect(g.errors).toEqual({'async': true}); - expect(g.pending).toEqual(false); - })); + it('should set multiple async validators from array', fakeAsync(() => { + const g = new FormGroup( + {'one': new FormControl('value')}, null!, + [asyncValidator('expected'), otherObservableValidator]); + expect(g.pending).toEqual(true); - it('should set multiple async validators from options obj', fakeAsync(() => { - const g = new FormGroup( - {'one': new FormControl('value')}, - {asyncValidators: [asyncValidator('expected'), otherObservableValidator]}); - expect(g.pending).toEqual(true); + tick(); + expect(g.errors).toEqual({'async': true, 'other': true}); + expect(g.pending).toEqual(false); + })); - tick(); - expect(g.errors).toEqual({'async': true, 'other': true}); - expect(g.pending).toEqual(false); - })); + it('should set single async validator from options obj', fakeAsync(() => { + const g = new FormGroup( + {'one': new FormControl('value')}, {asyncValidators: asyncValidator('expected')}); + expect(g.pending).toEqual(true); - it('should set the parent group\'s status to pending', fakeAsync(() => { - const c = new FormControl('value', null !, asyncValidator('expected')); - const g = new FormGroup({'one': c}); + tick(); + expect(g.errors).toEqual({'async': true}); + expect(g.pending).toEqual(false); + })); - expect(g.pending).toEqual(true); + it('should set multiple async validators from options obj', fakeAsync(() => { + const g = new FormGroup( + {'one': new FormControl('value')}, + {asyncValidators: [asyncValidator('expected'), otherObservableValidator]}); + expect(g.pending).toEqual(true); - tick(1); + tick(); + expect(g.errors).toEqual({'async': true, 'other': true}); + expect(g.pending).toEqual(false); + })); - expect(g.pending).toEqual(false); - })); + it('should set the parent group\'s status to pending', fakeAsync(() => { + const c = new FormControl('value', null!, asyncValidator('expected')); + const g = new FormGroup({'one': c}); - it('should run the parent group\'s async validator when children are pending', - fakeAsync(() => { - const c = new FormControl('value', null !, asyncValidator('expected')); - const g = new FormGroup({'one': c}, null !, asyncValidator('expected')); + expect(g.pending).toEqual(true); - tick(1); + tick(1); - expect(g.errors).toEqual({'async': true}); - expect(g.get('one') !.errors).toEqual({'async': true}); - })); + expect(g.pending).toEqual(false); + })); + + it('should run the parent group\'s async validator when children are pending', fakeAsync(() => { + const c = new FormControl('value', null!, asyncValidator('expected')); + const g = new FormGroup({'one': c}, null!, asyncValidator('expected')); + + tick(1); + + expect(g.errors).toEqual({'async': true}); + expect(g.get('one')!.errors).toEqual({'async': true}); + })); + }); + + describe('disable() & enable()', () => { + it('should mark the group as disabled', () => { + const g = new FormGroup({'one': new FormControl(null)}); + expect(g.disabled).toBe(false); + expect(g.valid).toBe(true); + + g.disable(); + expect(g.disabled).toBe(true); + expect(g.valid).toBe(false); + + g.enable(); + expect(g.disabled).toBe(false); + expect(g.valid).toBe(true); }); - describe('disable() & enable()', () => { - it('should mark the group as disabled', () => { - const g = new FormGroup({'one': new FormControl(null)}); - expect(g.disabled).toBe(false); - expect(g.valid).toBe(true); + it('should set the group status as disabled', () => { + const g = new FormGroup({'one': new FormControl(null)}); + expect(g.status).toEqual('VALID'); - g.disable(); - expect(g.disabled).toBe(true); - expect(g.valid).toBe(false); + g.disable(); + expect(g.status).toEqual('DISABLED'); - g.enable(); - expect(g.disabled).toBe(false); - expect(g.valid).toBe(true); - }); + g.enable(); + expect(g.status).toBe('VALID'); + }); - it('should set the group status as disabled', () => { - const g = new FormGroup({'one': new FormControl(null)}); - expect(g.status).toEqual('VALID'); + it('should mark children of the group as disabled', () => { + const c1 = new FormControl(null); + const c2 = new FormControl(null); + const g = new FormGroup({'one': c1, 'two': c2}); + expect(c1.disabled).toBe(false); + expect(c2.disabled).toBe(false); - g.disable(); - expect(g.status).toEqual('DISABLED'); + g.disable(); + expect(c1.disabled).toBe(true); + expect(c2.disabled).toBe(true); - g.enable(); - expect(g.status).toBe('VALID'); + g.enable(); + expect(c1.disabled).toBe(false); + expect(c2.disabled).toBe(false); + }); + + it('should ignore disabled controls in validation', () => { + const g = new FormGroup({ + nested: new FormGroup({one: new FormControl(null, Validators.required)}), + two: new FormControl('two') }); + expect(g.valid).toBe(false); - it('should mark children of the group as disabled', () => { - const c1 = new FormControl(null); - const c2 = new FormControl(null); - const g = new FormGroup({'one': c1, 'two': c2}); - expect(c1.disabled).toBe(false); - expect(c2.disabled).toBe(false); + g.get('nested')!.disable(); + expect(g.valid).toBe(true); - g.disable(); - expect(c1.disabled).toBe(true); - expect(c2.disabled).toBe(true); + g.get('nested')!.enable(); + expect(g.valid).toBe(false); + }); - g.enable(); - expect(c1.disabled).toBe(false); - expect(c2.disabled).toBe(false); - }); + it('should ignore disabled controls when serializing value', () => { + const g = new FormGroup( + {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); + expect(g.value).toEqual({'nested': {'one': 'one'}, 'two': 'two'}); - it('should ignore disabled controls in validation', () => { - const g = new FormGroup({ - nested: new FormGroup({one: new FormControl(null, Validators.required)}), - two: new FormControl('two') - }); - expect(g.valid).toBe(false); + g.get('nested')!.disable(); + expect(g.value).toEqual({'two': 'two'}); - g.get('nested') !.disable(); - expect(g.valid).toBe(true); + g.get('nested')!.enable(); + expect(g.value).toEqual({'nested': {'one': 'one'}, 'two': 'two'}); + }); - g.get('nested') !.enable(); - expect(g.valid).toBe(false); - }); + it('should update its value when disabled with disabled children', () => { + const g = new FormGroup( + {nested: new FormGroup({one: new FormControl('one'), two: new FormControl('two')})}); - it('should ignore disabled controls when serializing value', () => { - const g = new FormGroup( - {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); - expect(g.value).toEqual({'nested': {'one': 'one'}, 'two': 'two'}); + g.get('nested.two')!.disable(); + expect(g.value).toEqual({nested: {one: 'one'}}); - g.get('nested') !.disable(); - expect(g.value).toEqual({'two': 'two'}); + g.get('nested')!.disable(); + expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); - g.get('nested') !.enable(); - expect(g.value).toEqual({'nested': {'one': 'one'}, 'two': 'two'}); - }); + g.get('nested')!.enable(); + expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); + }); - it('should update its value when disabled with disabled children', () => { - const g = new FormGroup( - {nested: new FormGroup({one: new FormControl('one'), two: new FormControl('two')})}); + it('should update its value when enabled with disabled children', () => { + const g = new FormGroup( + {nested: new FormGroup({one: new FormControl('one'), two: new FormControl('two')})}); - g.get('nested.two') !.disable(); - expect(g.value).toEqual({nested: {one: 'one'}}); + g.get('nested.two')!.disable(); + expect(g.value).toEqual({nested: {one: 'one'}}); - g.get('nested') !.disable(); - expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); + g.get('nested')!.enable(); + expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); + }); - g.get('nested') !.enable(); - expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); - }); + it('should ignore disabled controls when determining dirtiness', () => { + const g = new FormGroup( + {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); + g.get('nested.one')!.markAsDirty(); + expect(g.dirty).toBe(true); - it('should update its value when enabled with disabled children', () => { - const g = new FormGroup( - {nested: new FormGroup({one: new FormControl('one'), two: new FormControl('two')})}); + g.get('nested')!.disable(); + expect(g.get('nested')!.dirty).toBe(true); + expect(g.dirty).toEqual(false); - g.get('nested.two') !.disable(); - expect(g.value).toEqual({nested: {one: 'one'}}); + g.get('nested')!.enable(); + expect(g.dirty).toEqual(true); + }); - g.get('nested') !.enable(); - expect(g.value).toEqual({nested: {one: 'one', two: 'two'}}); - }); + it('should ignore disabled controls when determining touched state', () => { + const g = new FormGroup( + {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); + g.get('nested.one')!.markAsTouched(); + expect(g.touched).toBe(true); - it('should ignore disabled controls when determining dirtiness', () => { - const g = new FormGroup( - {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); - g.get('nested.one') !.markAsDirty(); - expect(g.dirty).toBe(true); + g.get('nested')!.disable(); + expect(g.get('nested')!.touched).toBe(true); + expect(g.touched).toEqual(false); - g.get('nested') !.disable(); - expect(g.get('nested') !.dirty).toBe(true); - expect(g.dirty).toEqual(false); + g.get('nested')!.enable(); + expect(g.touched).toEqual(true); + }); - g.get('nested') !.enable(); - expect(g.dirty).toEqual(true); - }); + it('should keep empty, disabled groups disabled when updating validity', () => { + const group = new FormGroup({}); + expect(group.status).toEqual('VALID'); - it('should ignore disabled controls when determining touched state', () => { - const g = new FormGroup( - {nested: new FormGroup({one: new FormControl('one')}), two: new FormControl('two')}); - g.get('nested.one') !.markAsTouched(); - expect(g.touched).toBe(true); + group.disable(); + expect(group.status).toEqual('DISABLED'); - g.get('nested') !.disable(); - expect(g.get('nested') !.touched).toBe(true); - expect(g.touched).toEqual(false); + group.updateValueAndValidity(); + expect(group.status).toEqual('DISABLED'); - g.get('nested') !.enable(); - expect(g.touched).toEqual(true); - }); + group.addControl('one', new FormControl({value: '', disabled: true})); + expect(group.status).toEqual('DISABLED'); - it('should keep empty, disabled groups disabled when updating validity', () => { - const group = new FormGroup({}); - expect(group.status).toEqual('VALID'); + group.addControl('two', new FormControl()); + expect(group.status).toEqual('VALID'); + }); - group.disable(); - expect(group.status).toEqual('DISABLED'); + it('should re-enable empty, disabled groups', () => { + const group = new FormGroup({}); + group.disable(); + expect(group.status).toEqual('DISABLED'); - group.updateValueAndValidity(); - expect(group.status).toEqual('DISABLED'); + group.enable(); + expect(group.status).toEqual('VALID'); + }); - group.addControl('one', new FormControl({value: '', disabled: true})); - expect(group.status).toEqual('DISABLED'); + it('should not run validators on disabled controls', () => { + const validator = jasmine.createSpy('validator'); + const g = new FormGroup({'one': new FormControl()}, validator); + expect(validator.calls.count()).toEqual(1); - group.addControl('two', new FormControl()); - expect(group.status).toEqual('VALID'); - }); + g.disable(); + expect(validator.calls.count()).toEqual(1); - it('should re-enable empty, disabled groups', () => { - const group = new FormGroup({}); - group.disable(); - expect(group.status).toEqual('DISABLED'); + g.setValue({one: 'value'}); + expect(validator.calls.count()).toEqual(1); - group.enable(); - expect(group.status).toEqual('VALID'); - }); + g.enable(); + expect(validator.calls.count()).toEqual(2); + }); - it('should not run validators on disabled controls', () => { - const validator = jasmine.createSpy('validator'); - const g = new FormGroup({'one': new FormControl()}, validator); - expect(validator.calls.count()).toEqual(1); + describe('disabled errors', () => { + it('should clear out group errors when disabled', () => { + const g = new FormGroup({'one': new FormControl()}, () => ({'expected': true})); + expect(g.errors).toEqual({'expected': true}); g.disable(); - expect(validator.calls.count()).toEqual(1); - - g.setValue({one: 'value'}); - expect(validator.calls.count()).toEqual(1); + expect(g.errors).toEqual(null); g.enable(); - expect(validator.calls.count()).toEqual(2); + expect(g.errors).toEqual({'expected': true}); }); - describe('disabled errors', () => { - it('should clear out group errors when disabled', () => { - const g = new FormGroup({'one': new FormControl()}, () => ({'expected': true})); - expect(g.errors).toEqual({'expected': true}); - - g.disable(); - expect(g.errors).toEqual(null); - - g.enable(); - expect(g.errors).toEqual({'expected': true}); - }); - - it('should re-populate group errors when enabled from a child', () => { - const g = new FormGroup({'one': new FormControl()}, () => ({'expected': true})); - g.disable(); - expect(g.errors).toEqual(null); - - g.addControl('two', new FormControl()); - expect(g.errors).toEqual({'expected': true}); - }); - - it('should clear out async group errors when disabled', fakeAsync(() => { - const g = - new FormGroup({'one': new FormControl()}, null !, asyncValidator('expected')); - tick(); - expect(g.errors).toEqual({'async': true}); - - g.disable(); - expect(g.errors).toEqual(null); - - g.enable(); - tick(); - expect(g.errors).toEqual({'async': true}); - })); - - it('should re-populate async group errors when enabled from a child', fakeAsync(() => { - const g = - new FormGroup({'one': new FormControl()}, null !, asyncValidator('expected')); - tick(); - expect(g.errors).toEqual({'async': true}); - - g.disable(); - expect(g.errors).toEqual(null); - - g.addControl('two', new FormControl()); - tick(); - expect(g.errors).toEqual({'async': true}); - })); + it('should re-populate group errors when enabled from a child', () => { + const g = new FormGroup({'one': new FormControl()}, () => ({'expected': true})); + g.disable(); + expect(g.errors).toEqual(null); + + g.addControl('two', new FormControl()); + expect(g.errors).toEqual({'expected': true}); }); - describe('disabled events', () => { - let logger: string[]; - let c: FormControl; - let g: FormGroup; - let form: FormGroup; - - beforeEach(() => { - logger = []; - c = new FormControl('', Validators.required); - g = new FormGroup({one: c}); - form = new FormGroup({g: g}); - }); - - it('should emit value change events in the right order', () => { - c.valueChanges.subscribe(() => logger.push('control')); - g.valueChanges.subscribe(() => logger.push('group')); - form.valueChanges.subscribe(() => logger.push('form')); - - g.disable(); - expect(logger).toEqual(['control', 'group', 'form']); - }); - - it('should emit status change events in the right order', () => { - c.statusChanges.subscribe(() => logger.push('control')); - g.statusChanges.subscribe(() => logger.push('group')); - form.statusChanges.subscribe(() => logger.push('form')); - - g.disable(); - expect(logger).toEqual(['control', 'group', 'form']); - }); - - it('should not emit value change events when emitEvent = false', () => { - c.valueChanges.subscribe(() => logger.push('control')); - g.valueChanges.subscribe(() => logger.push('group')); - form.valueChanges.subscribe(() => logger.push('form')); - - g.disable({emitEvent: false}); - expect(logger).toEqual([]); - g.enable({emitEvent: false}); - expect(logger).toEqual([]); - }); - - it('should not emit status change events when emitEvent = false', () => { - c.statusChanges.subscribe(() => logger.push('control')); - g.statusChanges.subscribe(() => logger.push('group')); - form.statusChanges.subscribe(() => logger.push('form')); - - g.disable({emitEvent: false}); - expect(logger).toEqual([]); - g.enable({emitEvent: false}); - expect(logger).toEqual([]); - }); + it('should clear out async group errors when disabled', fakeAsync(() => { + const g = new FormGroup({'one': new FormControl()}, null!, asyncValidator('expected')); + tick(); + expect(g.errors).toEqual({'async': true}); - }); + g.disable(); + expect(g.errors).toEqual(null); + + g.enable(); + tick(); + expect(g.errors).toEqual({'async': true}); + })); + + it('should re-populate async group errors when enabled from a child', fakeAsync(() => { + const g = new FormGroup({'one': new FormControl()}, null!, asyncValidator('expected')); + tick(); + expect(g.errors).toEqual({'async': true}); + g.disable(); + expect(g.errors).toEqual(null); + + g.addControl('two', new FormControl()); + tick(); + expect(g.errors).toEqual({'async': true}); + })); }); - describe('updateTreeValidity()', () => { - let c: FormControl, c2: FormControl, c3: FormControl; - let nested: FormGroup, form: FormGroup; + describe('disabled events', () => { let logger: string[]; + let c: FormControl; + let g: FormGroup; + let form: FormGroup; beforeEach(() => { - c = new FormControl('one'); - c2 = new FormControl('two'); - c3 = new FormControl('three'); - nested = new FormGroup({one: c, two: c2}); - form = new FormGroup({nested: nested, three: c3}); logger = []; + c = new FormControl('', Validators.required); + g = new FormGroup({one: c}); + form = new FormGroup({g: g}); + }); - c.statusChanges.subscribe(() => logger.push('one')); - c2.statusChanges.subscribe(() => logger.push('two')); - c3.statusChanges.subscribe(() => logger.push('three')); - nested.statusChanges.subscribe(() => logger.push('nested')); + it('should emit value change events in the right order', () => { + c.valueChanges.subscribe(() => logger.push('control')); + g.valueChanges.subscribe(() => logger.push('group')); + form.valueChanges.subscribe(() => logger.push('form')); + + g.disable(); + expect(logger).toEqual(['control', 'group', 'form']); + }); + + it('should emit status change events in the right order', () => { + c.statusChanges.subscribe(() => logger.push('control')); + g.statusChanges.subscribe(() => logger.push('group')); form.statusChanges.subscribe(() => logger.push('form')); + + g.disable(); + expect(logger).toEqual(['control', 'group', 'form']); }); - it('should update tree validity', () => { - (form as any)._updateTreeValidity(); - expect(logger).toEqual(['one', 'two', 'nested', 'three', 'form']); + it('should not emit value change events when emitEvent = false', () => { + c.valueChanges.subscribe(() => logger.push('control')); + g.valueChanges.subscribe(() => logger.push('group')); + form.valueChanges.subscribe(() => logger.push('form')); + + g.disable({emitEvent: false}); + expect(logger).toEqual([]); + g.enable({emitEvent: false}); + expect(logger).toEqual([]); }); - it('should not emit events when turned off', () => { - (form as any)._updateTreeValidity({emitEvent: false}); + it('should not emit status change events when emitEvent = false', () => { + c.statusChanges.subscribe(() => logger.push('control')); + g.statusChanges.subscribe(() => logger.push('group')); + form.statusChanges.subscribe(() => logger.push('form')); + + g.disable({emitEvent: false}); + expect(logger).toEqual([]); + g.enable({emitEvent: false}); expect(logger).toEqual([]); }); + }); + }); + describe('updateTreeValidity()', () => { + let c: FormControl, c2: FormControl, c3: FormControl; + let nested: FormGroup, form: FormGroup; + let logger: string[]; + + beforeEach(() => { + c = new FormControl('one'); + c2 = new FormControl('two'); + c3 = new FormControl('three'); + nested = new FormGroup({one: c, two: c2}); + form = new FormGroup({nested: nested, three: c3}); + logger = []; + + c.statusChanges.subscribe(() => logger.push('one')); + c2.statusChanges.subscribe(() => logger.push('two')); + c3.statusChanges.subscribe(() => logger.push('three')); + nested.statusChanges.subscribe(() => logger.push('nested')); + form.statusChanges.subscribe(() => logger.push('form')); }); - describe('setControl()', () => { - let c: FormControl; - let g: FormGroup; + it('should update tree validity', () => { + (form as any)._updateTreeValidity(); + expect(logger).toEqual(['one', 'two', 'nested', 'three', 'form']); + }); - beforeEach(() => { - c = new FormControl('one'); - g = new FormGroup({one: c}); - }); + it('should not emit events when turned off', () => { + (form as any)._updateTreeValidity({emitEvent: false}); + expect(logger).toEqual([]); + }); + }); - it('should replace existing control with new control', () => { - const c2 = new FormControl('new!', Validators.minLength(10)); - g.setControl('one', c2); + describe('setControl()', () => { + let c: FormControl; + let g: FormGroup; - expect(g.controls['one']).toEqual(c2); - expect(g.value).toEqual({one: 'new!'}); - expect(g.valid).toBe(false); - }); + beforeEach(() => { + c = new FormControl('one'); + g = new FormGroup({one: c}); + }); - it('should add control if control did not exist before', () => { - const c2 = new FormControl('new!', Validators.minLength(10)); - g.setControl('two', c2); + it('should replace existing control with new control', () => { + const c2 = new FormControl('new!', Validators.minLength(10)); + g.setControl('one', c2); - expect(g.controls['two']).toEqual(c2); - expect(g.value).toEqual({one: 'one', two: 'new!'}); - expect(g.valid).toBe(false); - }); + expect(g.controls['one']).toEqual(c2); + expect(g.value).toEqual({one: 'new!'}); + expect(g.valid).toBe(false); + }); - it('should remove control if new control is null', () => { - g.setControl('one', null !); - expect(g.controls['one']).not.toBeDefined(); - expect(g.value).toEqual({}); - }); + it('should add control if control did not exist before', () => { + const c2 = new FormControl('new!', Validators.minLength(10)); + g.setControl('two', c2); - it('should only emit value change event once', () => { - const logger: string[] = []; - const c2 = new FormControl('new!'); - g.valueChanges.subscribe(() => logger.push('change!')); - g.setControl('one', c2); - expect(logger).toEqual(['change!']); - }); + expect(g.controls['two']).toEqual(c2); + expect(g.value).toEqual({one: 'one', two: 'new!'}); + expect(g.valid).toBe(false); + }); + it('should remove control if new control is null', () => { + g.setControl('one', null!); + expect(g.controls['one']).not.toBeDefined(); + expect(g.value).toEqual({}); }); - describe('pending', () => { - let c: FormControl; - let g: FormGroup; + it('should only emit value change event once', () => { + const logger: string[] = []; + const c2 = new FormControl('new!'); + g.valueChanges.subscribe(() => logger.push('change!')); + g.setControl('one', c2); + expect(logger).toEqual(['change!']); + }); + }); + + describe('pending', () => { + let c: FormControl; + let g: FormGroup; + + beforeEach(() => { + c = new FormControl('value'); + g = new FormGroup({'one': c}); + }); + + it('should be false after creating a control', () => { + expect(g.pending).toEqual(false); + }); + + it('should be true after changing the value of the control', () => { + c.markAsPending(); + expect(g.pending).toEqual(true); + }); + + it('should not update the parent when onlySelf = true', () => { + c.markAsPending({onlySelf: true}); + expect(g.pending).toEqual(false); + }); + + describe('status change events', () => { + let logger: string[]; beforeEach(() => { - c = new FormControl('value'); - g = new FormGroup({'one': c}); + logger = []; + g.statusChanges.subscribe((status) => logger.push(status)); }); - it('should be false after creating a control', () => { expect(g.pending).toEqual(false); }); - - it('should be true after changing the value of the control', () => { + it('should emit event after marking control as pending', () => { c.markAsPending(); - expect(g.pending).toEqual(true); + expect(logger).toEqual(['PENDING']); }); - it('should not update the parent when onlySelf = true', () => { + it('should not emit event when onlySelf = true', () => { c.markAsPending({onlySelf: true}); - expect(g.pending).toEqual(false); + expect(logger).toEqual([]); }); - describe('status change events', () => { - let logger: string[]; - - beforeEach(() => { - logger = []; - g.statusChanges.subscribe((status) => logger.push(status)); - }); - - it('should emit event after marking control as pending', () => { - c.markAsPending(); - expect(logger).toEqual(['PENDING']); - }); - - it('should not emit event when onlySelf = true', () => { - c.markAsPending({onlySelf: true}); - expect(logger).toEqual([]); - }); - - it('should not emit event when emitEvent = false', () => { - c.markAsPending({emitEvent: false}); - expect(logger).toEqual([]); - }); - - it('should emit event when parent is markedAsPending', () => { - g.markAsPending(); - expect(logger).toEqual(['PENDING']); - }); + it('should not emit event when emitEvent = false', () => { + c.markAsPending({emitEvent: false}); + expect(logger).toEqual([]); }); - }); + it('should emit event when parent is markedAsPending', () => { + g.markAsPending(); + expect(logger).toEqual(['PENDING']); + }); + }); }); +}); })(); diff --git a/packages/forms/test/reactive_integration_spec.ts b/packages/forms/test/reactive_integration_spec.ts index 9a1b144dda26e..8e8d5e0f4cb74 100644 --- a/packages/forms/test/reactive_integration_spec.ts +++ b/packages/forms/test/reactive_integration_spec.ts @@ -7,8 +7,8 @@ */ import {ɵgetDOM as getDOM} from '@angular/common'; -import {Component, Directive, Input, Type, forwardRef} from '@angular/core'; -import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'; +import {Component, Directive, forwardRef, Input, Type} from '@angular/core'; +import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; import {AbstractControl, AsyncValidator, AsyncValidatorFn, COMPOSITION_BUFFER_MODE, FormArray, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, ReactiveFormsModule, Validators} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util'; @@ -19,7 +19,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; { describe('reactive forms integration tests', () => { - function initTest<T>(component: Type<T>, ...directives: Type<any>[]): ComponentFixture<T> { TestBed.configureTestingModule( {declarations: [component, ...directives], imports: [FormsModule, ReactiveFormsModule]}); @@ -74,11 +73,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(form.value).toEqual({'login': 'updatedValue'}); }); - }); describe('re-bound form groups', () => { - it('should update DOM elements initially', () => { const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('oldValue')}); @@ -150,7 +147,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; }); fixture.componentInstance.form = form; fixture.detectChanges(); - expect(form.get('login') !.errors).toEqual({required: true}); + expect(form.get('login')!.errors).toEqual({required: true}); const newForm = new FormGroup({ 'login': new FormControl(''), @@ -161,34 +158,31 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; fixture.componentInstance.form = newForm; fixture.detectChanges(); - expect(newForm.get('login') !.errors).toEqual({required: true}); + expect(newForm.get('login')!.errors).toEqual({required: true}); }); it('should pick up dir validators from nested form groups', () => { const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator); const form = new FormGroup({ - 'signin': - new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) + 'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) }); fixture.componentInstance.form = form; fixture.detectChanges(); - expect(form.get('signin') !.valid).toBe(false); + expect(form.get('signin')!.valid).toBe(false); const newForm = new FormGroup({ - 'signin': - new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) + 'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) }); fixture.componentInstance.form = newForm; fixture.detectChanges(); - expect(form.get('signin') !.valid).toBe(false); + expect(form.get('signin')!.valid).toBe(false); }); it('should strip named controls that are not found', () => { const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator); const form = new FormGroup({ - 'signin': - new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) + 'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) }); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -200,8 +194,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(emailInput.nativeElement.value).toEqual('email'); const newForm = new FormGroup({ - 'signin': - new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) + 'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) }); fixture.componentInstance.form = newForm; fixture.detectChanges(); @@ -237,7 +230,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; }); describe('nested control rebinding', () => { - it('should attach dir to control when leaf control changes', () => { const form = new FormGroup({'login': new FormControl('oldValue')}); const fixture = initTest(FormGroupComp); @@ -359,7 +351,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(newArr.value).toEqual(['last one']); - newArr.get([0]) !.setValue('set value'); + newArr.get([0])!.setValue('set value'); fixture.detectChanges(); firstInput = fixture.debugElement.query(By.css('input')).nativeElement; @@ -416,14 +408,12 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(newArr.value).toEqual(['SF', 'LA', 'Tulsa']); - newArr.get([2]) !.setValue('NY'); + newArr.get([2])!.setValue('NY'); fixture.detectChanges(); expect(lastInput.value).toEqual('NY'); }); - }); - }); describe('form arrays', () => { @@ -494,7 +484,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; cities: [{town: 'LA', state: 'CA'}, {town: 'NY', state: 'NY'}] }); }); - }); describe('programmatic changes', () => { @@ -592,13 +581,10 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; const input = fixture.debugElement.query(By.css('my-input')); expect(input.nativeElement.getAttribute('disabled')).toBe(null); }); - }); - }); describe('user input', () => { - it('should mark controls as touched after interacting with the DOM control', () => { const fixture = initTest(FormGroupComp); const login = new FormControl('oldValue'); @@ -613,14 +599,13 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(login.touched).toBe(true); }); - }); describe('submit and reset events', () => { it('should emit ngSubmit event with the original submit event on submit', () => { const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')}); - fixture.componentInstance.event = null !; + fixture.componentInstance.event = null!; fixture.detectChanges(); const formEl = fixture.debugElement.query(By.css('form')).nativeElement; @@ -672,18 +657,18 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; form.reset(); expect(loginEl.value).toBe(''); }); - }); describe('value changes and status changes', () => { - it('should mark controls as dirty before emitting a value change event', () => { const fixture = initTest(FormGroupComp); const login = new FormControl('oldValue'); fixture.componentInstance.form = new FormGroup({'login': login}); fixture.detectChanges(); - login.valueChanges.subscribe(() => { expect(login.dirty).toBe(true); }); + login.valueChanges.subscribe(() => { + expect(login.dirty).toBe(true); + }); const loginEl = fixture.debugElement.query(By.css('input')).nativeElement; loginEl.value = 'newValue'; @@ -705,11 +690,12 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(login.pristine).toBe(false); - login.valueChanges.subscribe(() => { expect(login.pristine).toBe(true); }); + login.valueChanges.subscribe(() => { + expect(login.pristine).toBe(true); + }); form.reset(); }); - }); describe('setting status classes', () => { @@ -736,7 +722,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; it('should work with single fields and async validators', fakeAsync(() => { const fixture = initTest(FormControlComp); - const control = new FormControl('', null !, uniqLoginAsyncValidator('good')); + const control = new FormControl('', null!, uniqLoginAsyncValidator('good')); fixture.debugElement.componentInstance.control = control; fixture.detectChanges(); @@ -831,13 +817,10 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(sortedClassList(formEl)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); }); - }); describe('updateOn options', () => { - describe('on blur', () => { - it('should not update value or validity based on user input until blur', () => { const fixture = initTest(FormControlComp); const control = new FormControl('', {validators: Validators.required, updateOn: 'blur'}); @@ -1141,7 +1124,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(control.value) .toEqual('Nancy', 'Expected value to change once control is blurred.'); expect(control.valid).toBe(true, 'Expected validation to run once control is blurred.'); - }); it('should update on blur with array updateOn', () => { @@ -1167,7 +1149,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(control.value) .toEqual('Nancy', 'Expected value to change once control is blurred.'); expect(control.valid).toBe(true, 'Expected validation to run once control is blurred.'); - }); @@ -1206,17 +1187,13 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(passwordControl.valid) .toBe(true, 'Expected validation to run once control is blurred.'); }); - - }); describe('on submit', () => { - it('should set initial value and validity on init', () => { const fixture = initTest(FormGroupComp); const form = new FormGroup({ - login: - new FormControl('Nancy', {validators: Validators.required, updateOn: 'submit'}) + login: new FormControl('Nancy', {validators: Validators.required, updateOn: 'submit'}) }); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -1430,7 +1407,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; fixture.componentInstance.form = formGroup; fixture.detectChanges(); - const values: (string | {[key: string]: string})[] = []; + const values: (string|{[key: string]: string})[] = []; const streams = merge( control.valueChanges, control.statusChanges, formGroup.valueChanges, formGroup.statusChanges); @@ -1493,8 +1470,8 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; fixture.componentInstance.form = formGroup; fixture.detectChanges(); - formGroup.get('signin.login') !.setValidators(validatorSpy); - formGroup.get('signin') !.setValidators(groupValidatorSpy); + formGroup.get('signin.login')!.setValidators(validatorSpy); + formGroup.get('signin')!.setValidators(groupValidatorSpy); const form = fixture.debugElement.query(By.css('form')).nativeElement; dispatchEvent(form, 'submit'); @@ -1502,7 +1479,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(validatorSpy).not.toHaveBeenCalled(); expect(groupValidatorSpy).not.toHaveBeenCalled(); - }); it('should mark as untouched properly if pending touched', () => { @@ -1554,7 +1530,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(control.value).toEqual('Nancy', 'Expected value to change on submit.'); expect(control.valid).toBe(true, 'Expected validation to run on submit.'); - }); it('should update on submit with array updateOn', () => { @@ -1581,7 +1556,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(control.value).toEqual('Nancy', 'Expected value to change once control on submit'); expect(control.valid).toBe(true, 'Expected validation to run on submit.'); - }); it('should allow child control updateOn submit to override group updateOn', () => { @@ -1619,9 +1593,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(passwordControl.value).toEqual('Carson', 'Expected value to change on submit.'); expect(passwordControl.valid).toBe(true, 'Expected validation to run on submit.'); }); - }); - }); describe('ngModel interactions', () => { @@ -1636,7 +1608,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; }); describe('deprecation warnings', () => { - it('should warn once by default when using ngModel with formControlName', fakeAsync(() => { const fixture = initTest(FormGroupNgModel); fixture.componentInstance.form = @@ -1679,8 +1650,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; fakeAsync(() => { TestBed.configureTestingModule({ declarations: [FormControlNgModel], - imports: - [ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'always'})] + imports: [ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'always'})] }); const fixture = TestBed.createComponent(FormControlNgModel); @@ -1710,7 +1680,6 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(warnSpy).not.toHaveBeenCalled(); })); - }); it('should support ngModel for complex forms', fakeAsync(() => { @@ -1794,9 +1763,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(fixture.componentInstance.login) .toEqual('Nancy', 'Expected ngModel value to update on submit.'); - })); - }); describe('validations', () => { @@ -1969,9 +1936,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; .toEqual(pattern.nativeElement.getAttribute('pattern')); fixture.componentInstance.required = false; - fixture.componentInstance.minLen = null !; - fixture.componentInstance.maxLen = null !; - fixture.componentInstance.pattern = null !; + fixture.componentInstance.minLen = null!; + fixture.componentInstance.maxLen = null!; + fixture.componentInstance.pattern = null!; fixture.detectChanges(); expect(form.hasError('required', ['login'])).toEqual(false); @@ -2011,9 +1978,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; fixture.detectChanges(); fixture.componentInstance.required = false; - fixture.componentInstance.minLen = null !; - fixture.componentInstance.maxLen = null !; - fixture.componentInstance.pattern = null !; + fixture.componentInstance.minLen = null!; + fixture.componentInstance.maxLen = null!; + fixture.componentInstance.pattern = null!; fixture.detectChanges(); expect(newForm.hasError('required', ['login'])).toEqual(false); @@ -2111,7 +2078,7 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; const fixture = initTest(FormControlComp); const resultArr: number[] = []; fixture.componentInstance.control = - new FormControl('', null !, observableValidator(resultArr)); + new FormControl('', null!, observableValidator(resultArr)); fixture.detectChanges(); tick(100); @@ -2130,12 +2097,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(resultArr.length) .toEqual(2, `Expected original observable to be canceled on the next value change.`); })); - - }); describe('errors', () => { - it('should throw if a form isn\'t passed into formGroup', () => { const fixture = initTest(FormGroupComp); @@ -2335,11 +2299,9 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; expect(() => fixture.detectChanges()) .toThrowError(new RegExp('If you define both a name and a formControlName')); }); - }); describe('IME events', () => { - it('should determine IME event handling depending on platform by default', () => { const fixture = initTest(FormControlComp); fixture.componentInstance.control = new FormControl('oldValue'); @@ -2417,16 +2379,16 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec'; // formControl should update normally expect(fixture.componentInstance.control.value).toEqual('updatedValue'); }); - }); - }); } function uniqLoginAsyncValidator(expectedValue: string, timeout: number = 0) { return (c: AbstractControl) => { let resolve: (result: any) => void; - const promise = new Promise<any>(res => { resolve = res; }); + const promise = new Promise<any>(res => { + resolve = res; + }); const res = (c.value == expectedValue) ? null : {'uniqLogin': true}; setTimeout(() => resolve(res), timeout); return promise; @@ -2452,22 +2414,22 @@ class LoginIsEmptyValidator { @Directive({ selector: '[uniq-login-validator]', - providers: [{ - provide: NG_ASYNC_VALIDATORS, - useExisting: forwardRef(() => UniqLoginValidator), - multi: true - }] + providers: [ + {provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => UniqLoginValidator), multi: true} + ] }) class UniqLoginValidator implements AsyncValidator { @Input('uniq-login-validator') expected: any; - validate(c: AbstractControl) { return uniqLoginAsyncValidator(this.expected)(c); } + validate(c: AbstractControl) { + return uniqLoginAsyncValidator(this.expected)(c); + } } @Component({selector: 'form-control-comp', template: `<input type="text" [formControl]="control">`}) class FormControlComp { // TODO(issue/24571): remove '!'. - control !: FormControl; + control!: FormControl; } @Component({ @@ -2479,11 +2441,11 @@ class FormControlComp { }) class FormGroupComp { // TODO(issue/24571): remove '!'. - control !: FormControl; + control!: FormControl; // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; // TODO(issue/24571): remove '!'. - event !: Event; + event!: Event; } @Component({ @@ -2499,7 +2461,7 @@ class FormGroupComp { }) class NestedFormGroupComp { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; } @Component({ @@ -2515,9 +2477,9 @@ class NestedFormGroupComp { }) class FormArrayComp { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; // TODO(issue/24571): remove '!'. - cityArray !: FormArray; + cityArray!: FormArray; } @Component({ @@ -2534,9 +2496,9 @@ class FormArrayComp { }) class FormArrayNestedGroup { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; // TODO(issue/24571): remove '!'. - cityArray !: FormArray; + cityArray!: FormArray; } @Component({ @@ -2549,11 +2511,11 @@ class FormArrayNestedGroup { }) class FormGroupNgModel { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; // TODO(issue/24571): remove '!'. - login !: string; + login!: string; // TODO(issue/24571): remove '!'. - password !: string; + password!: string; } @Component({ @@ -2565,13 +2527,13 @@ class FormGroupNgModel { }) class FormControlNgModel { // TODO(issue/24571): remove '!'. - control !: FormControl; + control!: FormControl; // TODO(issue/24571): remove '!'. - login !: string; + login!: string; // TODO(issue/24571): remove '!'. - passwordControl !: FormControl; + passwordControl!: FormControl; // TODO(issue/24571): remove '!'. - password !: string; + password!: string; } @Component({ @@ -2586,7 +2548,7 @@ class FormControlNgModel { }) class LoginIsEmptyWrapper { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; } @Component({ @@ -2601,15 +2563,15 @@ class LoginIsEmptyWrapper { }) class ValidationBindingsForm { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; // TODO(issue/24571): remove '!'. - required !: boolean; + required!: boolean; // TODO(issue/24571): remove '!'. - minLen !: number; + minLen!: number; // TODO(issue/24571): remove '!'. - maxLen !: number; + maxLen!: number; // TODO(issue/24571): remove '!'. - pattern !: string; + pattern!: string; } @Component({ @@ -2618,7 +2580,7 @@ class ValidationBindingsForm { }) class FormControlCheckboxRequiredValidator { // TODO(issue/24571): remove '!'. - control !: FormControl; + control!: FormControl; } @Component({ @@ -2630,5 +2592,5 @@ class FormControlCheckboxRequiredValidator { }) class UniqLoginWrapper { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; } diff --git a/packages/forms/test/spies.ts b/packages/forms/test/spies.ts index 1f6b3940c7558..27bbcd0d98944 100644 --- a/packages/forms/test/spies.ts +++ b/packages/forms/test/spies.ts @@ -16,6 +16,10 @@ export class SpyChangeDetectorRef extends SpyObject { } } -export class SpyNgControl extends SpyObject { path = []; } +export class SpyNgControl extends SpyObject { + path = []; +} -export class SpyValueAccessor extends SpyObject { writeValue: any; } +export class SpyValueAccessor extends SpyObject { + writeValue: any; +} diff --git a/packages/forms/test/template_integration_spec.ts b/packages/forms/test/template_integration_spec.ts index a78ad61c26cf0..0cde9665d1c3d 100644 --- a/packages/forms/test/template_integration_spec.ts +++ b/packages/forms/test/template_integration_spec.ts @@ -7,8 +7,8 @@ */ import {ɵgetDOM as getDOM} from '@angular/common'; -import {Component, Directive, Type, forwardRef} from '@angular/core'; -import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing'; +import {Component, Directive, forwardRef, Type} from '@angular/core'; +import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; import {AbstractControl, AsyncValidator, COMPOSITION_BUFFER_MODE, FormControl, FormsModule, NG_ASYNC_VALIDATORS, NgForm, NgModel} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util'; @@ -18,7 +18,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat { describe('template-driven forms integration tests', () => { - function initTest<T>(component: Type<T>, ...directives: Type<any>[]): ComponentFixture<T> { TestBed.configureTestingModule( {declarations: [component, ...directives], imports: [FormsModule]}); @@ -57,7 +56,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat expect(form.valid).toBe(false); })); - it('should report properties which are written outside of template bindings', async() => { + it('should report properties which are written outside of template bindings', async () => { // For example ngModel writes to `checked` property programmatically // (template does not contain binding to `checked` explicitly) // https://github.com/angular/angular/issues/33695 @@ -131,9 +130,9 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - expect(form.control.get('name') !.value).toEqual({first: 'Nancy', last: 'Drew'}); - expect(form.control.get('name.first') !.value).toEqual('Nancy'); - expect(form.control.get('email') !.value).toEqual('some email'); + expect(form.control.get('name')!.value).toEqual({first: 'Nancy', last: 'Drew'}); + expect(form.control.get('name.first')!.value).toEqual('Nancy'); + expect(form.control.get('email')!.value).toEqual('some email'); })); it('should remove controls and control groups from form control model', fakeAsync(() => { @@ -145,7 +144,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - expect(form.control.get('email') !.value).toEqual('some email'); + expect(form.control.get('email')!.value).toEqual('some email'); expect(form.value).toEqual({name: {first: 'Nancy'}, email: 'some email'}); // should remove individual control successfully @@ -156,8 +155,8 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat expect(form.control.get('email')).toBe(null); expect(form.value).toEqual({name: {first: 'Nancy'}}); - expect(form.control.get('name') !.value).toEqual({first: 'Nancy'}); - expect(form.control.get('name.first') !.value).toEqual('Nancy'); + expect(form.control.get('name')!.value).toEqual({first: 'Nancy'}); + expect(form.control.get('name.first')!.value).toEqual('Nancy'); // should remove form group successfully fixture.componentInstance.groupShowing = false; @@ -192,7 +191,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat })); it('should set status classes with ngModel and async validators', fakeAsync(() => { - const fixture = initTest(NgModelAsyncValidation, NgAsyncValidator); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -252,7 +250,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat it('should not create a template-driven form when ngNoForm is used', () => { const fixture = initTest(NgNoFormComp); fixture.detectChanges(); - expect(fixture.debugElement.children[0].providerTokens !.length).toEqual(0); + expect(fixture.debugElement.children[0].providerTokens!.length).toEqual(0); }); it('should not add novalidate when ngNoForm is used', () => { @@ -304,9 +302,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat }); describe('updateOn', () => { - describe('blur', () => { - it('should default updateOn to change', fakeAsync(() => { const fixture = initTest(NgModelForm); fixture.componentInstance.name = ''; @@ -484,8 +480,8 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat const values: any[] = []; const form = fixture.debugElement.children[0].injector.get(NgForm); - const sub = merge(form.valueChanges !, form.statusChanges !) - .subscribe(val => values.push(val)); + const sub = + merge(form.valueChanges!, form.statusChanges!).subscribe(val => values.push(val)); const input = fixture.debugElement.query(By.css('input')).nativeElement; input.value = 'Nancy Drew'; @@ -560,13 +556,10 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat .toEqual( ['fired', 'fired'], 'Expected ngModelChanges to fire again on blur if value changed.'); - })); - }); describe('submit', () => { - it('should set control updateOn to submit properly', fakeAsync(() => { const fixture = initTest(NgModelForm); fixture.componentInstance.name = ''; @@ -700,8 +693,8 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - form.control.get('name') !.setValidators(groupValidatorSpy); - form.control.get('name.last') !.setValidators(validatorSpy); + form.control.get('name')!.setValidators(groupValidatorSpy); + form.control.get('name.last')!.setValidators(validatorSpy); const formEl = fixture.debugElement.query(By.css('form')).nativeElement; dispatchEvent(formEl, 'submit'); @@ -816,8 +809,8 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat const values: any[] = []; const form = fixture.debugElement.children[0].injector.get(NgForm); - const sub = merge(form.valueChanges !, form.statusChanges !) - .subscribe(val => values.push(val)); + const sub = + merge(form.valueChanges!, form.statusChanges!).subscribe(val => values.push(val)); const input = fixture.debugElement.query(By.css('input')).nativeElement; input.value = 'Nancy Drew'; @@ -898,11 +891,9 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat ['fired', 'fired'], 'Expected ngModelChanges to fire again on submit if value changed.'); })); - }); describe('ngFormOptions', () => { - it('should use ngFormOptions value when ngModelOptions are not set', fakeAsync(() => { const fixture = initTest(NgModelOptionsStandalone); fixture.componentInstance.options = {name: 'two'}; @@ -911,12 +902,12 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - const controlOne = form.control.get('one') !as FormControl; + const controlOne = form.control.get('one')! as FormControl; expect((controlOne as any)._updateOn).toBeUndefined(); expect(controlOne.updateOn) .toEqual('blur', 'Expected first control to inherit updateOn from parent form.'); - const controlTwo = form.control.get('two') !as FormControl; + const controlTwo = form.control.get('two')! as FormControl; expect((controlTwo as any)._updateOn).toBeUndefined(); expect(controlTwo.updateOn) .toEqual('blur', 'Expected last control to inherit updateOn from parent form.'); @@ -952,12 +943,12 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - const controlOne = form.control.get('one') !as FormControl; + const controlOne = form.control.get('one')! as FormControl; expect((controlOne as any)._updateOn).toBeUndefined(); expect(controlOne.updateOn) .toEqual('change', 'Expected control updateOn to inherit form updateOn.'); - const controlTwo = form.control.get('two') !as FormControl; + const controlTwo = form.control.get('two')! as FormControl; expect((controlTwo as any)._updateOn) .toEqual('blur', 'Expected control to set blur override.'); expect(controlTwo.updateOn) @@ -1016,15 +1007,13 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat expect(fixture.componentInstance.two) .toEqual('Nancy Drew', 'Expected standalone ngModel not to inherit blur update.'); })); - }); - }); describe('submit and reset events', () => { it('should emit ngSubmit event with the original submit event on submit', fakeAsync(() => { const fixture = initTest(NgModelForm); - fixture.componentInstance.event = null !; + fixture.componentInstance.event = null!; const form = fixture.debugElement.query(By.css('form')); dispatchEvent(form.nativeElement, 'submit'); @@ -1097,11 +1086,11 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat expect(form.valid).toEqual(true); expect(form.value).toEqual({}); - let formValidity: string = undefined !; - let formValue: Object = undefined !; + let formValidity: string = undefined!; + let formValue: Object = undefined!; - form.statusChanges !.subscribe((status: string) => formValidity = status); - form.valueChanges !.subscribe((value: string) => formValue = value); + form.statusChanges!.subscribe((status: string) => formValidity = status); + form.valueChanges!.subscribe((value: string) => formValue = value); tick(); @@ -1116,8 +1105,9 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat fixture.detectChanges(); tick(); - form.get('name') !.valueChanges.subscribe( - () => { expect(form.get('name') !.dirty).toBe(true); }); + form.get('name')!.valueChanges.subscribe(() => { + expect(form.get('name')!.dirty).toBe(true); + }); const inputEl = fixture.debugElement.query(By.css('input')).nativeElement; inputEl.value = 'newValue'; @@ -1138,10 +1128,11 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat inputEl.value = 'newValue'; dispatchEvent(inputEl, 'input'); - expect(form.get('name') !.pristine).toBe(false); + expect(form.get('name')!.pristine).toBe(false); - form.get('name') !.valueChanges.subscribe( - () => { expect(form.get('name') !.pristine).toBe(true); }); + form.get('name')!.valueChanges.subscribe(() => { + expect(form.get('name')!.pristine).toBe(true); + }); dispatchEvent(formEl, 'reset'); })); @@ -1160,7 +1151,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat const form = fixture.debugElement.children[0].injector.get(NgForm); expect(form.value).toEqual({name: {first: '', last: 'Drew'}, email: 'some email'}); expect(form.valid).toBe(false); - expect(form.control.get('name.first') !.disabled).toBe(false); + expect(form.control.get('name.first')!.disabled).toBe(false); fixture.componentInstance.isDisabled = true; fixture.detectChanges(); @@ -1168,7 +1159,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat expect(form.value).toEqual({name: {last: 'Drew'}, email: 'some email'}); expect(form.valid).toBe(true); - expect(form.control.get('name.first') !.disabled).toBe(true); + expect(form.control.get('name.first')!.disabled).toBe(true); })); it('should add disabled attribute in the UI if disable() is called programmatically', @@ -1180,7 +1171,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - form.control.get('name.first') !.disable(); + form.control.get('name.first')!.disable(); fixture.detectChanges(); tick(); @@ -1197,7 +1188,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat fixture.detectChanges(); fixture.whenStable().then(() => { const form = fixture.debugElement.children[0].injector.get(NgForm); - expect(form.control.get('name') !.disabled).toBe(true); + expect(form.control.get('name')!.disabled).toBe(true); const customInput = fixture.debugElement.query(By.css('[name="custom"]')); expect(customInput.nativeElement.disabled).toEqual(true); @@ -1219,7 +1210,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat fixture.detectChanges(); tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - expect(form.control.get('name') !.disabled).toBe(true); + expect(form.control.get('name')!.disabled).toBe(true); const input = fixture.debugElement.query(By.css('input')); expect(input.nativeElement.disabled).toEqual(true); @@ -1229,18 +1220,16 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat tick(); expect(input.nativeElement.disabled).toEqual(false); })); - }); describe('validation directives', () => { - it('required validator should validate checkbox', fakeAsync(() => { const fixture = initTest(NgModelCheckboxRequiredValidator); fixture.detectChanges(); tick(); const control = - fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !; + fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox')!; const input = fixture.debugElement.query(By.css('input')); expect(input.nativeElement.checked).toBe(false); @@ -1276,7 +1265,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat tick(); const control = - fixture.debugElement.children[0].injector.get(NgForm).control.get('email') !; + fixture.debugElement.children[0].injector.get(NgForm).control.get('email')!; const input = fixture.debugElement.query(By.css('input')); expect(control.hasError('email')).toBe(false); @@ -1476,9 +1465,9 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat .toEqual(pattern.nativeElement.getAttribute('pattern')); fixture.componentInstance.required = false; - fixture.componentInstance.minLen = null !; - fixture.componentInstance.maxLen = null !; - fixture.componentInstance.pattern = null !; + fixture.componentInstance.minLen = null!; + fixture.componentInstance.maxLen = null!; + fixture.componentInstance.pattern = null!; fixture.detectChanges(); expect(form.control.hasError('required', ['required'])).toEqual(false); @@ -1522,7 +1511,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat expect(onNgModelChange).toHaveBeenCalledTimes(2); tick(); })); - }); describe('IME events', () => { @@ -1611,7 +1599,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat // ngModel should update normally expect(fixture.componentInstance.name).toEqual('updatedValue'); })); - }); describe('ngModel corner cases', () => { @@ -1650,7 +1637,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat expect(() => fixture.detectChanges()).not.toThrowError(); })); }); - }); } @@ -1662,7 +1648,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat }) class StandaloneNgModel { // TODO(issue/24571): remove '!'. - name !: string; + name!: string; } @Component({ @@ -1675,9 +1661,9 @@ class StandaloneNgModel { }) class NgModelForm { // TODO(issue/24571): remove '!'. - name !: string | null; + name!: string|null; // TODO(issue/24571): remove '!'. - event !: Event; + event!: Event; options = {}; onReset() {} @@ -1701,13 +1687,13 @@ class NgModelNativeValidateForm { }) class NgModelGroupForm { // TODO(issue/24571): remove '!'. - first !: string; + first!: string; // TODO(issue/24571): remove '!'. - last !: string; + last!: string; // TODO(issue/24571): remove '!'. - email !: string; + email!: string; // TODO(issue/24571): remove '!'. - isDisabled !: boolean; + isDisabled!: boolean; options = {updateOn: 'change'}; } @@ -1724,7 +1710,7 @@ class NgModelGroupForm { }) class NgModelValidBinding { // TODO(issue/24571): remove '!'. - first !: string; + first!: string; } @@ -1741,11 +1727,11 @@ class NgModelValidBinding { }) class NgModelNgIfForm { // TODO(issue/24571): remove '!'. - first !: string; + first!: string; groupShowing = true; emailShowing = true; // TODO(issue/24571): remove '!'. - email !: string; + email!: string; } @Component({ @@ -1781,9 +1767,9 @@ class InvalidNgModelNoName { }) class NgModelOptionsStandalone { // TODO(issue/24571): remove '!'. - one !: string; + one!: string; // TODO(issue/24571): remove '!'. - two !: string; + two!: string; options: {name?: string, standalone?: boolean, updateOn?: string} = {standalone: true}; formOptions = {}; } @@ -1801,13 +1787,13 @@ class NgModelOptionsStandalone { }) class NgModelValidationBindings { // TODO(issue/24571): remove '!'. - required !: boolean; + required!: boolean; // TODO(issue/24571): remove '!'. - minLen !: number; + minLen!: number; // TODO(issue/24571): remove '!'. - maxLen !: number; + maxLen!: number; // TODO(issue/24571): remove '!'. - pattern !: string; + pattern!: string; } @Component({ @@ -1820,11 +1806,11 @@ class NgModelValidationBindings { }) class NgModelMultipleValidators { // TODO(issue/24571): remove '!'. - required !: boolean; + required!: boolean; // TODO(issue/24571): remove '!'. - minLen !: number; + minLen!: number; // TODO(issue/24571): remove '!'. - pattern !: string | RegExp; + pattern!: string|RegExp; } @Component({ @@ -1847,12 +1833,13 @@ class NgModelEmailValidator { @Directive({ selector: '[ng-async-validator]', - providers: [ - {provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => NgAsyncValidator), multi: true} - ] + providers: + [{provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => NgAsyncValidator), multi: true}] }) class NgAsyncValidator implements AsyncValidator { - validate(c: AbstractControl) { return Promise.resolve(null); } + validate(c: AbstractControl) { + return Promise.resolve(null); + } } @Component({ @@ -1873,11 +1860,13 @@ class NgModelAsyncValidation { }) class NgModelChangesForm { // TODO(issue/24571): remove '!'. - name !: string; + name!: string; events: string[] = []; options: any; - log() { this.events.push('fired'); } + log() { + this.events.push('fired'); + } } @Component({ diff --git a/packages/forms/test/validators_spec.ts b/packages/forms/test/validators_spec.ts index 0d3f1189655ae..0a6719421b8d1 100644 --- a/packages/forms/test/validators_spec.ts +++ b/packages/forms/test/validators_spec.ts @@ -11,453 +11,487 @@ import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {AbstractControl, AsyncValidatorFn, FormArray, FormControl, Validators} from '@angular/forms'; import {normalizeAsyncValidator} from '@angular/forms/src/directives/normalize_validator'; import {AsyncValidator, ValidationErrors, ValidatorFn} from '@angular/forms/src/directives/validators'; -import {Observable, of , timer} from 'rxjs'; +import {Observable, of, timer} from 'rxjs'; import {first, map} from 'rxjs/operators'; (function() { - function validator(key: string, error: any): ValidatorFn { - return (c: AbstractControl) => { - const r: ValidationErrors = {}; - r[key] = error; - return r; - }; +function validator(key: string, error: any): ValidatorFn { + return (c: AbstractControl) => { + const r: ValidationErrors = {}; + r[key] = error; + return r; + }; +} + +class AsyncValidatorDirective implements AsyncValidator { + constructor(private expected: string, private error: any) {} + + validate(c: any): Observable<ValidationErrors> { + return Observable.create((obs: any) => { + const error = this.expected !== c.value ? this.error : null; + obs.next(error); + obs.complete(); + }); } +} - class AsyncValidatorDirective implements AsyncValidator { - constructor(private expected: string, private error: any) {} +describe('Validators', () => { + describe('min', () => { + it('should not error on an empty string', () => { + expect(Validators.min(2)(new FormControl(''))).toBeNull(); + }); - validate(c: any): Observable<ValidationErrors> { - return Observable.create((obs: any) => { - const error = this.expected !== c.value ? this.error : null; - obs.next(error); - obs.complete(); - }); - } - } + it('should not error on null', () => { + expect(Validators.min(2)(new FormControl(null))).toBeNull(); + }); - describe('Validators', () => { - describe('min', () => { - it('should not error on an empty string', - () => { expect(Validators.min(2)(new FormControl(''))).toBeNull(); }); + it('should not error on undefined', () => { + expect(Validators.min(2)(new FormControl(undefined))).toBeNull(); + }); - it('should not error on null', - () => { expect(Validators.min(2)(new FormControl(null))).toBeNull(); }); + it('should return null if NaN after parsing', () => { + expect(Validators.min(2)(new FormControl('a'))).toBeNull(); + }); - it('should not error on undefined', - () => { expect(Validators.min(2)(new FormControl(undefined))).toBeNull(); }); + it('should return a validation error on small values', () => { + expect(Validators.min(2)(new FormControl(1))).toEqual({'min': {'min': 2, 'actual': 1}}); + }); - it('should return null if NaN after parsing', - () => { expect(Validators.min(2)(new FormControl('a'))).toBeNull(); }); + it('should return a validation error on small values converted from strings', () => { + expect(Validators.min(2)(new FormControl('1'))).toEqual({'min': {'min': 2, 'actual': '1'}}); + }); - it('should return a validation error on small values', () => { - expect(Validators.min(2)(new FormControl(1))).toEqual({'min': {'min': 2, 'actual': 1}}); - }); + it('should not error on big values', () => { + expect(Validators.min(2)(new FormControl(3))).toBeNull(); + }); + + it('should not error on equal values', () => { + expect(Validators.min(2)(new FormControl(2))).toBeNull(); + }); - it('should return a validation error on small values converted from strings', () => { - expect(Validators.min(2)(new FormControl('1'))).toEqual({'min': {'min': 2, 'actual': '1'}}); + it('should not error on equal values when value is string', () => { + expect(Validators.min(2)(new FormControl('2'))).toBeNull(); + }); + + it('should validate as expected when min value is a string', () => { + expect(Validators.min('2' as any)(new FormControl(1))).toEqual({ + 'min': {'min': '2', 'actual': 1} }); + }); - it('should not error on big values', - () => { expect(Validators.min(2)(new FormControl(3))).toBeNull(); }); + it('should return null if min value is undefined', () => { + expect(Validators.min(undefined as any)(new FormControl(3))).toBeNull(); + }); + + it('should return null if min value is null', () => { + expect(Validators.min(null as any)(new FormControl(3))).toBeNull(); + }); + }); - it('should not error on equal values', - () => { expect(Validators.min(2)(new FormControl(2))).toBeNull(); }); + describe('max', () => { + it('should not error on an empty string', () => { + expect(Validators.max(2)(new FormControl(''))).toBeNull(); + }); - it('should not error on equal values when value is string', - () => { expect(Validators.min(2)(new FormControl('2'))).toBeNull(); }); + it('should not error on null', () => { + expect(Validators.max(2)(new FormControl(null))).toBeNull(); + }); - it('should validate as expected when min value is a string', () => { - expect(Validators.min('2' as any)(new FormControl(1))).toEqual({ - 'min': {'min': '2', 'actual': 1} - }); - }); + it('should not error on undefined', () => { + expect(Validators.max(2)(new FormControl(undefined))).toBeNull(); + }); - it('should return null if min value is undefined', - () => { expect(Validators.min(undefined as any)(new FormControl(3))).toBeNull(); }); + it('should return null if NaN after parsing', () => { + expect(Validators.max(2)(new FormControl('aaa'))).toBeNull(); + }); - it('should return null if min value is null', - () => { expect(Validators.min(null as any)(new FormControl(3))).toBeNull(); }); + it('should return a validation error on big values', () => { + expect(Validators.max(2)(new FormControl(3))).toEqual({'max': {'max': 2, 'actual': 3}}); }); - describe('max', () => { - it('should not error on an empty string', - () => { expect(Validators.max(2)(new FormControl(''))).toBeNull(); }); + it('should return a validation error on big values converted from strings', () => { + expect(Validators.max(2)(new FormControl('3'))).toEqual({'max': {'max': 2, 'actual': '3'}}); + }); - it('should not error on null', - () => { expect(Validators.max(2)(new FormControl(null))).toBeNull(); }); + it('should not error on small values', () => { + expect(Validators.max(2)(new FormControl(1))).toBeNull(); + }); - it('should not error on undefined', - () => { expect(Validators.max(2)(new FormControl(undefined))).toBeNull(); }); + it('should not error on equal values', () => { + expect(Validators.max(2)(new FormControl(2))).toBeNull(); + }); - it('should return null if NaN after parsing', - () => { expect(Validators.max(2)(new FormControl('aaa'))).toBeNull(); }); + it('should not error on equal values when value is string', () => { + expect(Validators.max(2)(new FormControl('2'))).toBeNull(); + }); - it('should return a validation error on big values', () => { - expect(Validators.max(2)(new FormControl(3))).toEqual({'max': {'max': 2, 'actual': 3}}); + it('should validate as expected when max value is a string', () => { + expect(Validators.max('2' as any)(new FormControl(3))).toEqual({ + 'max': {'max': '2', 'actual': 3} }); + }); - it('should return a validation error on big values converted from strings', () => { - expect(Validators.max(2)(new FormControl('3'))).toEqual({'max': {'max': 2, 'actual': '3'}}); - }); + it('should return null if max value is undefined', () => { + expect(Validators.max(undefined as any)(new FormControl(3))).toBeNull(); + }); - it('should not error on small values', - () => { expect(Validators.max(2)(new FormControl(1))).toBeNull(); }); + it('should return null if max value is null', () => { + expect(Validators.max(null as any)(new FormControl(3))).toBeNull(); + }); + }); - it('should not error on equal values', - () => { expect(Validators.max(2)(new FormControl(2))).toBeNull(); }); - it('should not error on equal values when value is string', - () => { expect(Validators.max(2)(new FormControl('2'))).toBeNull(); }); + describe('required', () => { + it('should error on an empty string', () => { + expect(Validators.required(new FormControl(''))).toEqual({'required': true}); + }); - it('should validate as expected when max value is a string', () => { - expect(Validators.max('2' as any)(new FormControl(3))).toEqual({ - 'max': {'max': '2', 'actual': 3} - }); - }); + it('should error on null', () => { + expect(Validators.required(new FormControl(null))).toEqual({'required': true}); + }); - it('should return null if max value is undefined', - () => { expect(Validators.max(undefined as any)(new FormControl(3))).toBeNull(); }); + it('should not error on undefined', () => { + expect(Validators.required(new FormControl(undefined))).toEqual({'required': true}); + }); - it('should return null if max value is null', - () => { expect(Validators.max(null as any)(new FormControl(3))).toBeNull(); }); + it('should not error on a non-empty string', () => { + expect(Validators.required(new FormControl('not empty'))).toBeNull(); }); + it('should accept zero as valid', () => { + expect(Validators.required(new FormControl(0))).toBeNull(); + }); - describe('required', () => { - it('should error on an empty string', - () => { expect(Validators.required(new FormControl(''))).toEqual({'required': true}); }); + it('should error on an empty array', + () => expect(Validators.required(new FormControl([]))).toEqual({'required': true})); - it('should error on null', - () => { expect(Validators.required(new FormControl(null))).toEqual({'required': true}); }); + it('should not error on a non-empty array', + () => expect(Validators.required(new FormControl([1, 2]))).toBeNull()); + }); - it('should not error on undefined', () => { - expect(Validators.required(new FormControl(undefined))).toEqual({'required': true}); - }); + describe('requiredTrue', () => { + it('should error on false', + () => expect(Validators.requiredTrue(new FormControl(false))).toEqual({'required': true})); + + it('should not error on true', + () => expect(Validators.requiredTrue(new FormControl(true))).toBeNull()); + }); + + describe('email', () => { + it('should not error on an empty string', + () => expect(Validators.email(new FormControl(''))).toBeNull()); - it('should not error on a non-empty string', - () => { expect(Validators.required(new FormControl('not empty'))).toBeNull(); }); + it('should not error on null', + () => expect(Validators.email(new FormControl(null))).toBeNull()); - it('should accept zero as valid', - () => { expect(Validators.required(new FormControl(0))).toBeNull(); }); + it('should error on invalid email', + () => expect(Validators.email(new FormControl('some text'))).toEqual({'email': true})); - it('should error on an empty array', - () => expect(Validators.required(new FormControl([]))).toEqual({'required': true})); + it('should not error on valid email', + () => expect(Validators.email(new FormControl('test@gmail.com'))).toBeNull()); + }); - it('should not error on a non-empty array', - () => expect(Validators.required(new FormControl([1, 2]))).toBeNull()); + describe('minLength', () => { + it('should not error on an empty string', () => { + expect(Validators.minLength(2)(new FormControl(''))).toBeNull(); }); - describe('requiredTrue', () => { - it('should error on false', - () => expect(Validators.requiredTrue(new FormControl(false))).toEqual({'required': true})); + it('should not error on null', () => { + expect(Validators.minLength(2)(new FormControl(null))).toBeNull(); + }); - it('should not error on true', - () => expect(Validators.requiredTrue(new FormControl(true))).toBeNull()); + it('should not error on undefined', () => { + expect(Validators.minLength(2)(new FormControl(undefined))).toBeNull(); }); - describe('email', () => { - it('should not error on an empty string', - () => expect(Validators.email(new FormControl(''))).toBeNull()); + it('should not error on valid strings', () => { + expect(Validators.minLength(2)(new FormControl('aa'))).toBeNull(); + }); - it('should not error on null', - () => expect(Validators.email(new FormControl(null))).toBeNull()); + it('should error on short strings', () => { + expect(Validators.minLength(2)(new FormControl('a'))).toEqual({ + 'minlength': {'requiredLength': 2, 'actualLength': 1} + }); + }); - it('should error on invalid email', - () => expect(Validators.email(new FormControl('some text'))).toEqual({'email': true})); + it('should not error when FormArray has valid length', () => { + const fa = new FormArray([new FormControl(''), new FormControl('')]); + expect(Validators.minLength(2)(fa)).toBeNull(); + }); - it('should not error on valid email', - () => expect(Validators.email(new FormControl('test@gmail.com'))).toBeNull()); + it('should error when FormArray has invalid length', () => { + const fa = new FormArray([new FormControl('')]); + expect(Validators.minLength(2)(fa)).toEqual({ + 'minlength': {'requiredLength': 2, 'actualLength': 1} + }); }); + }); - describe('minLength', () => { - it('should not error on an empty string', - () => { expect(Validators.minLength(2)(new FormControl(''))).toBeNull(); }); + describe('maxLength', () => { + it('should not error on an empty string', () => { + expect(Validators.maxLength(2)(new FormControl(''))).toBeNull(); + }); - it('should not error on null', - () => { expect(Validators.minLength(2)(new FormControl(null))).toBeNull(); }); + it('should not error on null', () => { + expect(Validators.maxLength(2)(new FormControl(null))).toBeNull(); + }); - it('should not error on undefined', - () => { expect(Validators.minLength(2)(new FormControl(undefined))).toBeNull(); }); + it('should not error on undefined', () => { + expect(Validators.maxLength(2)(new FormControl(undefined))).toBeNull(); + }); - it('should not error on valid strings', - () => { expect(Validators.minLength(2)(new FormControl('aa'))).toBeNull(); }); + it('should not error on valid strings', () => { + expect(Validators.maxLength(2)(new FormControl('aa'))).toBeNull(); + }); - it('should error on short strings', () => { - expect(Validators.minLength(2)(new FormControl('a'))).toEqual({ - 'minlength': {'requiredLength': 2, 'actualLength': 1} - }); + it('should error on long strings', () => { + expect(Validators.maxLength(2)(new FormControl('aaa'))).toEqual({ + 'maxlength': {'requiredLength': 2, 'actualLength': 3} }); + }); - it('should not error when FormArray has valid length', () => { - const fa = new FormArray([new FormControl(''), new FormControl('')]); - expect(Validators.minLength(2)(fa)).toBeNull(); - }); + it('should not error when FormArray has valid length', () => { + const fa = new FormArray([new FormControl(''), new FormControl('')]); + expect(Validators.maxLength(2)(fa)).toBeNull(); + }); - it('should error when FormArray has invalid length', () => { - const fa = new FormArray([new FormControl('')]); - expect(Validators.minLength(2)(fa)).toEqual({ - 'minlength': {'requiredLength': 2, 'actualLength': 1} - }); + it('should error when FormArray has invalid length', () => { + const fa = new FormArray([new FormControl(''), new FormControl('')]); + expect(Validators.maxLength(1)(fa)).toEqual({ + 'maxlength': {'requiredLength': 1, 'actualLength': 2} }); }); + }); - describe('maxLength', () => { - it('should not error on an empty string', - () => { expect(Validators.maxLength(2)(new FormControl(''))).toBeNull(); }); + describe('pattern', () => { + it('should not error on an empty string', () => { + expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(''))).toBeNull(); + }); - it('should not error on null', - () => { expect(Validators.maxLength(2)(new FormControl(null))).toBeNull(); }); + it('should not error on null', () => { + expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(null))).toBeNull(); + }); - it('should not error on undefined', - () => { expect(Validators.maxLength(2)(new FormControl(undefined))).toBeNull(); }); + it('should not error on undefined', () => { + expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(undefined))).toBeNull(); + }); - it('should not error on valid strings', - () => { expect(Validators.maxLength(2)(new FormControl('aa'))).toBeNull(); }); + it('should not error on null value and "null" pattern', () => { + expect(Validators.pattern('null')(new FormControl(null))).toBeNull(); + }); - it('should error on long strings', () => { - expect(Validators.maxLength(2)(new FormControl('aaa'))).toEqual({ - 'maxlength': {'requiredLength': 2, 'actualLength': 3} - }); - }); + it('should not error on valid strings', + () => expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull()); - it('should not error when FormArray has valid length', () => { - const fa = new FormArray([new FormControl(''), new FormControl('')]); - expect(Validators.maxLength(2)(fa)).toBeNull(); + it('should error on failure to match string', () => { + expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({ + 'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'} }); + }); - it('should error when FormArray has invalid length', () => { - const fa = new FormArray([new FormControl(''), new FormControl('')]); - expect(Validators.maxLength(1)(fa)).toEqual({ - 'maxlength': {'requiredLength': 1, 'actualLength': 2} - }); + it('should accept RegExp object', () => { + const pattern: RegExp = new RegExp('[a-zA-Z ]+'); + expect(Validators.pattern(pattern)(new FormControl('aaAA'))).toBeNull(); + }); + + it('should error on failure to match RegExp object', () => { + const pattern: RegExp = new RegExp('^[a-zA-Z ]*$'); + expect(Validators.pattern(pattern)(new FormControl('aaa0'))).toEqual({ + 'pattern': {'requiredPattern': '/^[a-zA-Z ]*$/', 'actualValue': 'aaa0'} }); }); - describe('pattern', () => { - it('should not error on an empty string', - () => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(''))).toBeNull(); }); + it('should not error on "null" pattern', + () => expect(Validators.pattern(null!)(new FormControl('aaAA'))).toBeNull()); - it('should not error on null', - () => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(null))).toBeNull(); }); + it('should not error on "undefined" pattern', + () => expect(Validators.pattern(undefined!)(new FormControl('aaAA'))).toBeNull()); - it('should not error on undefined', () => { - expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(undefined))).toBeNull(); - }); + it('should work with pattern string containing both boundary symbols', + () => expect(Validators.pattern('^[aA]*$')(new FormControl('aaAA'))).toBeNull()); - it('should not error on null value and "null" pattern', - () => { expect(Validators.pattern('null')(new FormControl(null))).toBeNull(); }); + it('should work with pattern string containing only start boundary symbols', + () => expect(Validators.pattern('^[aA]*')(new FormControl('aaAA'))).toBeNull()); - it('should not error on valid strings', - () => expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull()); + it('should work with pattern string containing only end boundary symbols', + () => expect(Validators.pattern('[aA]*$')(new FormControl('aaAA'))).toBeNull()); - it('should error on failure to match string', () => { - expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({ - 'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'} - }); - }); + it('should work with pattern string not containing any boundary symbols', + () => expect(Validators.pattern('[aA]*')(new FormControl('aaAA'))).toBeNull()); + }); - it('should accept RegExp object', () => { - const pattern: RegExp = new RegExp('[a-zA-Z ]+'); - expect(Validators.pattern(pattern)(new FormControl('aaAA'))).toBeNull(); - }); + describe('compose', () => { + it('should return null when given null', () => { + expect(Validators.compose(null!)).toBe(null); + }); - it('should error on failure to match RegExp object', () => { - const pattern: RegExp = new RegExp('^[a-zA-Z ]*$'); - expect(Validators.pattern(pattern)(new FormControl('aaa0'))).toEqual({ - 'pattern': {'requiredPattern': '/^[a-zA-Z ]*$/', 'actualValue': 'aaa0'} - }); - }); + it('should collect errors from all the validators', () => { + const c = Validators.compose([validator('a', true), validator('b', true)])!; + expect(c(new FormControl(''))).toEqual({'a': true, 'b': true}); + }); - it('should not error on "null" pattern', - () => expect(Validators.pattern(null !)(new FormControl('aaAA'))).toBeNull()); + it('should run validators left to right', () => { + const c = Validators.compose([validator('a', 1), validator('a', 2)])!; + expect(c(new FormControl(''))).toEqual({'a': 2}); + }); - it('should not error on "undefined" pattern', - () => expect(Validators.pattern(undefined !)(new FormControl('aaAA'))).toBeNull()); + it('should return null when no errors', () => { + const c = Validators.compose([Validators.nullValidator, Validators.nullValidator])!; + expect(c(new FormControl(''))).toBeNull(); + }); - it('should work with pattern string containing both boundary symbols', - () => expect(Validators.pattern('^[aA]*$')(new FormControl('aaAA'))).toBeNull()); + it('should ignore nulls', () => { + const c = Validators.compose([null!, Validators.required])!; + expect(c(new FormControl(''))).toEqual({'required': true}); + }); + }); - it('should work with pattern string containing only start boundary symbols', - () => expect(Validators.pattern('^[aA]*')(new FormControl('aaAA'))).toBeNull()); + describe('composeAsync', () => { + describe('promises', () => { + function promiseValidator(response: {[key: string]: any}): AsyncValidatorFn { + return (c: AbstractControl) => { + const res = c.value != 'expected' ? response : null; + return Promise.resolve(res); + }; + } + + it('should return null when given null', () => { + expect(Validators.composeAsync(null!)).toBeNull(); + }); - it('should work with pattern string containing only end boundary symbols', - () => expect(Validators.pattern('[aA]*$')(new FormControl('aaAA'))).toBeNull()); + it('should collect errors from all the validators', fakeAsync(() => { + const v = Validators.composeAsync( + [promiseValidator({'one': true}), promiseValidator({'two': true})])!; - it('should work with pattern string not containing any boundary symbols', - () => expect(Validators.pattern('[aA]*')(new FormControl('aaAA'))).toBeNull()); - }); + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors); + tick(); - describe('compose', () => { - it('should return null when given null', - () => { expect(Validators.compose(null !)).toBe(null); }); + expect(errorMap!).toEqual({'one': true, 'two': true}); + })); - it('should collect errors from all the validators', () => { - const c = Validators.compose([validator('a', true), validator('b', true)]) !; - expect(c(new FormControl(''))).toEqual({'a': true, 'b': true}); - }); + it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => { + const v = Validators.composeAsync( + [normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))])!; - it('should run validators left to right', () => { - const c = Validators.compose([validator('a', 1), validator('a', 2)]) !; - expect(c(new FormControl(''))).toEqual({'a': 2}); - }); + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors); + tick(); - it('should return null when no errors', () => { - const c = Validators.compose([Validators.nullValidator, Validators.nullValidator]) !; - expect(c(new FormControl(''))).toBeNull(); - }); + expect(errorMap!).toEqual({'one': true}); + })); - it('should ignore nulls', () => { - const c = Validators.compose([null !, Validators.required]) !; - expect(c(new FormControl(''))).toEqual({'required': true}); - }); - }); + it('should return null when no errors', fakeAsync(() => { + const v = Validators.composeAsync([promiseValidator({'one': true})])!; + + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('expected')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors); + tick(); - describe('composeAsync', () => { + expect(errorMap).toBeNull(); + })); - describe('promises', () => { - function promiseValidator(response: {[key: string]: any}): AsyncValidatorFn { - return (c: AbstractControl) => { - const res = c.value != 'expected' ? response : null; - return Promise.resolve(res); - }; - } + it('should ignore nulls', fakeAsync(() => { + const v = Validators.composeAsync([promiseValidator({'one': true}), null!])!; - it('should return null when given null', - () => { expect(Validators.composeAsync(null !)).toBeNull(); }); + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors); + tick(); - it('should collect errors from all the validators', fakeAsync(() => { - const v = Validators.composeAsync( - [promiseValidator({'one': true}), promiseValidator({'two': true})]) !; + expect(errorMap!).toEqual({'one': true}); + })); + }); - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors); - tick(); + describe('observables', () => { + function observableValidator(response: {[key: string]: any}): AsyncValidatorFn { + return (c: AbstractControl) => { + const res = c.value != 'expected' ? response : null; + return of(res); + }; + } - expect(errorMap !).toEqual({'one': true, 'two': true}); - })); + it('should return null when given null', () => { + expect(Validators.composeAsync(null!)).toBeNull(); + }); - it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => { - const v = Validators.composeAsync([normalizeAsyncValidator( - new AsyncValidatorDirective('expected', {'one': true}))]) !; + it('should collect errors from all the validators', () => { + const v = Validators.composeAsync( + [observableValidator({'one': true}), observableValidator({'two': true})])!; - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors); - tick(); + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors); - expect(errorMap !).toEqual({'one': true}); - })); + expect(errorMap!).toEqual({'one': true, 'two': true}); + }); - it('should return null when no errors', fakeAsync(() => { - const v = Validators.composeAsync([promiseValidator({'one': true})]) !; + it('should normalize and evaluate async validator-directives correctly', () => { + const v = Validators.composeAsync( + [normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))])!; - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('expected')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors); - tick(); + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors)!; - expect(errorMap).toBeNull(); - })); + expect(errorMap!).toEqual({'one': true}); + }); - it('should ignore nulls', fakeAsync(() => { - const v = Validators.composeAsync([promiseValidator({'one': true}), null !]) !; + it('should return null when no errors', () => { + const v = Validators.composeAsync([observableValidator({'one': true})])!; - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors); - tick(); + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('expected')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors); - expect(errorMap !).toEqual({'one': true}); - })); + expect(errorMap).toBeNull(); }); - describe('observables', () => { - function observableValidator(response: {[key: string]: any}): AsyncValidatorFn { - return (c: AbstractControl) => { - const res = c.value != 'expected' ? response : null; - return of (res); - }; - } - - it('should return null when given null', - () => { expect(Validators.composeAsync(null !)).toBeNull(); }); - - it('should collect errors from all the validators', () => { - const v = Validators.composeAsync( - [observableValidator({'one': true}), observableValidator({'two': true})]) !; - - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors); - - expect(errorMap !).toEqual({'one': true, 'two': true}); - }); - - it('should normalize and evaluate async validator-directives correctly', () => { - const v = Validators.composeAsync( - [normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]) !; - - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors) !; - - expect(errorMap !).toEqual({'one': true}); - }); - - it('should return null when no errors', () => { - const v = Validators.composeAsync([observableValidator({'one': true})]) !; - - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('expected')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors); - - expect(errorMap).toBeNull(); - }); - - it('should ignore nulls', () => { - const v = Validators.composeAsync([observableValidator({'one': true}), null !]) !; - - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors); - - expect(errorMap !).toEqual({'one': true}); - }); - - it('should wait for all validators before setting errors', fakeAsync(() => { - function getTimerObs(time: number, errorMap: {[key: string]: any}): AsyncValidatorFn { - return (c: AbstractControl) => { return timer(time).pipe(map(() => errorMap)); }; - } - - const v = Validators.composeAsync( - [getTimerObs(100, {one: true}), getTimerObs(200, {two: true})]) !; - - let errorMap: {[key: string]: any}|null = undefined !; - (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) - .pipe(first()) - .subscribe((errors: {[key: string]: any} | null) => errorMap = errors); - - tick(100); - expect(errorMap).not.toBeDefined( - `Expected errors not to be set until all validators came back.`); - - tick(100); - expect(errorMap !) - .toEqual( - {one: true, two: true}, - `Expected errors to merge once all validators resolved.`); - })); + it('should ignore nulls', () => { + const v = Validators.composeAsync([observableValidator({'one': true}), null!])!; + + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors); + + expect(errorMap!).toEqual({'one': true}); }); + it('should wait for all validators before setting errors', fakeAsync(() => { + function getTimerObs(time: number, errorMap: {[key: string]: any}): AsyncValidatorFn { + return (c: AbstractControl) => { + return timer(time).pipe(map(() => errorMap)); + }; + } + + const v = Validators.composeAsync( + [getTimerObs(100, {one: true}), getTimerObs(200, {two: true})])!; + + let errorMap: {[key: string]: any}|null = undefined!; + (v(new FormControl('invalid')) as Observable<ValidationErrors|null>) + .pipe(first()) + .subscribe((errors: {[key: string]: any}|null) => errorMap = errors); + + tick(100); + expect(errorMap).not.toBeDefined( + `Expected errors not to be set until all validators came back.`); + + tick(100); + expect(errorMap!).toEqual( + {one: true, two: true}, `Expected errors to merge once all validators resolved.`); + })); }); }); +}); })(); diff --git a/packages/forms/test/value_accessor_integration_spec.ts b/packages/forms/test/value_accessor_integration_spec.ts index 7eac63877db49..852e04bde4cb6 100644 --- a/packages/forms/test/value_accessor_integration_spec.ts +++ b/packages/forms/test/value_accessor_integration_spec.ts @@ -7,14 +7,13 @@ */ import {Component, Directive, EventEmitter, Input, Output, Type, ViewChild} from '@angular/core'; -import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing'; +import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; import {AbstractControl, ControlValueAccessor, FormControl, FormGroup, FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgForm, NgModel, ReactiveFormsModule, Validators} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'; { describe('value accessors', () => { - function initTest<T>(component: Type<T>, ...directives: Type<any>[]): ComponentFixture<T> { TestBed.configureTestingModule( {declarations: [component, ...directives], imports: [FormsModule, ReactiveFormsModule]}); @@ -64,7 +63,11 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' fixture.detectChanges(); const input = fixture.debugElement.query(By.css('input')); - form.valueChanges.subscribe({next: (value) => { throw 'Should not happen'; }}); + form.valueChanges.subscribe({ + next: (value) => { + throw 'Should not happen'; + } + }); input.nativeElement.value = 'updatedValue'; dispatchEvent(input.nativeElement, 'change'); @@ -160,9 +163,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' }); describe('select controls', () => { - describe('in reactive forms', () => { - it(`should support primitive values`, () => { if (isNode) return; const fixture = initTest(FormControlNameSelect); @@ -197,7 +198,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' it('should throw an error if compareWith is not a function', () => { const fixture = initTest(FormControlSelectWithCompareFn); - fixture.componentInstance.compareFn = null !; + fixture.componentInstance.compareFn = null!; expect(() => fixture.detectChanges()) .toThrowError(/compareWith must be a function, but received null/); }); @@ -238,7 +239,6 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' expect(select.nativeElement.value).toEqual('3: Object'); expect(nyOption.nativeElement.selected).toBe(true); }); - }); describe('in template-driven forms', () => { @@ -336,7 +336,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' dispatchEvent(select.nativeElement, 'change'); fixture.detectChanges(); tick(); - expect(comp.selectedCity !['name']).toEqual('NYC'); + expect(comp.selectedCity!['name']).toEqual('NYC'); select.nativeElement.value = '0: null'; dispatchEvent(select.nativeElement, 'change'); @@ -348,7 +348,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' it('should throw an error when compareWith is not a function', () => { const fixture = initTest(NgModelSelectWithCustomCompareFnForm); const comp = fixture.componentInstance; - comp.compareFn = null !; + comp.compareFn = null!; expect(() => fixture.detectChanges()) .toThrowError(/compareWith must be a function, but received null/); }); @@ -398,16 +398,11 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' expect(select.nativeElement.value).toEqual('3: Object'); expect(nyOption.nativeElement.selected).toBe(true); })); - - }); - }); describe('select multiple controls', () => { - describe('in reactive forms', () => { - it('should support primitive values', () => { if (isNode) return; const fixture = initTest(FormControlSelectMultiple); @@ -432,7 +427,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' it('should throw an error when compareWith is not a function', () => { const fixture = initTest(FormControlSelectMultipleWithCompareFn); - fixture.componentInstance.compareFn = null !; + fixture.componentInstance.compareFn = null!; expect(() => fixture.detectChanges()) .toThrowError(/compareWith must be a function, but received null/); }); @@ -448,7 +443,6 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' expect(select.nativeElement.value).toEqual('0: Object'); expect(sfOption.nativeElement.selected).toBe(true); })); - }); describe('in template-driven forms', () => { @@ -520,7 +514,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' it('should throw an error when compareWith is not a function', () => { const fixture = initTest(NgModelSelectMultipleWithCustomCompareFnForm); const comp = fixture.componentInstance; - comp.compareFn = null !; + comp.compareFn = null!; expect(() => fixture.detectChanges()) .toThrowError(/compareWith must be a function, but received null/); }); @@ -539,13 +533,10 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' expect(select.nativeElement.value).toEqual('0: Object'); expect(sfOption.nativeElement.selected).toBe(true); })); - }); describe('should support <type=radio>', () => { - describe('in reactive forms', () => { - it('should support basic functionality', () => { const fixture = initTest(FormControlRadioButtons); const form = @@ -562,10 +553,10 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' fixture.detectChanges(); // view -> model - expect(form.get('food') !.value).toEqual('chicken'); + expect(form.get('food')!.value).toEqual('chicken'); expect(inputs[1].nativeElement.checked).toEqual(false); - form.get('food') !.setValue('fish'); + form.get('food')!.setValue('fish'); fixture.detectChanges(); // programmatic change -> view @@ -606,16 +597,16 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' fixture.componentInstance.form = form; fixture.detectChanges(); - form.get('food') !.setValue(null); + form.get('food')!.setValue(null); fixture.detectChanges(); const inputs = fixture.debugElement.queryAll(By.css('input')); expect(inputs[0].nativeElement.checked).toEqual(false); - form.get('food') !.setValue('chicken'); + form.get('food')!.setValue('chicken'); fixture.detectChanges(); - form.get('food') !.setValue(undefined); + form.get('food')!.setValue(undefined); fixture.detectChanges(); expect(inputs[0].nativeElement.checked).toEqual(false); }); @@ -706,13 +697,12 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' fixture.detectChanges(); // view -> model - expect(form.get('food') !.value).toEqual('chicken'); - expect(form.get('nested.food') !.value).toEqual('fish'); + expect(form.get('food')!.value).toEqual('chicken'); + expect(form.get('nested.food')!.value).toEqual('fish'); expect(inputs[1].nativeElement.checked).toEqual(false); expect(inputs[2].nativeElement.checked).toEqual(false); expect(inputs[3].nativeElement.checked).toEqual(true); - }); it('should disable all radio buttons when disable() is called', () => { @@ -728,7 +718,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' expect(inputs[2].nativeElement.disabled).toEqual(false); expect(inputs[3].nativeElement.disabled).toEqual(false); - form.get('food') !.disable(); + form.get('food')!.disable(); expect(inputs[0].nativeElement.disabled).toEqual(true); expect(inputs[1].nativeElement.disabled).toEqual(true); expect(inputs[2].nativeElement.disabled).toEqual(false); @@ -780,7 +770,6 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' expect(inputs[0].nativeElement.checked).toBe(false); expect(inputs[1].nativeElement.checked).toBe(true); }); - }); describe('in template-driven forms', () => { @@ -860,7 +849,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' fixture.detectChanges(); tick(); - fixture.componentInstance.food = null !; + fixture.componentInstance.food = null!; fixture.detectChanges(); tick(); @@ -872,7 +861,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' fixture.detectChanges(); tick(); - fixture.componentInstance.food = undefined !; + fixture.componentInstance.food = undefined!; fixture.detectChanges(); tick(); expect(inputs[0].nativeElement.checked).toEqual(false); @@ -886,7 +875,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); - form.control.get('food') !.disable(); + form.control.get('food')!.disable(); tick(); const inputs = fixture.debugElement.queryAll(By.css('input')); @@ -911,15 +900,11 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' expect(inputs[2].nativeElement.disabled).toBe(false); expect(inputs[3].nativeElement.disabled).toBe(false); })); - }); - }); describe('should support <type=range>', () => { - describe('in reactive forms', () => { - it('with basic use case', () => { const fixture = initTest(FormControlRangeInput); const control = new FormControl(10); @@ -968,7 +953,6 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' const input = fixture.debugElement.query(By.css('input')); expect(input.nativeElement.value).toEqual(''); }); - }); describe('in template-driven forms', () => { @@ -987,15 +971,12 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' tick(); // view -> model fixture.detectChanges(); - expect(typeof(fixture.componentInstance.val)).toBe('number'); + expect(typeof (fixture.componentInstance.val)).toBe('number'); })); - }); - }); describe('custom value accessors', () => { - describe('in reactive forms', () => { it('should support basic functionality', () => { const fixture = initTest(WrappedValueForm, WrappedValue); @@ -1014,9 +995,9 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' expect(form.value).toEqual({'login': 'bb'}); // custom validator - expect(form.get('login') !.errors).toEqual({'err': true}); + expect(form.get('login')!.errors).toEqual({'err': true}); form.setValue({login: 'expected'}); - expect(form.get('login') !.errors).toEqual(null); + expect(form.get('login')!.errors).toEqual(null); }); it('should support non builtin input elements that fire a change event without a \'target\' property', @@ -1042,7 +1023,7 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' }); fixture.detectChanges(); expect(fixture.componentInstance.form.status).toEqual('DISABLED'); - expect(fixture.componentInstance.form.get('login') !.status).toEqual('DISABLED'); + expect(fixture.componentInstance.form.get('login')!.status).toEqual('DISABLED'); }); it('should support custom accessors without setDisabledState - formControlDirective', @@ -1061,9 +1042,9 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' fixture.componentInstance.form = new FormGroup({'login': new FormControl('aa')}); fixture.detectChanges(); - expect(fixture.componentInstance.myInput !.control).toBeDefined(); - expect(fixture.componentInstance.myInput !.control) - .toEqual(fixture.componentInstance.myInput !.controlDir.control); + expect(fixture.componentInstance.myInput!.control).toBeDefined(); + expect(fixture.componentInstance.myInput!.control) + .toEqual(fixture.componentInstance.myInput!.controlDir.control); }); }); @@ -1089,16 +1070,14 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util' }); })); }); - }); - }); } @Component({selector: 'form-control-comp', template: `<input type="text" [formControl]="control">`}) export class FormControlComp { // TODO(issue/24571): remove '!'. - control !: FormControl; + control!: FormControl; } @Component({ @@ -1110,13 +1089,13 @@ export class FormControlComp { }) export class FormGroupComp { // TODO(issue/24571): remove '!'. - control !: FormControl; + control!: FormControl; // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; // TODO(issue/24571): remove '!'. - myGroup !: FormGroup; + myGroup!: FormGroup; // TODO(issue/24571): remove '!'. - event !: Event; + event!: Event; } @Component({ @@ -1125,7 +1104,7 @@ export class FormGroupComp { }) class FormControlNumberInput { // TODO(issue/24571): remove '!'. - control !: FormControl; + control!: FormControl; } @Component({ @@ -1167,7 +1146,7 @@ class FormControlSelectNgValue { }) class FormControlSelectWithCompareFn { compareFn: - (o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2? o1.id === o2.id: o1 === o2 + (o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2 ? o1.id === o2.id : o1 === o2 cities = [{id: 1, name: 'SF'}, {id: 2, name: 'NY'}]; form = new FormGroup({city: new FormControl({id: 1, name: 'SF'})}); } @@ -1211,7 +1190,7 @@ class FormControlSelectMultipleNgValue { }) class FormControlSelectMultipleWithCompareFn { compareFn: - (o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2? o1.id === o2.id: o1 === o2 + (o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2 ? o1.id === o2.id : o1 === o2 cities = [{id: 1, name: 'SF'}, {id: 2, name: 'NY'}]; form = new FormGroup({city: new FormControl([{id: 1, name: 'SF'}])}); } @@ -1254,7 +1233,7 @@ class NgModelSelectWithNullForm { }) class NgModelSelectWithCustomCompareFnForm { compareFn: - (o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2? o1.id === o2.id: o1 === o2 + (o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2 ? o1.id === o2.id : o1 === o2 selectedCity: any = {}; cities: any[] = []; } @@ -1270,7 +1249,7 @@ class NgModelSelectWithCustomCompareFnForm { }) class NgModelSelectMultipleWithCustomCompareFnForm { compareFn: - (o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2? o1.id === o2.id: o1 === o2 + (o1: any, o2: any) => boolean = (o1: any, o2: any) => o1 && o2 ? o1.id === o2.id : o1 === o2 selectedCities: any[] = []; cities: any[] = []; } @@ -1285,7 +1264,7 @@ class NgModelSelectMultipleWithCustomCompareFnForm { }) class NgModelSelectMultipleForm { // TODO(issue/24571): remove '!'. - selectedCities !: any[]; + selectedCities!: any[]; cities: any[] = []; } @@ -1295,7 +1274,7 @@ class NgModelSelectMultipleForm { }) class FormControlRangeInput { // TODO(issue/24571): remove '!'. - control !: FormControl; + control!: FormControl; } @Component({selector: 'ng-model-range-form', template: '<input type="range" [(ngModel)]="val">'}) @@ -1317,7 +1296,7 @@ class NgModelRangeForm { }) export class FormControlRadioButtons { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; showRadio = new FormControl('yes'); } @@ -1335,9 +1314,9 @@ export class FormControlRadioButtons { }) class NgModelRadioForm { // TODO(issue/24571): remove '!'. - food !: string; + food!: string; // TODO(issue/24571): remove '!'. - drink !: string; + drink!: string; } @Directive({ @@ -1351,37 +1330,55 @@ class NgModelRadioForm { class WrappedValue implements ControlValueAccessor { value: any; // TODO(issue/24571): remove '!'. - onChange !: Function; + onChange!: Function; - writeValue(value: any) { this.value = `!${value}!`; } + writeValue(value: any) { + this.value = `!${value}!`; + } - registerOnChange(fn: (value: any) => void) { this.onChange = fn; } + registerOnChange(fn: (value: any) => void) { + this.onChange = fn; + } registerOnTouched(fn: any) {} - handleOnInput(value: any) { this.onChange(value.substring(1, value.length - 1)); } + handleOnInput(value: any) { + this.onChange(value.substring(1, value.length - 1)); + } - validate(c: AbstractControl) { return c.value === 'expected' ? null : {'err': true}; } + validate(c: AbstractControl) { + return c.value === 'expected' ? null : {'err': true}; + } } @Component({selector: 'my-input', template: ''}) export class MyInput implements ControlValueAccessor { @Output('input') onInput = new EventEmitter(); // TODO(issue/24571): remove '!'. - value !: string; + value!: string; control: AbstractControl|null = null; - constructor(public controlDir: NgControl) { controlDir.valueAccessor = this; } + constructor(public controlDir: NgControl) { + controlDir.valueAccessor = this; + } - ngOnInit() { this.control = this.controlDir.control; } + ngOnInit() { + this.control = this.controlDir.control; + } - writeValue(value: any) { this.value = `!${value}!`; } + writeValue(value: any) { + this.value = `!${value}!`; + } - registerOnChange(fn: (value: any) => void) { this.onInput.subscribe({next: fn}); } + registerOnChange(fn: (value: any) => void) { + this.onInput.subscribe({next: fn}); + } registerOnTouched(fn: any) {} - dispatchChangeEvent() { this.onInput.emit(this.value.substring(1, this.value.length - 1)); } + dispatchChangeEvent() { + this.onInput.emit(this.value.substring(1, this.value.length - 1)); + } } @Component({ @@ -1393,7 +1390,7 @@ export class MyInput implements ControlValueAccessor { }) export class MyInputForm { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; @ViewChild(MyInput) myInput: MyInput|null = null; } @@ -1406,7 +1403,7 @@ export class MyInputForm { }) class WrappedValueForm { // TODO(issue/24571): remove '!'. - form !: FormGroup; + form!: FormGroup; } @Component({ @@ -1418,18 +1415,24 @@ class WrappedValueForm { }) export class NgModelCustomComp implements ControlValueAccessor { // TODO(issue/24571): remove '!'. - model !: string; + model!: string; @Input('disabled') isDisabled: boolean = false; // TODO(issue/24571): remove '!'. - changeFn !: (value: any) => void; + changeFn!: (value: any) => void; - writeValue(value: any) { this.model = value; } + writeValue(value: any) { + this.model = value; + } - registerOnChange(fn: (value: any) => void) { this.changeFn = fn; } + registerOnChange(fn: (value: any) => void) { + this.changeFn = fn; + } registerOnTouched() {} - setDisabledState(isDisabled: boolean) { this.isDisabled = isDisabled; } + setDisabledState(isDisabled: boolean) { + this.isDisabled = isDisabled; + } } @Component({ @@ -1442,6 +1445,6 @@ export class NgModelCustomComp implements ControlValueAccessor { }) export class NgModelCustomWrapper { // TODO(issue/24571): remove '!'. - name !: string; + name!: string; isDisabled = false; } diff --git a/packages/http/src/backends/browser_jsonp.ts b/packages/http/src/backends/browser_jsonp.ts index d5112c7a886f2..ab7f2082394ed 100644 --- a/packages/http/src/backends/browser_jsonp.ts +++ b/packages/http/src/backends/browser_jsonp.ts @@ -30,9 +30,13 @@ export class BrowserJsonp { return node; } - nextRequestID(): string { return `__req${_nextRequestId++}`; } + nextRequestID(): string { + return `__req${_nextRequestId++}`; + } - requestCallback(id: string): string { return `${JSONP_HOME}.${id}.finished`; } + requestCallback(id: string): string { + return `${JSONP_HOME}.${id}.finished`; + } exposeConnection(id: string, connection: any) { const connections = _getJsonpConnections(); @@ -45,7 +49,9 @@ export class BrowserJsonp { } // Attach the <script> element to the DOM - send(node: any) { document.body.appendChild(<Node>(node)); } + send(node: any) { + document.body.appendChild(<Node>(node)); + } // Remove <script> element from the DOM cleanup(node: any) { diff --git a/packages/http/src/backends/browser_xhr.ts b/packages/http/src/backends/browser_xhr.ts index d093413efa6dd..a71ee62d20d59 100644 --- a/packages/http/src/backends/browser_xhr.ts +++ b/packages/http/src/backends/browser_xhr.ts @@ -19,5 +19,7 @@ import {Injectable} from '@angular/core'; @Injectable() export class BrowserXhr { constructor() {} - build(): any { return <any>(new XMLHttpRequest()); } + build(): any { + return <any>(new XMLHttpRequest()); + } } diff --git a/packages/http/src/backends/jsonp_backend.ts b/packages/http/src/backends/jsonp_backend.ts index 00dcaa35e23af..beedfb1f8271a 100644 --- a/packages/http/src/backends/jsonp_backend.ts +++ b/packages/http/src/backends/jsonp_backend.ts @@ -28,9 +28,9 @@ const JSONP_ERR_WRONG_METHOD = 'JSONP requests must use GET request method.'; */ export class JSONPConnection implements Connection { // TODO(issue/24571): remove '!'. - private _id !: string; + private _id!: string; // TODO(issue/24571): remove '!'. - private _script !: Element; + private _script!: Element; private _responseData: any; private _finished: boolean = false; @@ -38,7 +38,7 @@ export class JSONPConnection implements Connection { * The {@link ReadyState} of this request. */ // TODO(issue/24571): remove '!'. - readyState !: ReadyState; + readyState!: ReadyState; /** * The outgoing HTTP request. @@ -58,7 +58,6 @@ export class JSONPConnection implements Connection { } this.request = req; this.response = new Observable<Response>((responseObserver: Observer<Response>) => { - this.readyState = ReadyState.Loading; const id = this._id = _dom.nextRequestID(); diff --git a/packages/http/src/backends/xhr_backend.ts b/packages/http/src/backends/xhr_backend.ts index 54256b2810248..7881d64f1aeaa 100644 --- a/packages/http/src/backends/xhr_backend.ts +++ b/packages/http/src/backends/xhr_backend.ts @@ -39,7 +39,7 @@ export class XHRConnection implements Connection { */ response: Observable<Response>; // TODO(issue/24571): remove '!'. - readyState !: ReadyState; + readyState!: ReadyState; constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) { this.request = req; this.response = new Observable<Response>((responseObserver: Observer<Response>) => { @@ -116,7 +116,7 @@ export class XHRConnection implements Connection { if (!req.headers.has('Accept')) { req.headers.append('Accept', 'application/json, text/plain, */*'); } - req.headers.forEach((values, name) => _xhr.setRequestHeader(name !, values.join(','))); + req.headers.forEach((values, name) => _xhr.setRequestHeader(name!, values.join(','))); // Select the correct buffer type to store the response if (req.responseType != null && _xhr.responseType != null) { diff --git a/packages/http/src/base_request_options.ts b/packages/http/src/base_request_options.ts index beda7bba39fd9..d48b95d8a29c2 100644 --- a/packages/http/src/base_request_options.ts +++ b/packages/http/src/base_request_options.ts @@ -65,11 +65,15 @@ export class RequestOptions { /** * @deprecated from 4.0.0. Use params instead. */ - get search(): URLSearchParams { return this.params; } + get search(): URLSearchParams { + return this.params; + } /** * @deprecated from 4.0.0. Use params instead. */ - set search(params: URLSearchParams) { this.params = params; } + set search(params: URLSearchParams) { + this.params = params; + } /** * Enable use credentials for a {@link Request}. */ @@ -143,7 +147,7 @@ export class RequestOptions { return this._parseParams(params); } - private _parseParams(objParams: {[key: string]: any | any[]} = {}): URLSearchParams { + private _parseParams(objParams: {[key: string]: any|any[]} = {}): URLSearchParams { const params = new URLSearchParams(); Object.keys(objParams).forEach((key: string) => { const value: any|any[] = objParams[key]; @@ -206,5 +210,7 @@ export class RequestOptions { */ @Injectable() export class BaseRequestOptions extends RequestOptions { - constructor() { super({method: RequestMethod.Get, headers: new Headers()}); } + constructor() { + super({method: RequestMethod.Get, headers: new Headers()}); + } } diff --git a/packages/http/src/body.ts b/packages/http/src/body.ts index 18e4c7b7e4638..84fd52481746a 100644 --- a/packages/http/src/body.ts +++ b/packages/http/src/body.ts @@ -92,8 +92,8 @@ export abstract class Body { } /** - * Returns the request's body as a Blob, assuming that body exists. - */ + * Returns the request's body as a Blob, assuming that body exists. + */ blob(): Blob { if (this._body instanceof Blob) { return this._body; diff --git a/packages/http/src/headers.ts b/packages/http/src/headers.ts index ba2c4c2162cd2..7311a6e89ba3b 100644 --- a/packages/http/src/headers.ts +++ b/packages/http/src/headers.ts @@ -96,7 +96,7 @@ export class Headers { /** * Deletes all header values for the given name. */ - delete (name: string): void { + delete(name: string): void { const lcName = name.toLowerCase(); this._normalizedNames.delete(lcName); this._headers.delete(lcName); @@ -124,12 +124,16 @@ export class Headers { /** * Checks for existence of header by given name. */ - has(name: string): boolean { return this._headers.has(name.toLowerCase()); } + has(name: string): boolean { + return this._headers.has(name.toLowerCase()); + } /** * Returns the names of the headers */ - keys(): string[] { return Array.from(this._normalizedNames.values()); } + keys(): string[] { + return Array.from(this._normalizedNames.values()); + } /** * Sets or overrides header value for given name. @@ -148,7 +152,9 @@ export class Headers { /** * Returns values of all headers. */ - values(): string[][] { return Array.from(this._headers.values()); } + values(): string[][] { + return Array.from(this._headers.values()); + } /** * Returns string of all headers. @@ -160,7 +166,7 @@ export class Headers { this._headers.forEach((values: string[], name: string) => { const split: string[] = []; values.forEach(v => split.push(...v.split(','))); - serialized[this._normalizedNames.get(name) !] = split; + serialized[this._normalizedNames.get(name)!] = split; }); return serialized; @@ -176,7 +182,9 @@ export class Headers { /** * This method is not implemented. */ - entries() { throw new Error('"entries" method is not implemented on Headers class'); } + entries() { + throw new Error('"entries" method is not implemented on Headers class'); + } private mayBeSetNormalizedName(name: string): void { const lcName = name.toLowerCase(); diff --git a/packages/http/src/http.ts b/packages/http/src/http.ts index f46f472d3c974..9946162640789 100644 --- a/packages/http/src/http.ts +++ b/packages/http/src/http.ts @@ -20,7 +20,7 @@ function httpRequest(backend: ConnectionBackend, request: Request): Observable<R } function mergeOptions( - defaultOpts: BaseRequestOptions, providedOpts: RequestOptionsArgs | undefined, + defaultOpts: BaseRequestOptions, providedOpts: RequestOptionsArgs|undefined, method: RequestMethod, url: string): RequestArgs { const newOptions = defaultOpts; if (providedOpts) { @@ -156,7 +156,7 @@ export class Http { /** * Performs a request with `delete` http method. */ - delete (url: string, options?: RequestOptionsArgs): Observable<Response> { + delete(url: string, options?: RequestOptionsArgs): Observable<Response> { return this.request( new Request(mergeOptions(this._defaultOptions, options, RequestMethod.Delete, url))); } diff --git a/packages/http/src/http_utils.ts b/packages/http/src/http_utils.ts index 694d40eebd3b6..8c04c28aa994b 100644 --- a/packages/http/src/http_utils.ts +++ b/packages/http/src/http_utils.ts @@ -8,7 +8,7 @@ import {RequestMethod} from './enums'; -export function normalizeMethodName(method: string | RequestMethod): RequestMethod { +export function normalizeMethodName(method: string|RequestMethod): RequestMethod { if (typeof method !== 'string') return method; switch (method.toUpperCase()) { diff --git a/packages/http/src/interfaces.ts b/packages/http/src/interfaces.ts index 257544989532d..f1bc08c43b22c 100644 --- a/packages/http/src/interfaces.ts +++ b/packages/http/src/interfaces.ts @@ -20,7 +20,9 @@ import {URLSearchParams} from './url_search_params'; * @deprecated see https://angular.io/guide/http * @publicApi */ -export abstract class ConnectionBackend { abstract createConnection(request: any): Connection; } +export abstract class ConnectionBackend { + abstract createConnection(request: any): Connection; +} /** * Abstract class from which real connections are derived. @@ -30,9 +32,9 @@ export abstract class ConnectionBackend { abstract createConnection(request: any */ export abstract class Connection { // TODO(issue/24571): remove '!'. - readyState !: ReadyState; + readyState!: ReadyState; // TODO(issue/24571): remove '!'. - request !: Request; + request!: Request; response: any; // TODO: generic of <Response>; } @@ -42,7 +44,9 @@ export abstract class Connection { * @deprecated see https://angular.io/guide/http * @publicApi */ -export abstract class XSRFStrategy { abstract configureRequest(req: Request): void; } +export abstract class XSRFStrategy { + abstract configureRequest(req: Request): void; +} /** * Interface for options to construct a RequestOptions, based on @@ -66,7 +70,9 @@ export interface RequestOptionsArgs { /** * Required structure when constructing new Request(); */ -export interface RequestArgs extends RequestOptionsArgs { url: string|null; } +export interface RequestArgs extends RequestOptionsArgs { + url: string|null; +} /** * Interface for options to construct a Response, based on diff --git a/packages/http/src/static_request.ts b/packages/http/src/static_request.ts index c48cd252cb585..1e50c53a2d779 100644 --- a/packages/http/src/static_request.ts +++ b/packages/http/src/static_request.ts @@ -76,7 +76,7 @@ export class Request extends Body { super(); // TODO: assert that url is present const url = requestOptions.url; - this.url = requestOptions.url !; + this.url = requestOptions.url!; const paramsArg = requestOptions.params || requestOptions.search; if (paramsArg) { let params: string; @@ -95,13 +95,13 @@ export class Request extends Body { } } this._body = requestOptions.body; - this.method = normalizeMethodName(requestOptions.method !); + this.method = normalizeMethodName(requestOptions.method!); // TODO(jeffbcross): implement behavior // Defaults to 'omit', consistent with browser this.headers = new Headers(requestOptions.headers); this.contentType = this.detectContentType(); - this.withCredentials = requestOptions.withCredentials !; - this.responseType = requestOptions.responseType !; + this.withCredentials = requestOptions.withCredentials!; + this.responseType = requestOptions.responseType!; } /** diff --git a/packages/http/src/static_response.ts b/packages/http/src/static_response.ts index 66b760186df5e..71869a5eb3d23 100644 --- a/packages/http/src/static_response.ts +++ b/packages/http/src/static_response.ts @@ -73,14 +73,14 @@ export class Response extends Body { * the result of a progress event. */ // TODO(issue/24571): remove '!'. - bytesLoaded !: number; + bytesLoaded!: number; /** * Non-standard property * * Denotes how many bytes are expected in the final response body. */ // TODO(issue/24571): remove '!'. - totalBytes !: number; + totalBytes!: number; /** * Headers object based on the `Headers` class in the [Fetch * Spec](https://fetch.spec.whatwg.org/#headers-class). @@ -90,12 +90,12 @@ export class Response extends Body { constructor(responseOptions: ResponseOptions) { super(); this._body = responseOptions.body; - this.status = responseOptions.status !; + this.status = responseOptions.status!; this.ok = (this.status >= 200 && this.status <= 299); this.statusText = responseOptions.statusText; this.headers = responseOptions.headers; - this.type = responseOptions.type !; - this.url = responseOptions.url !; + this.type = responseOptions.type!; + this.url = responseOptions.url!; } toString(): string { diff --git a/packages/http/src/url_search_params.ts b/packages/http/src/url_search_params.ts index 6d8e8cd70af86..55ee366451ee3 100644 --- a/packages/http/src/url_search_params.ts +++ b/packages/http/src/url_search_params.ts @@ -26,9 +26,13 @@ function paramParser(rawParams: string = ''): Map<string, string[]> { * @publicApi **/ export class QueryEncoder { - encodeKey(key: string): string { return standardEncoding(key); } + encodeKey(key: string): string { + return standardEncoding(key); + } - encodeValue(value: string): string { return standardEncoding(value); } + encodeValue(value: string): string { + return standardEncoding(value); + } } function standardEncoding(v: string): string { @@ -93,7 +97,9 @@ export class URLSearchParams { return clone; } - has(param: string): boolean { return this.paramsMap.has(param); } + has(param: string): boolean { + return this.paramsMap.has(param); + } get(param: string): string|null { const storedParam = this.paramsMap.get(param); @@ -101,7 +107,9 @@ export class URLSearchParams { return Array.isArray(storedParam) ? storedParam[0] : null; } - getAll(param: string): string[] { return this.paramsMap.get(param) || []; } + getAll(param: string): string[] { + return this.paramsMap.get(param) || []; + } set(param: string, val: string) { if (val === void 0 || val === null) { @@ -182,5 +190,7 @@ export class URLSearchParams { return paramsList.join('&'); } - delete (param: string): void { this.paramsMap.delete(param); } + delete(param: string): void { + this.paramsMap.delete(param); + } } diff --git a/packages/http/test/backends/jsonp_backend_spec.ts b/packages/http/test/backends/jsonp_backend_spec.ts index 724cec7107d77..528cef5b4acb2 100644 --- a/packages/http/test/backends/jsonp_backend_spec.ts +++ b/packages/http/test/backends/jsonp_backend_spec.ts @@ -7,7 +7,7 @@ */ import {Injector} from '@angular/core'; -import {AsyncTestCompleter, SpyObject, afterEach, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; +import {afterEach, AsyncTestCompleter, beforeEach, describe, inject, it, SpyObject} from '@angular/core/testing/src/testing_internal'; import {BrowserJsonp} from '@angular/http/src/backends/browser_jsonp'; import {JSONPBackend, JSONPConnection} from '@angular/http/src/backends/jsonp_backend'; import {BaseRequestOptions, RequestOptions} from '@angular/http/src/base_request_options'; @@ -21,12 +21,16 @@ let existingScripts: MockBrowserJsonp[] = []; class MockBrowserJsonp extends BrowserJsonp { // TODO(issue/24571): remove '!'. - src !: string; + src!: string; callbacks = new Map<string, (data: any) => any>(); - addEventListener(type: string, cb: (data: any) => any) { this.callbacks.set(type, cb); } + addEventListener(type: string, cb: (data: any) => any) { + this.callbacks.set(type, cb); + } - removeEventListener(type: string, cb: Function) { this.callbacks.delete(type); } + removeEventListener(type: string, cb: Function) { + this.callbacks.delete(type); + } dispatchEvent(type: string, argument: any = {}) { const cb = this.callbacks.get(type); @@ -65,10 +69,12 @@ class MockBrowserJsonp extends BrowserJsonp { new Request(base.merge(new RequestOptions({url: 'https://google.com'})) as any); }); - afterEach(() => { existingScripts = []; }); + afterEach(() => { + existingScripts = []; + }); it('should create a connection', () => { - let instance: JSONPConnection = undefined !; + let instance: JSONPConnection = undefined!; expect(() => instance = backend.createConnection(sampleRequest)).not.toThrow(); expect(instance).toBeAnInstanceOf(JSONPConnection); }); @@ -141,8 +147,9 @@ class MockBrowserJsonp extends BrowserJsonp { RequestMethod.Head, RequestMethod.Patch] .forEach(method => { const base = new BaseRequestOptions(); - const req = new Request(base.merge( - new RequestOptions({url: 'https://google.com', method: method})) as any); + const req = new Request( + base.merge(new RequestOptions({url: 'https://google.com', method: method})) as + any); expect( () => new (JSONPConnection as any)(req, new MockBrowserJsonp()) .response.subscribe()) diff --git a/packages/http/test/backends/mock_backend_spec.ts b/packages/http/test/backends/mock_backend_spec.ts index ade1e47c674ee..97c5c5e11f510 100644 --- a/packages/http/test/backends/mock_backend_spec.ts +++ b/packages/http/test/backends/mock_backend_spec.ts @@ -18,7 +18,6 @@ import {ReplaySubject} from 'rxjs'; { describe('MockBackend', () => { - let backend: MockBackend; let sampleRequest1: Request; let sampleResponse1: Response; @@ -40,7 +39,9 @@ import {ReplaySubject} from 'rxjs'; sampleResponse2 = new Response(new ResponseOptions({body: 'response2'})); }); - it('should create a new MockBackend', () => { expect(backend).toBeAnInstanceOf(MockBackend); }); + it('should create a new MockBackend', () => { + expect(backend).toBeAnInstanceOf(MockBackend); + }); it('should create a new MockConnection', () => { expect(backend.createConnection(sampleRequest1)).toBeAnInstanceOf(MockConnection); @@ -54,7 +55,9 @@ import {ReplaySubject} from 'rxjs'; it('should allow responding after subscription', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const connection: MockConnection = backend.createConnection(sampleRequest1); - connection.response.subscribe(() => { async.done(); }); + connection.response.subscribe(() => { + async.done(); + }); connection.mockRespond(sampleResponse1); })); @@ -62,20 +65,26 @@ import {ReplaySubject} from 'rxjs'; inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const connection: MockConnection = backend.createConnection(sampleRequest1); connection.mockRespond(sampleResponse1); - connection.response.subscribe(() => { async.done(); }); + connection.response.subscribe(() => { + async.done(); + }); })); it('should allow responding after subscription with an error', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const connection: MockConnection = backend.createConnection(sampleRequest1); - connection.response.subscribe(null !, () => { async.done(); }); + connection.response.subscribe(null!, () => { + async.done(); + }); connection.mockError(new Error('nope')); })); it('should not throw when there are no unresolved requests', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const connection: MockConnection = backend.createConnection(sampleRequest1); - connection.response.subscribe(() => { async.done(); }); + connection.response.subscribe(() => { + async.done(); + }); connection.mockRespond(sampleResponse1); backend.verifyNoPendingRequests(); })); @@ -83,7 +92,9 @@ import {ReplaySubject} from 'rxjs'; xit('should throw when there are unresolved requests', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const connection: MockConnection = backend.createConnection(sampleRequest1); - connection.response.subscribe(() => { async.done(); }); + connection.response.subscribe(() => { + async.done(); + }); backend.verifyNoPendingRequests(); })); @@ -91,7 +102,9 @@ import {ReplaySubject} from 'rxjs'; inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const connection1: MockConnection = backend.createConnection(sampleRequest1); const connection2: MockConnection = backend.createConnection(sampleRequest1); - connection1.response.subscribe(() => { async.done(); }); + connection1.response.subscribe(() => { + async.done(); + }); connection2.response.subscribe(() => {}); connection2.mockRespond(sampleResponse1); connection1.mockRespond(sampleResponse1); @@ -101,12 +114,12 @@ import {ReplaySubject} from 'rxjs'; xit('should allow double subscribing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const responses: Response[] = [sampleResponse1, sampleResponse2]; - backend.connections.subscribe((c: MockConnection) => c.mockRespond(responses.shift() !)); + backend.connections.subscribe((c: MockConnection) => c.mockRespond(responses.shift()!)); const responseObservable: ReplaySubject<Response> = backend.createConnection(sampleRequest1).response; responseObservable.subscribe(res => expect(res.text()).toBe('response1')); responseObservable.subscribe( - res => expect(res.text()).toBe('response2'), null !, async.done); + res => expect(res.text()).toBe('response2'), null!, async.done); })); // TODO(robwormald): readyStates are leaving? diff --git a/packages/http/test/backends/xhr_backend_spec.ts b/packages/http/test/backends/xhr_backend_spec.ts index 3fb285f204687..ace4085f1e43b 100644 --- a/packages/http/test/backends/xhr_backend_spec.ts +++ b/packages/http/test/backends/xhr_backend_spec.ts @@ -8,7 +8,7 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {Injectable} from '@angular/core'; -import {AsyncTestCompleter, SpyObject, afterEach, beforeEach, beforeEachProviders, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; +import {afterEach, AsyncTestCompleter, beforeEach, beforeEachProviders, describe, expect, inject, it, SpyObject} from '@angular/core/testing/src/testing_internal'; import {BrowserXhr} from '@angular/http/src/backends/browser_xhr'; import {CookieXSRFStrategy, XHRBackend, XHRConnection} from '@angular/http/src/backends/xhr_backend'; import {BaseRequestOptions, RequestOptions} from '@angular/http/src/base_request_options'; @@ -34,19 +34,19 @@ class MockBrowserXHR extends BrowserXhr { response: any; responseType: string; // TODO(issue/24571): remove '!'. - responseText !: string; + responseText!: string; setRequestHeader: any; callbacks = new Map<string, Function>(); // TODO(issue/24571): remove '!'. - status !: number; + status!: number; // TODO(issue/24571): remove '!'. - responseHeaders !: string; + responseHeaders!: string; // TODO(issue/24571): remove '!'. - responseURL !: string; + responseURL!: string; // TODO(issue/24571): remove '!'. - statusText !: string; + statusText!: string; // TODO(issue/24571): remove '!'. - withCredentials !: boolean; + withCredentials!: boolean; constructor() { super(); @@ -60,29 +60,49 @@ class MockBrowserXHR extends BrowserXhr { this.responseType = ''; } - setStatusCode(status: number) { this.status = status; } + setStatusCode(status: number) { + this.status = status; + } - setStatusText(statusText: string) { this.statusText = statusText; } + setStatusText(statusText: string) { + this.statusText = statusText; + } - setResponse(value: string) { this.response = value; } + setResponse(value: string) { + this.response = value; + } - setResponseText(value: string) { this.responseText = value; } + setResponseText(value: string) { + this.responseText = value; + } - setResponseURL(value: string) { this.responseURL = value; } + setResponseURL(value: string) { + this.responseURL = value; + } - setResponseHeaders(value: string) { this.responseHeaders = value; } + setResponseHeaders(value: string) { + this.responseHeaders = value; + } - getAllResponseHeaders() { return this.responseHeaders || ''; } + getAllResponseHeaders() { + return this.responseHeaders || ''; + } getResponseHeader(key: string) { return Headers.fromResponseHeaderString(this.responseHeaders).get(key); } - addEventListener(type: string, cb: Function) { this.callbacks.set(type, cb); } + addEventListener(type: string, cb: Function) { + this.callbacks.set(type, cb); + } - removeEventListener(type: string, cb: Function) { this.callbacks.delete(type); } + removeEventListener(type: string, cb: Function) { + this.callbacks.delete(type); + } - dispatchEvent(type: string) { this.callbacks.get(type) !({}); } + dispatchEvent(type: string) { + this.callbacks.get(type)!({}); + } build() { const xhr = new MockBrowserXHR(); @@ -99,7 +119,8 @@ class MockBrowserXHR extends BrowserXhr { beforeEachProviders( () => [{provide: ResponseOptions, useClass: BaseResponseOptions}, - {provide: BrowserXhr, useClass: MockBrowserXHR}, XHRBackend, + {provide: BrowserXhr, useClass: MockBrowserXHR}, + XHRBackend, {provide: XSRFStrategy, useValue: new CookieXSRFStrategy()}, ]); @@ -110,7 +131,9 @@ class MockBrowserXHR extends BrowserXhr { new Request(base.merge(new RequestOptions({url: 'https://google.com'})) as any); })); - afterEach(() => { existingXHRs = []; }); + afterEach(() => { + existingXHRs = []; + }); describe('creating a connection', () => { @Injectable() @@ -119,8 +142,9 @@ class MockBrowserXHR extends BrowserXhr { } beforeEachProviders(() => [{provide: XSRFStrategy, useClass: NoopXsrfStrategy}]); - it('succeeds', - () => { expect(() => backend.createConnection(sampleRequest)).not.toThrow(); }); + it('succeeds', () => { + expect(() => backend.createConnection(sampleRequest)).not.toThrow(); + }); }); if (getDOM().supportsCookies()) { @@ -171,8 +195,13 @@ class MockBrowserXHR extends BrowserXhr { sampleRequest, new MockBrowserXHR(), new ResponseOptions({type: ResponseType.Error})); connection.response.subscribe( - (res: Response) => { expect(res.type).toBe(ResponseType.Error); }, null !, - () => { async.done(); }); + (res: Response) => { + expect(res.type).toBe(ResponseType.Error); + }, + null!, + () => { + async.done(); + }); existingXHRs[0].setStatusCode(200); existingXHRs[0].dispatchEvent('load'); })); @@ -189,7 +218,7 @@ class MockBrowserXHR extends BrowserXhr { const connection = new XHRConnection( sampleRequest, new MockBrowserXHR(), new ResponseOptions({type: ResponseType.Error})); - connection.response.subscribe(null !, (res: Response) => { + connection.response.subscribe(null!, (res: Response) => { expect(res.type).toBe(ResponseType.Error); async.done(); }); @@ -201,7 +230,7 @@ class MockBrowserXHR extends BrowserXhr { const connection = new XHRConnection( sampleRequest, new MockBrowserXHR(), new ResponseOptions({type: ResponseType.Error})); - connection.response.subscribe(null !, (res: Response) => { + connection.response.subscribe(null!, (res: Response) => { expect(res.type).toBe(ResponseType.Error); expect(res.status).toEqual(0); expect(res.statusText).toEqual(''); @@ -365,7 +394,7 @@ class MockBrowserXHR extends BrowserXhr { }); it('should use blob body without type to the request', () => { - const body = createBlob(['body { color: red; }'], null !); + const body = createBlob(['body { color: red; }'], null!); const base = new BaseRequestOptions(); const connection = new XHRConnection( new Request(base.merge(new RequestOptions({body: body}))), new MockBrowserXHR()); @@ -377,7 +406,7 @@ class MockBrowserXHR extends BrowserXhr { it('should use blob body without type with custom content type header to the request', () => { const headers = new Headers({'Content-Type': 'text/css'}); - const body = createBlob(['body { color: red; }'], null !); + const body = createBlob(['body { color: red; }'], null!); const base = new BaseRequestOptions(); const connection = new XHRConnection( new Request(base.merge(new RequestOptions({body: body, headers: headers}))), @@ -451,7 +480,9 @@ class MockBrowserXHR extends BrowserXhr { nextCalled = true; expect(res.status).toBe(statusCode); }, - (errRes: Response) => { errorCalled = true; }, + (errRes: Response) => { + errorCalled = true; + }, () => { expect(nextCalled).toBe(true); expect(errorCalled).toBe(false); @@ -484,7 +515,9 @@ class MockBrowserXHR extends BrowserXhr { sampleRequest, new MockBrowserXHR(), new ResponseOptions({status: statusCode})); connection.response.subscribe( - (res: Response) => { throw 'should not be called'; }, + (res: Response) => { + throw 'should not be called'; + }, (errRes: Response) => { expect(errRes.ok).toBe(false); async.done(); @@ -503,13 +536,17 @@ class MockBrowserXHR extends BrowserXhr { sampleRequest, new MockBrowserXHR(), new ResponseOptions({status: statusCode})); connection.response.subscribe( - (res: Response) => { nextCalled = true; }, + (res: Response) => { + nextCalled = true; + }, (errRes: Response) => { expect(errRes.status).toBe(statusCode); expect(nextCalled).toBe(false); async.done(); }, - () => { throw 'should not be called'; }); + () => { + throw 'should not be called'; + }); existingXHRs[0].setStatusCode(statusCode); existingXHRs[0].dispatchEvent('load'); @@ -601,7 +638,7 @@ class MockBrowserXHR extends BrowserXhr { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const conn = new XHRConnection(sampleRequest, new MockBrowserXHR(), new ResponseOptions()); - conn.response.subscribe(null !, (res: Response) => { + conn.response.subscribe(null!, (res: Response) => { expect(res.text()).toBe('{json: "object"}'); async.done(); }); @@ -622,10 +659,10 @@ Transfer-Encoding: chunked Connection: keep-alive`; connection.response.subscribe((res: Response) => { - expect(res.headers !.get('Date')).toEqual('Fri, 20 Nov 2015 01:45:26 GMT'); - expect(res.headers !.get('Content-Type')).toEqual('application/json; charset=utf-8'); - expect(res.headers !.get('Transfer-Encoding')).toEqual('chunked'); - expect(res.headers !.get('Connection')).toEqual('keep-alive'); + expect(res.headers!.get('Date')).toEqual('Fri, 20 Nov 2015 01:45:26 GMT'); + expect(res.headers!.get('Content-Type')).toEqual('application/json; charset=utf-8'); + expect(res.headers!.get('Transfer-Encoding')).toEqual('chunked'); + expect(res.headers!.get('Connection')).toEqual('keep-alive'); async.done(); }); diff --git a/packages/http/test/headers_spec.ts b/packages/http/test/headers_spec.ts index f5684ceeb913c..f0d61ebff5823 100644 --- a/packages/http/test/headers_spec.ts +++ b/packages/http/test/headers_spec.ts @@ -10,7 +10,6 @@ import {Headers} from '@angular/http/src/headers'; { describe('Headers', () => { - describe('initialization', () => { it('should conform to spec', () => { const httpHeaders = { @@ -165,8 +164,9 @@ import {Headers} from '@angular/http/src/headers'; ref = {'Accept': values}; }); - it('should be serializable with toJSON', - () => { expect(JSON.stringify(headers)).toEqual(JSON.stringify(ref)); }); + it('should be serializable with toJSON', () => { + expect(JSON.stringify(headers)).toEqual(JSON.stringify(ref)); + }); it('should be able to recreate serializedHeaders', () => { const parsedHeaders = JSON.parse(JSON.stringify(headers)); diff --git a/packages/http/test/http_spec.ts b/packages/http/test/http_spec.ts index 24f95fb531ec2..459e2fbcf3a8b 100644 --- a/packages/http/test/http_spec.ts +++ b/packages/http/test/http_spec.ts @@ -7,14 +7,14 @@ */ import {Injector} from '@angular/core'; -import {TestBed, getTestBed} from '@angular/core/testing'; -import {AsyncTestCompleter, afterEach, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; +import {getTestBed, TestBed} from '@angular/core/testing'; +import {afterEach, AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; import {stringToArrayBuffer} from '@angular/http/src/http_utils'; import {MockBackend, MockConnection} from '@angular/http/testing/src/mock_backend'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {Observable, zip} from 'rxjs'; -import {BaseRequestOptions, ConnectionBackend, Http, HttpModule, JSONPBackend, Jsonp, JsonpModule, Request, RequestMethod, RequestOptions, Response, ResponseContentType, ResponseOptions, URLSearchParams, XHRBackend} from '../index'; +import {BaseRequestOptions, ConnectionBackend, Http, HttpModule, Jsonp, JSONPBackend, JsonpModule, Request, RequestMethod, RequestOptions, Response, ResponseContentType, ResponseOptions, URLSearchParams, XHRBackend} from '../index'; { describe('injectables', () => { @@ -38,7 +38,6 @@ import {BaseRequestOptions, ConnectionBackend, Http, HttpModule, JSONPBackend, J it('should allow using jsonpInjectables and httpInjectables in same injector', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - http = injector.get(Http); jsonp = injector.get(Jsonp); jsonpBackend = injector.get(JSONPBackend) as any as MockBackend; @@ -105,8 +104,9 @@ import {BaseRequestOptions, ConnectionBackend, Http, HttpModule, JSONPBackend, J describe('Http', () => { describe('.request()', () => { - it('should return an Observable', - () => { expect(http.request(url)).toBeAnInstanceOf(Observable); }); + it('should return an Observable', () => { + expect(http.request(url)).toBeAnInstanceOf(Observable); + }); it('should accept a fully-qualified request as its only parameter', @@ -174,8 +174,13 @@ import {BaseRequestOptions, ConnectionBackend, Http, HttpModule, JSONPBackend, J backend.connections.subscribe((c: MockConnection) => c.mockRespond(baseResponse)); http.request('http://basic.connection') .subscribe( - (res: Response) => { expect(res.text()).toBe('base response'); }, null !, - () => { async.done(); }); + (res: Response) => { + expect(res.text()).toBe('base response'); + }, + null!, + () => { + async.done(); + }); })); it('should perform multiple get requests and complete the responses', @@ -187,8 +192,13 @@ import {BaseRequestOptions, ConnectionBackend, Http, HttpModule, JSONPBackend, J }); http.request('http://basic.connection') .subscribe( - (res: Response) => { expect(res.text()).toBe('base response'); }, null !, - () => { async.done(); }); + (res: Response) => { + expect(res.text()).toBe('base response'); + }, + null!, + () => { + async.done(); + }); })); it('should throw if url is not a string or Request', () => { @@ -422,7 +432,6 @@ import {BaseRequestOptions, ConnectionBackend, Http, HttpModule, JSONPBackend, J }); describe('response buffer', () => { - it('should attach the provided buffer to the response', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { backend.connections.subscribe((c: MockConnection) => { diff --git a/packages/http/test/static_request_spec.ts b/packages/http/test/static_request_spec.ts index 570ef334ccfa1..816816611691d 100644 --- a/packages/http/test/static_request_spec.ts +++ b/packages/http/test/static_request_spec.ts @@ -27,66 +27,67 @@ import {supportsWebAnimation} from '@angular/platform-browser/testing/src/browse it('should return ContentType.JSON', () => { const req = new Request(new RequestOptions({ - url: 'test', - method: 'GET', - body: null, - headers: new Headers({'content-type': 'application/json'}) - }) as any); + url: 'test', + method: 'GET', + body: null, + headers: new Headers({'content-type': 'application/json'}) + }) as any); expect(req.detectContentType()).toEqual(ContentType.JSON); }); it('should return ContentType.FORM', () => { - const req = new Request(new RequestOptions({ - url: 'test', - method: 'GET', - body: null, - headers: new Headers({'content-type': 'application/x-www-form-urlencoded'}) - }) as any); + const req = new Request( + new RequestOptions({ + url: 'test', + method: 'GET', + body: null, + headers: new Headers({'content-type': 'application/x-www-form-urlencoded'}) + }) as any); expect(req.detectContentType()).toEqual(ContentType.FORM); }); it('should return ContentType.FORM_DATA', () => { const req = new Request(new RequestOptions({ - url: 'test', - method: 'GET', - body: null, - headers: new Headers({'content-type': 'multipart/form-data'}) - }) as any); + url: 'test', + method: 'GET', + body: null, + headers: new Headers({'content-type': 'multipart/form-data'}) + }) as any); expect(req.detectContentType()).toEqual(ContentType.FORM_DATA); }); it('should return ContentType.TEXT', () => { const req = new Request(new RequestOptions({ - url: 'test', - method: 'GET', - body: null, - headers: new Headers({'content-type': 'text/plain'}) - }) as any); + url: 'test', + method: 'GET', + body: null, + headers: new Headers({'content-type': 'text/plain'}) + }) as any); expect(req.detectContentType()).toEqual(ContentType.TEXT); }); it('should return ContentType.BLOB', () => { const req = new Request(new RequestOptions({ - url: 'test', - method: 'GET', - body: null, - headers: new Headers({'content-type': 'application/octet-stream'}) - }) as any); + url: 'test', + method: 'GET', + body: null, + headers: new Headers({'content-type': 'application/octet-stream'}) + }) as any); expect(req.detectContentType()).toEqual(ContentType.BLOB); }); it('should not create a blob out of ArrayBuffer', () => { const req = new Request(new RequestOptions({ - url: 'test', - method: 'GET', - body: new ArrayBuffer(1), - headers: new Headers({'content-type': 'application/octet-stream'}) - }) as any); + url: 'test', + method: 'GET', + body: new ArrayBuffer(1), + headers: new Headers({'content-type': 'application/octet-stream'}) + }) as any); expect(req.detectContentType()).toEqual(ContentType.ARRAY_BUFFER); }); @@ -94,11 +95,11 @@ import {supportsWebAnimation} from '@angular/platform-browser/testing/src/browse it('should return empty string if no body is present', () => { const req = new Request(new RequestOptions({ - url: 'test', - method: 'GET', - body: null, - headers: new Headers({'content-type': 'application/json'}) - }) as any); + url: 'test', + method: 'GET', + body: null, + headers: new Headers({'content-type': 'application/json'}) + }) as any); expect(req.text()).toEqual(''); }); diff --git a/packages/http/test/url_search_params_spec.ts b/packages/http/test/url_search_params_spec.ts index 8144ce95ae9a0..5d1fcfb9f5a62 100644 --- a/packages/http/test/url_search_params_spec.ts +++ b/packages/http/test/url_search_params_spec.ts @@ -36,8 +36,12 @@ import {URLSearchParams} from '@angular/http/src/url_search_params'; it('should optionally accept a custom parser', () => { const fooEveryThingParser = { - encodeKey() { return 'I AM KEY'; }, - encodeValue() { return 'I AM VALUE'; } + encodeKey() { + return 'I AM KEY'; + }, + encodeValue() { + return 'I AM VALUE'; + } }; const params = new URLSearchParams('', fooEveryThingParser); params.set('myKey', 'myValue'); @@ -68,8 +72,9 @@ import {URLSearchParams} from '@angular/http/src/url_search_params'; **/ let params = new URLSearchParams(); - '! $ \' ( ) * + , ; A 9 - . _ ~ ? / ='.split(' ').forEach( - (char, idx) => { params.set(`a${idx}`, char); }); + '! $ \' ( ) * + , ; A 9 - . _ ~ ? / ='.split(' ').forEach((char, idx) => { + params.set(`a${idx}`, char); + }); expect(params.toString()) .toBe( `a0=!&a1=$&a2=\'&a3=(&a4=)&a5=*&a6=+&a7=,&a8=;&a9=A&a10=9&a11=-&a12=.&a13=_&a14=~&a15=?&a16=/&a17==` @@ -130,8 +135,12 @@ import {URLSearchParams} from '@angular/http/src/url_search_params'; it('should support a clone operation via clone()', () => { const fooQueryEncoder = { - encodeKey(k: string) { return encodeURIComponent(k); }, - encodeValue(v: string) { return encodeURIComponent(v); } + encodeKey(k: string) { + return encodeURIComponent(k); + }, + encodeValue(v: string) { + return encodeURIComponent(v); + } }; const paramsA = new URLSearchParams('', fooQueryEncoder); paramsA.set('a', '2'); @@ -151,21 +160,20 @@ import {URLSearchParams} from '@angular/http/src/url_search_params'; it('should remove the parameter when set to undefined or null', () => { const params = new URLSearchParams('q=Q'); - params.set('q', undefined !); + params.set('q', undefined!); expect(params.has('q')).toBe(false); expect(params.toString()).toEqual(''); - params.set('q', null !); + params.set('q', null!); expect(params.has('q')).toBe(false); expect(params.toString()).toEqual(''); }); it('should ignore the value when append undefined or null', () => { const params = new URLSearchParams('q=Q'); - params.append('q', undefined !); + params.append('q', undefined!); expect(params.toString()).toEqual('q=Q'); - params.append('q', null !); + params.append('q', null!); expect(params.toString()).toEqual('q=Q'); }); - }); } diff --git a/packages/http/testing/src/mock_backend.ts b/packages/http/testing/src/mock_backend.ts index 5c8a5aeb8838b..64be8e2893a27 100644 --- a/packages/http/testing/src/mock_backend.ts +++ b/packages/http/testing/src/mock_backend.ts @@ -245,7 +245,9 @@ export class MockBackend implements ConnectionBackend { * * This method only exists in the mock implementation, not in real Backends. */ - resolveAllConnections() { this.connections.subscribe((c: MockConnection) => c.readyState = 4); } + resolveAllConnections() { + this.connections.subscribe((c: MockConnection) => c.readyState = 4); + } /** * Creates a new {@link MockConnection}. This is equivalent to calling `new diff --git a/packages/language-service/language-service.ts b/packages/language-service/language-service.ts index 1fa65563fca4b..3ddd039a9b365 100644 --- a/packages/language-service/language-service.ts +++ b/packages/language-service/language-service.ts @@ -15,6 +15,5 @@ */ export {createLanguageService} from './src/language_service'; export * from './src/ts_plugin'; -export {Completion, Completions, Declaration, Declarations, Definition, Diagnostic, Diagnostics, Hover, HoverTextSection, LanguageService, LanguageServiceHost, Location, Span, TemplateSource, TemplateSources} from './src/types'; +export {Declaration, Definition, Diagnostic, LanguageService, LanguageServiceHost, Span, TemplateSource} from './src/types'; export {TypeScriptServiceHost, createLanguageServiceFromTypescript} from './src/typescript_host'; -export {VERSION} from './src/version'; diff --git a/packages/language-service/src/binding_utils.ts b/packages/language-service/src/binding_utils.ts new file mode 100644 index 0000000000000..99902a8aacf6b --- /dev/null +++ b/packages/language-service/src/binding_utils.ts @@ -0,0 +1,69 @@ +/** + * @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 + */ + +/** + * Matches an Angular attribute to a binding type. See `ATTR` for more details. + * + * This is adapted from packages/compiler/src/render3/r3_template_transform.ts + * to allow empty binding names and match template attributes. + */ +const BIND_NAME_REGEXP = + /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@)|(\*))(.*))|\[\(([^\)]*)\)\]|\[([^\]]*)\]|\(([^\)]*)\))$/; +/** + * Represents possible Angular attribute bindings, as indices on a match of `BIND_NAME_REGEXP`. + */ +export enum ATTR { + /** "bind-" */ + KW_BIND = 1, + /** "let-" */ + KW_LET = 2, + /** "ref-/#" */ + KW_REF = 3, + /** "on-" */ + KW_ON = 4, + /** "bindon-" */ + KW_BINDON = 5, + /** "@" */ + KW_AT = 6, + /** + * "*" + * Microsyntax template starts with '*'. See https://angular.io/api/core/TemplateRef + */ + KW_MICROSYNTAX = 7, + /** The identifier after "bind-", "let-", "ref-/#", "on-", "bindon-", "@", or "*" */ + IDENT_KW = 8, + /** Identifier inside [()] */ + IDENT_BANANA_BOX = 9, + /** Identifier inside [] */ + IDENT_PROPERTY = 10, + /** Identifier inside () */ + IDENT_EVENT = 11, +} + +export interface BindingDescriptor { + kind: ATTR; + name: string; +} +/** + * Returns a descriptor for a given Angular attribute, or undefined if the attribute is + * not an Angular attribute. + */ +export function getBindingDescriptor(attribute: string): BindingDescriptor|undefined { + const bindParts = attribute.match(BIND_NAME_REGEXP); + if (!bindParts) return; + // The first match element is skipped because it matches the entire attribute text, including the + // binding part. + const kind = bindParts.findIndex((val, i) => i > 0 && val !== undefined); + if (!(kind in ATTR)) { + throw TypeError(`"${kind}" is not a valid Angular binding kind for "${attribute}"`); + } + return { + kind, + name: bindParts[ATTR.IDENT_KW], + }; +} diff --git a/packages/language-service/src/common.ts b/packages/language-service/src/common.ts deleted file mode 100644 index 9da46502e5b0b..0000000000000 --- a/packages/language-service/src/common.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @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 {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CssSelector, Node as HtmlAst, ParseError, Parser, TemplateAst} from '@angular/compiler'; - -import {TemplateSource} from './types'; - -export interface AstResult { - htmlAst: HtmlAst[]; - templateAst: TemplateAst[]; - directive: CompileDirectiveMetadata; - directives: CompileDirectiveSummary[]; - pipes: CompilePipeSummary[]; - parseErrors?: ParseError[]; - expressionParser: Parser; - template: TemplateSource; -} - -export type SelectorInfo = { - selectors: CssSelector[], - map: Map<CssSelector, CompileDirectiveSummary> -}; diff --git a/packages/language-service/src/completions.ts b/packages/language-service/src/completions.ts index 0a01ebf5dd241..a637eba1da54b 100644 --- a/packages/language-service/src/completions.ts +++ b/packages/language-service/src/completions.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, AbsoluteSourceSpan, AstPath, AttrAst, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, Element, ElementAst, EmptyExpr, ExpressionBinding, HtmlAstPath, NAMED_ENTITIES, Node as HtmlAst, NullTemplateVisitor, ParseSpan, ReferenceAst, TagContentType, TemplateBinding, Text, VariableBinding, getHtmlTagDefinition} from '@angular/compiler'; +import {AbsoluteSourceSpan, AST, AstPath, AttrAst, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, Element, ElementAst, EmptyExpr, ExpressionBinding, getHtmlTagDefinition, HtmlAstPath, NAMED_ENTITIES, Node as HtmlAst, NullTemplateVisitor, ParseSpan, ReferenceAst, TagContentType, TemplateBinding, Text, VariableBinding} from '@angular/compiler'; import {$$, $_, isAsciiLetter, isDigit} from '@angular/compiler/src/chars'; -import {AstResult} from './common'; +import {ATTR, getBindingDescriptor} from './binding_utils'; import {getExpressionScope} from './expression_diagnostics'; import {getExpressionCompletions} from './expressions'; import {attributeNames, elementNames, eventNames, propertyNames} from './html_info'; @@ -45,35 +45,6 @@ const ANGULAR_ELEMENTS: ReadonlyArray<ng.CompletionEntry> = [ }, ]; -// This is adapted from packages/compiler/src/render3/r3_template_transform.ts -// to allow empty binding names. -const BIND_NAME_REGEXP = - /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*))|\[\(([^\)]*)\)\]|\[([^\]]*)\]|\(([^\)]*)\))$/; -enum ATTR { - // Group 1 = "bind-" - KW_BIND_IDX = 1, - // Group 2 = "let-" - KW_LET_IDX = 2, - // Group 3 = "ref-/#" - KW_REF_IDX = 3, - // Group 4 = "on-" - KW_ON_IDX = 4, - // Group 5 = "bindon-" - KW_BINDON_IDX = 5, - // Group 6 = "@" - KW_AT_IDX = 6, - // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@" - IDENT_KW_IDX = 7, - // Group 8 = identifier inside [()] - IDENT_BANANA_BOX_IDX = 8, - // Group 9 = identifier inside [] - IDENT_PROPERTY_IDX = 9, - // Group 10 = identifier inside () - IDENT_EVENT_IDX = 10, -} -// Microsyntax template starts with '*'. See https://angular.io/api/core/TemplateRef -const TEMPLATE_ATTR_PREFIX = '*'; - function isIdentifierPart(code: number) { // Identifiers consist of alphanumeric characters, '_', or '$'. return isAsciiLetter(code) || isDigit(code) || code == $$ || code == $_; @@ -83,12 +54,22 @@ function isIdentifierPart(code: number) { * Gets the span of word in a template that surrounds `position`. If there is no word around * `position`, nothing is returned. */ -function getBoundedWordSpan(templateInfo: AstResult, position: number): ts.TextSpan|undefined { +function getBoundedWordSpan( + templateInfo: ng.AstResult, position: number, ast: HtmlAst|undefined): ts.TextSpan|undefined { const {template} = templateInfo; const templateSrc = template.source; if (!templateSrc) return; + if (ast instanceof Element) { + // The HTML tag may include `-` (e.g. `app-root`), + // so use the HtmlAst to get the span before ayazhafiz refactor the code. + return { + start: templateInfo.template.span.start + ast.startSourceSpan!.start.offset + 1, + length: ast.name.length + }; + } + // TODO(ayazhafiz): A solution based on word expansion will always be expensive compared to one // based on ASTs. Whatever penalty we incur is probably manageable for small-length (i.e. the // majority of) identifiers, but the current solution involes a number of branchings and we can't @@ -145,7 +126,7 @@ function getBoundedWordSpan(templateInfo: AstResult, position: number): ts.TextS } export function getTemplateCompletions( - templateInfo: AstResult, position: number): ng.CompletionEntry[] { + templateInfo: ng.AstResult, position: number): ng.CompletionEntry[] { let result: ng.CompletionEntry[] = []; const {htmlAst, template} = templateInfo; // The templateNode starts at the delimiter character so we add 1 to skip it. @@ -213,15 +194,16 @@ export function getTemplateCompletions( null); } - const replacementSpan = getBoundedWordSpan(templateInfo, position); + const replacementSpan = getBoundedWordSpan(templateInfo, position, mostSpecific); return result.map(entry => { return { - ...entry, replacementSpan, + ...entry, + replacementSpan, }; }); } -function attributeCompletions(info: AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] { +function attributeCompletions(info: ng.AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] { const attr = path.tail; const elem = path.parentOf(attr); if (!(attr instanceof Attribute) || !(elem instanceof Element)) { @@ -232,34 +214,39 @@ function attributeCompletions(info: AstResult, path: AstPath<HtmlAst>): ng.Compl // matching using regex. This is because the regexp would incorrectly identify // bind parts for cases like [()|] // ^ cursor is here - const bindParts = attr.name.match(BIND_NAME_REGEXP); - const isTemplateRef = attr.name.startsWith(TEMPLATE_ATTR_PREFIX); - const isBinding = bindParts !== null || isTemplateRef; - - if (!isBinding) { + const binding = getBindingDescriptor(attr.name); + if (!binding) { + // This is a normal HTML attribute, not an Angular attribute. return attributeCompletionsForElement(info, elem.name); } const results: string[] = []; const ngAttrs = angularAttributes(info, elem.name); - if (!bindParts) { - // If bindParts is null then this must be a TemplateRef. - results.push(...ngAttrs.templateRefs); - } else if ( - bindParts[ATTR.KW_BIND_IDX] !== undefined || - bindParts[ATTR.IDENT_PROPERTY_IDX] !== undefined) { - // property binding via bind- or [] - results.push(...propertyNames(elem.name), ...ngAttrs.inputs); - } else if ( - bindParts[ATTR.KW_ON_IDX] !== undefined || bindParts[ATTR.IDENT_EVENT_IDX] !== undefined) { - // event binding via on- or () - results.push(...eventNames(elem.name), ...ngAttrs.outputs); - } else if ( - bindParts[ATTR.KW_BINDON_IDX] !== undefined || - bindParts[ATTR.IDENT_BANANA_BOX_IDX] !== undefined) { - // banana-in-a-box binding via bindon- or [()] - results.push(...ngAttrs.bananas); + switch (binding.kind) { + case ATTR.KW_MICROSYNTAX: + // template reference attribute: *attrName + results.push(...ngAttrs.templateRefs); + break; + + case ATTR.KW_BIND: + case ATTR.IDENT_PROPERTY: + // property binding via bind- or [] + results.push(...propertyNames(elem.name), ...ngAttrs.inputs); + break; + + case ATTR.KW_ON: + case ATTR.IDENT_EVENT: + // event binding via on- or () + results.push(...eventNames(elem.name), ...ngAttrs.outputs); + break; + + case ATTR.KW_BINDON: + case ATTR.IDENT_BANANA_BOX: + // banana-in-a-box binding via bindon- or [()] + results.push(...ngAttrs.bananas); + break; } + return results.map(name => { return { name, @@ -270,7 +257,7 @@ function attributeCompletions(info: AstResult, path: AstPath<HtmlAst>): ng.Compl } function attributeCompletionsForElement( - info: AstResult, elementName: string): ng.CompletionEntry[] { + info: ng.AstResult, elementName: string): ng.CompletionEntry[] { const results: ng.CompletionEntry[] = []; if (info.template instanceof InlineTemplate) { @@ -304,7 +291,8 @@ function attributeCompletionsForElement( * @param info Object that contains the template AST * @param htmlPath Path to the HTML node */ -function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.CompletionEntry[] { +function attributeValueCompletions( + info: ng.AstResult, htmlPath: HtmlAstPath): ng.CompletionEntry[] { // Find the corresponding Template AST path. const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position); const visitor = new ExpressionVisitor(info, htmlPath.position, () => { @@ -320,8 +308,8 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C // In order to provide accurate attribute value completion, we need to know // what the LHS is, and construct the proper AST if it is missing. const htmlAttr = htmlPath.tail as Attribute; - const bindParts = htmlAttr.name.match(BIND_NAME_REGEXP); - if (bindParts && bindParts[ATTR.KW_REF_IDX] !== undefined) { + const binding = getBindingDescriptor(htmlAttr.name); + if (binding && binding.kind === ATTR.KW_REF) { let refAst: ReferenceAst|undefined; let elemAst: ElementAst|undefined; if (templatePath.tail instanceof ReferenceAst) { @@ -331,7 +319,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C elemAst = parent; } } else if (templatePath.tail instanceof ElementAst) { - refAst = new ReferenceAst(htmlAttr.name, null !, htmlAttr.value, htmlAttr.valueSpan !); + refAst = new ReferenceAst(htmlAttr.name, null!, htmlAttr.value, htmlAttr.valueSpan!); elemAst = templatePath.tail; } if (refAst && elemAst) { @@ -340,13 +328,13 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C } else { // HtmlAst contains the `Attribute` node, however the corresponding `AttrAst` // node is missing from the TemplateAst. - const attrAst = new AttrAst(htmlAttr.name, htmlAttr.value, htmlAttr.valueSpan !); + const attrAst = new AttrAst(htmlAttr.name, htmlAttr.value, htmlAttr.valueSpan!); attrAst.visit(visitor, null); } return visitor.results; } -function elementCompletions(info: AstResult): ng.CompletionEntry[] { +function elementCompletions(info: ng.AstResult): ng.CompletionEntry[] { const results: ng.CompletionEntry[] = [...ANGULAR_ELEMENTS]; if (info.template instanceof InlineTemplate) { @@ -392,7 +380,7 @@ function entityCompletions(value: string, position: number): ng.CompletionEntry[ return result; } -function interpolationCompletions(info: AstResult, position: number): ng.CompletionEntry[] { +function interpolationCompletions(info: ng.AstResult, position: number): ng.CompletionEntry[] { // Look for an interpolation in at the position. const templatePath = findTemplateAstAt(info.templateAst, position); if (!templatePath.tail) { @@ -411,7 +399,7 @@ function interpolationCompletions(info: AstResult, position: number): ng.Complet // code checks for this case and returns element completions if it is detected or undefined // if it is not. function voidElementAttributeCompletions( - info: AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] { + info: ng.AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] { const tail = path.tail; if (tail instanceof Text) { const match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/); @@ -429,12 +417,14 @@ class ExpressionVisitor extends NullTemplateVisitor { private readonly completions = new Map<string, ng.CompletionEntry>(); constructor( - private readonly info: AstResult, private readonly position: number, + private readonly info: ng.AstResult, private readonly position: number, private readonly getExpressionScope: () => ng.SymbolTable) { super(); } - get results(): ng.CompletionEntry[] { return Array.from(this.completions.values()); } + get results(): ng.CompletionEntry[] { + return Array.from(this.completions.values()); + } visitDirectiveProperty(ast: BoundDirectivePropertyAst): void { this.processExpressionCompletions(ast.value); @@ -444,18 +434,21 @@ class ExpressionVisitor extends NullTemplateVisitor { this.processExpressionCompletions(ast.value); } - visitEvent(ast: BoundEventAst): void { this.processExpressionCompletions(ast.handler); } + visitEvent(ast: BoundEventAst): void { + this.processExpressionCompletions(ast.handler); + } visitElement(): void { // no-op for now } visitAttr(ast: AttrAst) { - if (ast.name.startsWith(TEMPLATE_ATTR_PREFIX)) { + const binding = getBindingDescriptor(ast.name); + if (binding && binding.kind === ATTR.KW_MICROSYNTAX) { // This a template binding given by micro syntax expression. // First, verify the attribute consists of some binding we can give completions for. // The sourceSpan of AttrAst points to the RHS of the attribute - const templateKey = ast.name.substring(TEMPLATE_ATTR_PREFIX.length); + const templateKey = binding.name; const templateValue = ast.sourceSpan.toString(); const templateUrl = ast.sourceSpan.start.file.url; // TODO(kyliau): We are unable to determine the absolute offset of the key @@ -465,13 +458,13 @@ class ExpressionVisitor extends NullTemplateVisitor { const {templateBindings} = this.info.expressionParser.parseTemplateBindings( templateKey, templateValue, templateUrl, absKeyOffset, absValueOffset); // Find the template binding that contains the position. - const binding = templateBindings.find(b => inSpan(this.position, b.sourceSpan)); + const templateBinding = templateBindings.find(b => inSpan(this.position, b.sourceSpan)); - if (!binding) { + if (!templateBinding) { return; } - this.microSyntaxInAttributeValue(ast, binding); + this.microSyntaxInAttributeValue(ast, templateBinding); } else { const expressionAst = this.info.expressionParser.parseBinding( ast.value, ast.sourceSpan.toString(), ast.sourceSpan.start.offset); @@ -577,7 +570,7 @@ class ExpressionVisitor extends NullTemplateVisitor { } } else if (binding instanceof ExpressionBinding) { if (inSpan(this.position, binding.value?.ast.sourceSpan)) { - this.processExpressionCompletions(binding.value !.ast); + this.processExpressionCompletions(binding.value!.ast); return; } else if (!binding.value && this.position > binding.key.span.end) { // No expression is defined for the value of the key expression binding, but the cursor is @@ -626,7 +619,7 @@ interface AngularAttributes { * @param info * @param elementName */ -function angularAttributes(info: AstResult, elementName: string): AngularAttributes { +function angularAttributes(info: ng.AstResult, elementName: string): AngularAttributes { const {selectors, map: selectorMap} = getSelectors(info); const templateRefs = new Set<string>(); const inputs = new Set<string>(); @@ -637,7 +630,7 @@ function angularAttributes(info: AstResult, elementName: string): AngularAttribu if (selector.element && selector.element !== elementName) { continue; } - const summary = selectorMap.get(selector) !; + const summary = selectorMap.get(selector)!; const hasTemplateRef = isStructuralDirective(summary.type); // attributes are listed in (attribute, value) pairs for (let i = 0; i < selector.attrs.length; i += 2) { diff --git a/packages/language-service/src/definitions.ts b/packages/language-service/src/definitions.ts index 17c14744d5c6c..0164ce2b248e0 100644 --- a/packages/language-service/src/definitions.ts +++ b/packages/language-service/src/definitions.ts @@ -7,12 +7,11 @@ */ import * as path from 'path'; -import * as ts from 'typescript'; // used as value and is provided at runtime -import {AstResult} from './common'; +import * as ts from 'typescript'; // used as value and is provided at runtime + import {locateSymbols} from './locate_symbol'; -import {getPropertyAssignmentFromValue, isClassDecoratorProperty} from './template'; -import {Span} from './types'; -import {findTightestNode} from './utils'; +import {AstResult, Span} from './types'; +import {findTightestNode, getPropertyAssignmentFromValue, isClassDecoratorProperty} from './utils'; /** * Convert Angular Span to TypeScript TextSpan. Angular Span has 'start' and diff --git a/packages/language-service/src/diagnostic_messages.ts b/packages/language-service/src/diagnostic_messages.ts index 885d781bd2992..f045d7f751d24 100644 --- a/packages/language-service/src/diagnostic_messages.ts +++ b/packages/language-service/src/diagnostic_messages.ts @@ -14,15 +14,15 @@ export interface DiagnosticMessage { kind: keyof typeof ts.DiagnosticCategory; } -type DiagnosticName = 'directive_not_in_module' | 'missing_template_and_templateurl' | - 'both_template_and_templateurl' | 'invalid_templateurl' | 'template_context_missing_member' | - 'callable_expression_expected_method_call' | 'call_target_not_callable' | - 'expression_might_be_null' | 'expected_a_number_type' | 'expected_a_string_or_number_type' | - 'expected_operands_of_similar_type_or_any' | 'unrecognized_operator' | - 'unrecognized_primitive' | 'no_pipe_found' | 'unable_to_resolve_compatible_call_signature' | - 'unable_to_resolve_signature' | 'could_not_resolve_type' | 'identifier_not_callable' | - 'identifier_possibly_undefined' | 'identifier_not_defined_in_app_context' | - 'identifier_not_defined_on_receiver' | 'identifier_is_private'; +type DiagnosticName = 'directive_not_in_module'|'missing_template_and_templateurl'| + 'both_template_and_templateurl'|'invalid_templateurl'|'template_context_missing_member'| + 'callable_expression_expected_method_call'|'call_target_not_callable'| + 'expression_might_be_null'|'expected_a_number_type'|'expected_a_string_or_number_type'| + 'expected_operands_of_comparable_types_or_any'|'unrecognized_operator'|'unrecognized_primitive'| + 'no_pipe_found'|'unable_to_resolve_compatible_call_signature'|'unable_to_resolve_signature'| + 'could_not_resolve_type'|'identifier_not_callable'|'identifier_possibly_undefined'| + 'identifier_not_defined_in_app_context'|'identifier_not_defined_on_receiver'| + 'identifier_is_private'; export const Diagnostic: Record<DiagnosticName, DiagnosticMessage> = { directive_not_in_module: { @@ -77,8 +77,8 @@ export const Diagnostic: Record<DiagnosticName, DiagnosticMessage> = { kind: 'Error', }, - expected_operands_of_similar_type_or_any: { - message: 'Expected operands to be of similar type or any', + expected_operands_of_comparable_types_or_any: { + message: 'Expected operands to be of comparable types or any', kind: 'Error', }, @@ -156,6 +156,7 @@ export function createDiagnostic( dm.message.replace(/%(\d+)/g, (_, index: string) => formatArgs[+index - 1]); return { kind: ts.DiagnosticCategory[dm.kind], - message: formattedMessage, span, + message: formattedMessage, + span, }; } diff --git a/packages/language-service/src/diagnostics.ts b/packages/language-service/src/diagnostics.ts index d437a918ea224..f5c55ecd82884 100644 --- a/packages/language-service/src/diagnostics.ts +++ b/packages/language-service/src/diagnostics.ts @@ -10,20 +10,17 @@ import {NgAnalyzedModules} from '@angular/compiler'; import * as path from 'path'; import * as ts from 'typescript'; -import {AstResult} from './common'; -import {Diagnostic, createDiagnostic} from './diagnostic_messages'; +import {createDiagnostic, Diagnostic} from './diagnostic_messages'; import {getTemplateExpressionDiagnostics} from './expression_diagnostics'; import * as ng from './types'; import {TypeScriptServiceHost} from './typescript_host'; import {findPropertyValueOfType, findTightestNode, offsetSpan, spanOf} from './utils'; - - /** * Return diagnostic information for the parsed AST of the template. * @param ast contains HTML and template AST */ -export function getTemplateDiagnostics(ast: AstResult): ng.Diagnostic[] { +export function getTemplateDiagnostics(ast: ng.AstResult): ng.Diagnostic[] { const {parseErrors, templateAst, htmlAst, template} = ast; if (parseErrors && parseErrors.length) { return parseErrors.map(e => { @@ -193,7 +190,7 @@ function chainDiagnostics(chain: ng.DiagnosticMessageChain): ts.DiagnosticMessag * @param file */ export function ngDiagnosticToTsDiagnostic( - d: ng.Diagnostic, file: ts.SourceFile | undefined): ts.Diagnostic { + d: ng.Diagnostic, file: ts.SourceFile|undefined): ts.Diagnostic { return { file, start: d.span.start, diff --git a/packages/language-service/src/expression_diagnostics.ts b/packages/language-service/src/expression_diagnostics.ts index 0af26418be9c6..1baa12c50ba21 100644 --- a/packages/language-service/src/expression_diagnostics.ts +++ b/packages/language-service/src/expression_diagnostics.ts @@ -6,32 +6,22 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, AstPath, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, CompileDirectiveSummary, CompileTypeMetadata, DirectiveAst, ElementAst, EmbeddedTemplateAst, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, ReferenceAst, TemplateAst, TemplateAstPath, VariableAst, identifierName, templateVisitAll, tokenReference} from '@angular/compiler'; +import {AST, AstPath, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, CompileDirectiveSummary, CompileTypeMetadata, DirectiveAst, ElementAst, EmbeddedTemplateAst, identifierName, ParseSourceSpan, RecursiveTemplateAstVisitor, ReferenceAst, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference, VariableAst} from '@angular/compiler'; -import {Diagnostic, createDiagnostic} from './diagnostic_messages'; +import {createDiagnostic, Diagnostic} from './diagnostic_messages'; import {AstType} from './expression_type'; import {BuiltinType, Definition, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols'; import * as ng from './types'; import {findOutputBinding, getPathToNodeAtPosition} from './utils'; -export interface DiagnosticTemplateInfo { - fileName?: string; - offset: number; - query: SymbolQuery; - members: SymbolTable; - htmlAst: Node[]; - templateAst: TemplateAst[]; - source: string; -} - -export function getTemplateExpressionDiagnostics(info: DiagnosticTemplateInfo): ng.Diagnostic[] { +export function getTemplateExpressionDiagnostics(info: ng.DiagnosticTemplateInfo): ng.Diagnostic[] { const visitor = new ExpressionDiagnosticsVisitor( info, (path: TemplateAstPath) => getExpressionScope(info, path)); templateVisitAll(visitor, info.templateAst); return visitor.diagnostics; } -function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] { +function getReferences(info: ng.DiagnosticTemplateInfo): SymbolDeclaration[] { const result: SymbolDeclaration[] = []; function processReferences(references: ReferenceAst[]) { @@ -44,7 +34,9 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] { name: reference.name, kind: 'reference', type: type || info.query.getBuiltinType(BuiltinType.Any), - get definition() { return getDefinitionOf(info, reference); } + get definition() { + return getDefinitionOf(info, reference); + } }); } } @@ -65,7 +57,7 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] { return result; } -function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined { +function getDefinitionOf(info: ng.DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined { if (info.fileName) { const templateOffset = info.offset; return [{ @@ -85,7 +77,7 @@ function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Defini * @param path template AST path */ function getVarDeclarations( - info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration[] { + info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration[] { const results: SymbolDeclaration[] = []; for (let current = path.head; current; current = path.childOf(current)) { if (!(current instanceof EmbeddedTemplateAst)) { @@ -109,7 +101,10 @@ function getVarDeclarations( results.push({ name: variable.name, kind: 'variable', - type: symbol, get definition() { return getDefinitionOf(info, variable); }, + type: symbol, + get definition() { + return getDefinitionOf(info, variable); + }, }); } } @@ -148,7 +143,7 @@ function getVariableTypeFromDirectiveContext( * @param templateElement */ function refinedVariableType( - value: string, mergedTable: SymbolTable, info: DiagnosticTemplateInfo, + value: string, mergedTable: SymbolTable, info: ng.DiagnosticTemplateInfo, templateElement: EmbeddedTemplateAst): Symbol { if (value === '$implicit') { // Special case: ngFor directive @@ -200,7 +195,7 @@ function refinedVariableType( } function getEventDeclaration( - info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration|undefined { + info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration|undefined { const event = path.tail; if (!(event instanceof BoundEventAst)) { // No event available in this context. @@ -235,7 +230,7 @@ function getEventDeclaration( * derived for. */ export function getExpressionScope( - info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolTable { + info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolTable { let result = info.members; const references = getReferences(info); const variables = getVarDeclarations(info, path); @@ -256,7 +251,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor { diagnostics: ng.Diagnostic[] = []; constructor( - private info: DiagnosticTemplateInfo, + private info: ng.DiagnosticTemplateInfo, private getExpressionScope: (path: TemplateAstPath, includeEvent: boolean) => SymbolTable) { super(); this.path = new AstPath<TemplateAst>([]); @@ -296,7 +291,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor { visitVariable(ast: VariableAst): void { const directive = this.directiveSummary; if (directive && ast.value) { - const context = this.info.query.getTemplateContext(directive.type.reference) !; + const context = this.info.query.getTemplateContext(directive.type.reference)!; if (context && !context.has(ast.value)) { const missingMember = ast.value === '$implicit' ? 'an implicit value' : `a member called '${ast.value}'`; @@ -322,7 +317,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor { // Find directive that references this template this.directiveSummary = - ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type)) !; + ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type))!; // Process children super.visitEmbeddedTemplate(ast, context); @@ -350,9 +345,13 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor { } } - private push(ast: TemplateAst) { this.path.push(ast); } + private push(ast: TemplateAst) { + this.path.push(ast); + } - private pop() { this.path.pop(); } + private pop() { + this.path.pop(); + } private absSpan(span: Span, additionalOffset: number = 0): Span { return { @@ -366,7 +365,7 @@ function hasTemplateReference(type: CompileTypeMetadata): boolean { if (type.diDeps) { for (let diDep of type.diDeps) { if (diDep.token && diDep.token.identifier && - identifierName(diDep.token !.identifier !) == 'TemplateRef') + identifierName(diDep.token!.identifier!) == 'TemplateRef') return true; } } diff --git a/packages/language-service/src/expression_type.ts b/packages/language-service/src/expression_type.ts index 5277ca3c55273..732fe97910704 100644 --- a/packages/language-service/src/expression_type.ts +++ b/packages/language-service/src/expression_type.ts @@ -8,11 +8,13 @@ import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler'; -import {Diagnostic, createDiagnostic} from './diagnostic_messages'; +import {createDiagnostic, Diagnostic} from './diagnostic_messages'; import {BuiltinType, Signature, Symbol, SymbolQuery, SymbolTable} from './symbols'; import * as ng from './types'; -export interface ExpressionDiagnosticsContext { inEvent?: boolean; } +interface ExpressionDiagnosticsContext { + inEvent?: boolean; +} // AstType calculatetype of the ast given AST element. export class AstType implements AstVisitor { @@ -22,7 +24,9 @@ export class AstType implements AstVisitor { private scope: SymbolTable, private query: SymbolQuery, private context: ExpressionDiagnosticsContext, private source: string) {} - getType(ast: AST): Symbol { return ast.visit(this); } + getType(ast: AST): Symbol { + return ast.visit(this); + } getDiagnostics(ast: AST): ng.Diagnostic[] { const type: Symbol = ast.visit(this); @@ -34,16 +38,6 @@ export class AstType implements AstVisitor { } visitBinary(ast: Binary): Symbol { - // Treat undefined and null as other. - function normalize(kind: BuiltinType, other: BuiltinType): BuiltinType { - switch (kind) { - case BuiltinType.Undefined: - case BuiltinType.Null: - return normalize(other, BuiltinType.Other); - } - return kind; - } - const getType = (ast: AST, operation: string): Symbol => { const type = this.getType(ast); if (type.nullable) { @@ -60,17 +54,14 @@ export class AstType implements AstVisitor { this.diagnostics.push(createDiagnostic(ast.span, Diagnostic.expression_might_be_null)); break; } - return this.query.getNonNullableType(type); } return type; }; const leftType = getType(ast.left, ast.operation); const rightType = getType(ast.right, ast.operation); - const leftRawKind = this.query.getTypeKind(leftType); - const rightRawKind = this.query.getTypeKind(rightType); - const leftKind = normalize(leftRawKind, rightRawKind); - const rightKind = normalize(rightRawKind, leftRawKind); + const leftKind = this.query.getTypeKind(leftType); + const rightKind = this.query.getTypeKind(rightType); // The following swtich implements operator typing similar to the // type production tables in the TypeScript specification. @@ -150,26 +141,15 @@ export class AstType implements AstVisitor { case '!=': case '===': case '!==': - switch (operKind) { - case BuiltinType.Any << 8 | BuiltinType.Any: - case BuiltinType.Any << 8 | BuiltinType.Boolean: - case BuiltinType.Any << 8 | BuiltinType.Number: - case BuiltinType.Any << 8 | BuiltinType.String: - case BuiltinType.Any << 8 | BuiltinType.Other: - case BuiltinType.Boolean << 8 | BuiltinType.Any: - case BuiltinType.Boolean << 8 | BuiltinType.Boolean: - case BuiltinType.Number << 8 | BuiltinType.Any: - case BuiltinType.Number << 8 | BuiltinType.Number: - case BuiltinType.String << 8 | BuiltinType.Any: - case BuiltinType.String << 8 | BuiltinType.String: - case BuiltinType.Other << 8 | BuiltinType.Any: - case BuiltinType.Other << 8 | BuiltinType.Other: - return this.query.getBuiltinType(BuiltinType.Boolean); - default: - this.diagnostics.push( - createDiagnostic(ast.span, Diagnostic.expected_operands_of_similar_type_or_any)); - return this.anyType; + if (!(leftKind & rightKind) && + !((leftKind | rightKind) & (BuiltinType.Null | BuiltinType.Undefined))) { + // Two values are comparable only if + // - they have some type overlap, or + // - at least one is not defined + this.diagnostics.push( + createDiagnostic(ast.span, Diagnostic.expected_operands_of_comparable_types_or_any)); } + return this.query.getBuiltinType(BuiltinType.Boolean); case '&&': return rightType; case '||': @@ -204,10 +184,10 @@ export class AstType implements AstVisitor { // support contextual typing of arguments so this is simpler than TypeScript's // version. const args = ast.args.map(arg => this.getType(arg)); - const target = this.getType(ast.target !); + const target = this.getType(ast.target!); if (!target || !target.callable) { this.diagnostics.push(createDiagnostic( - ast.span, Diagnostic.call_target_not_callable, this.sourceOf(ast.target !), target.name)); + ast.span, Diagnostic.call_target_not_callable, this.sourceOf(ast.target!), target.name)); return this.anyType; } const signature = target.selectSignature(args); @@ -221,7 +201,7 @@ export class AstType implements AstVisitor { return this.anyType; } - visitImplicitReceiver(ast: ImplicitReceiver): Symbol { + visitImplicitReceiver(_ast: ImplicitReceiver): Symbol { const _this = this; // Return a pseudo-symbol for the implicit receiver. // The members of the implicit receiver are what is defined by the @@ -237,11 +217,24 @@ export class AstType implements AstVisitor { public: true, definition: undefined, documentation: [], - members(): SymbolTable{return _this.scope;}, - signatures(): Signature[]{return [];}, - selectSignature(types): Signature | undefined{return undefined;}, - indexed(argument): Symbol | undefined{return undefined;}, - typeArguments(): Symbol[] | undefined{return undefined;}, + members(): SymbolTable { + return _this.scope; + }, + signatures(): Signature[] { + return []; + }, + selectSignature(_types): Signature | + undefined { + return undefined; + }, + indexed(_argument): Symbol | + undefined { + return undefined; + }, + typeArguments(): Symbol[] | + undefined { + return undefined; + }, }; } @@ -349,7 +342,7 @@ export class AstType implements AstVisitor { return this.getType(ast.value); } - visitQuote(ast: Quote) { + visitQuote(_ast: Quote) { // The type of a quoted expression is any. return this.query.getBuiltinType(BuiltinType.Any); } diff --git a/packages/language-service/src/expressions.ts b/packages/language-service/src/expressions.ts index 594a3c565b17b..1d27b209ab95f 100644 --- a/packages/language-service/src/expressions.ts +++ b/packages/language-service/src/expressions.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ASTWithSource, AstPath as AstPathBase, RecursiveAstVisitor} from '@angular/compiler'; +import {AST, AstPath as AstPathBase, ASTWithSource, RecursiveAstVisitor} from '@angular/compiler'; + import {AstType} from './expression_type'; import {BuiltinType, Span, Symbol, SymbolTable, TemplateSource} from './types'; import {inSpan} from './utils'; @@ -41,7 +42,7 @@ export function getExpressionCompletions( undefined { const path = findAstAt(ast, position); if (path.empty) return undefined; - const tail = path.tail !; + const tail = path.tail!; let result: SymbolTable|undefined = scope; function getType(ast: AST): Symbol { @@ -52,18 +53,20 @@ export function getExpressionCompletions( // (that is the scope of the implicit receiver) is the right scope as the user is typing the // beginning of an expression. tail.visit({ - visitBinary(ast) {}, - visitChain(ast) {}, - visitConditional(ast) {}, - visitFunctionCall(ast) {}, - visitImplicitReceiver(ast) {}, - visitInterpolation(ast) { result = undefined; }, - visitKeyedRead(ast) {}, - visitKeyedWrite(ast) {}, - visitLiteralArray(ast) {}, - visitLiteralMap(ast) {}, - visitLiteralPrimitive(ast) {}, - visitMethodCall(ast) {}, + visitBinary(_ast) {}, + visitChain(_ast) {}, + visitConditional(_ast) {}, + visitFunctionCall(_ast) {}, + visitImplicitReceiver(_ast) {}, + visitInterpolation(_ast) { + result = undefined; + }, + visitKeyedRead(_ast) {}, + visitKeyedWrite(_ast) {}, + visitLiteralArray(_ast) {}, + visitLiteralMap(_ast) {}, + visitLiteralPrimitive(_ast) {}, + visitMethodCall(_ast) {}, visitPipe(ast) { if (position >= ast.exp.span.end && (!ast.args || !ast.args.length || position < (<AST>ast.args[0]).span.start)) { @@ -71,8 +74,8 @@ export function getExpressionCompletions( result = templateInfo.query.getPipes(); } }, - visitPrefixNot(ast) {}, - visitNonNullAssert(ast) {}, + visitPrefixNot(_ast) {}, + visitNonNullAssert(_ast) {}, visitPropertyRead(ast) { const receiverType = getType(ast.receiver); result = receiverType ? receiverType.members() : scope; @@ -81,7 +84,7 @@ export function getExpressionCompletions( const receiverType = getType(ast.receiver); result = receiverType ? receiverType.members() : scope; }, - visitQuote(ast) { + visitQuote(_ast) { // For a quote, return the members of any (if there are any). result = templateInfo.query.getBuiltinType(BuiltinType.Any).members(); }, @@ -111,7 +114,7 @@ export function getExpressionSymbol( templateInfo: TemplateSource): {symbol: Symbol, span: Span}|undefined { const path = findAstAt(ast, position, /* excludeEmpty */ true); if (path.empty) return undefined; - const tail = path.tail !; + const tail = path.tail!; function getType(ast: AST): Symbol { return new AstType(scope, templateInfo.query, {}, templateInfo.source).getType(ast); @@ -124,17 +127,17 @@ export function getExpressionSymbol( // (that is the scope of the implicit receiver) is the right scope as the user is typing the // beginning of an expression. tail.visit({ - visitBinary(ast) {}, - visitChain(ast) {}, - visitConditional(ast) {}, - visitFunctionCall(ast) {}, - visitImplicitReceiver(ast) {}, - visitInterpolation(ast) {}, - visitKeyedRead(ast) {}, - visitKeyedWrite(ast) {}, - visitLiteralArray(ast) {}, - visitLiteralMap(ast) {}, - visitLiteralPrimitive(ast) {}, + visitBinary(_ast) {}, + visitChain(_ast) {}, + visitConditional(_ast) {}, + visitFunctionCall(_ast) {}, + visitImplicitReceiver(_ast) {}, + visitInterpolation(_ast) {}, + visitKeyedRead(_ast) {}, + visitKeyedWrite(_ast) {}, + visitLiteralArray(_ast) {}, + visitLiteralMap(_ast) {}, + visitLiteralPrimitive(_ast) {}, visitMethodCall(ast) { const receiverType = getType(ast.receiver); symbol = receiverType && receiverType.members().get(ast.name); @@ -156,8 +159,8 @@ export function getExpressionSymbol( }; } }, - visitPrefixNot(ast) {}, - visitNonNullAssert(ast) {}, + visitPrefixNot(_ast) {}, + visitNonNullAssert(_ast) {}, visitPropertyRead(ast) { const receiverType = getType(ast.receiver); symbol = receiverType && receiverType.members().get(ast.name); @@ -174,7 +177,7 @@ export function getExpressionSymbol( // ^^^^^^ value; visited separately as a nested AST span = {start, end: start + ast.name.length}; }, - visitQuote(ast) {}, + visitQuote(_ast) {}, visitSafeMethodCall(ast) { const receiverType = getType(ast.receiver); symbol = receiverType && receiverType.members().get(ast.name); diff --git a/packages/language-service/src/hover.ts b/packages/language-service/src/hover.ts index 145fdfbc6b4c5..9e30ac861478b 100644 --- a/packages/language-service/src/hover.ts +++ b/packages/language-service/src/hover.ts @@ -8,7 +8,6 @@ import {NgAnalyzedModules} from '@angular/compiler'; import * as ts from 'typescript'; -import {AstResult} from './common'; import {locateSymbols} from './locate_symbol'; import * as ng from './types'; import {inSpan} from './utils'; @@ -27,7 +26,8 @@ const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.inter * @param analyzedModules all NgModules in the program. */ export function getTemplateHover( - info: AstResult, position: number, analyzedModules: NgAnalyzedModules): ts.QuickInfo|undefined { + info: ng.AstResult, position: number, analyzedModules: NgAnalyzedModules): ts.QuickInfo| + undefined { const symbolInfo = locateSymbols(info, position)[0]; if (!symbolInfo) { return; @@ -37,14 +37,15 @@ export function getTemplateHover( // The container is either the symbol's container (for example, 'AppComponent' // is the container of the symbol 'title' in its template) or the NgModule // that the directive belongs to (the container of AppComponent is AppModule). - let containerName: string|undefined = symbol.container ?.name; + let containerName: string|undefined = symbol.container?.name; if (!containerName && staticSymbol) { // If there is a static symbol then the target is a directive. const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol); - containerName = ngModule ?.type.reference.name; + containerName = ngModule?.type.reference.name; } - return createQuickInfo(symbol.name, symbol.kind, span, containerName, symbol.type?.name, symbol.documentation); + return createQuickInfo( + symbol.name, symbol.kind, span, containerName, symbol.type?.name, symbol.documentation); } /** @@ -63,7 +64,7 @@ export function getTsHover( const kind = metadata.isComponent ? 'component' : 'directive'; const textSpan = ts.createTextSpanFromBounds(declarationSpan.start, declarationSpan.end); const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol); - const moduleName = ngModule ?.type.reference.name; + const moduleName = ngModule?.type.reference.name; return createQuickInfo( directiveName, kind, textSpan, moduleName, ts.ScriptElementKind.classElement); } diff --git a/packages/language-service/src/html_info.ts b/packages/language-service/src/html_info.ts index b54243ba2fc32..934c9889813f0 100644 --- a/packages/language-service/src/html_info.ts +++ b/packages/language-service/src/html_info.ts @@ -10,7 +10,7 @@ // This section defines the HTML elements and attribute surface of HTML 4 // which is derived from https://www.w3.org/TR/html4/strict.dtd -type attrType = string | string[]; +type attrType = string|string[]; type hash<T> = { [name: string]: T }; @@ -104,7 +104,9 @@ const groups: hash<number>[] = [ {class: 1, style: 1}, {hreflang: 2, rel: 1, rev: 1}, {ismap: 7}, - { defer: 25, event: 1, for : 1 } + { + defer: 25, event: 1, for: 1 + } ]; const elements: {[name: string]: number[]} = { @@ -193,7 +195,7 @@ export function elementNames(): string[] { return Object.keys(elements).sort().map(v => v.toLowerCase()); } -function compose(indexes: number[] | undefined): hash<attrType> { +function compose(indexes: number[]|undefined): hash<attrType> { const result: hash<attrType> = {}; if (indexes) { for (let index of indexes) { @@ -415,7 +417,9 @@ export class SchemaInformation { }); } - allKnownElements(): string[] { return Object.keys(this.schema); } + allKnownElements(): string[] { + return Object.keys(this.schema); + } eventsOf(elementName: string): string[] { const elementType = this.schema[elementName.toLowerCase()] || {}; @@ -449,7 +453,3 @@ export function eventNames(elementName: string): string[] { export function propertyNames(elementName: string): string[] { return SchemaInformation.instance.propertiesOf(elementName); } - -export function propertyType(elementName: string, propertyName: string): string { - return SchemaInformation.instance.typeOf(elementName, propertyName); -} diff --git a/packages/language-service/src/language_service.ts b/packages/language-service/src/language_service.ts index c6d1faacfec0b..375a6f39d0abe 100644 --- a/packages/language-service/src/language_service.ts +++ b/packages/language-service/src/language_service.ts @@ -49,7 +49,7 @@ class LanguageServiceImpl implements ng.LanguageService { getCompletionsAtPosition( fileName: string, position: number, - options?: tss.GetCompletionsAtPositionOptions): tss.CompletionInfo|undefined { + _options?: tss.GetCompletionsAtPositionOptions): tss.CompletionInfo|undefined { this.host.getAnalyzedModules(); // same role as 'synchronizeHostData' const ast = this.host.getTemplateAstAtPosition(fileName, position); if (!ast) { diff --git a/packages/language-service/src/locate_symbol.ts b/packages/language-service/src/locate_symbol.ts index 1f1cbc286fd16..3b6e4024dc55f 100644 --- a/packages/language-service/src/locate_symbol.ts +++ b/packages/language-service/src/locate_symbol.ts @@ -6,21 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, VariableBinding, templateVisitAll, tokenReference} from '@angular/compiler'; +import {AST, Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference, VariableBinding} from '@angular/compiler'; import * as tss from 'typescript/lib/tsserverlibrary'; -import {AstResult} from './common'; import {getExpressionScope} from './expression_diagnostics'; import {getExpressionSymbol} from './expressions'; -import {Definition, DirectiveKind, Span, Symbol} from './types'; +import {AstResult, Definition, DirectiveKind, Span, Symbol, SymbolInfo} from './types'; import {diagnosticInfoFromTemplateInfo, findOutputBinding, findTemplateAstAt, getPathToNodeAtPosition, inSpan, invertMap, isNarrower, offsetSpan, spanOf} from './utils'; -export interface SymbolInfo { - symbol: Symbol; - span: tss.TextSpan; - staticSymbol?: StaticSymbol; -} - /** * Traverses a template AST and locates symbol(s) at a specified position. * @param info template AST information set @@ -77,7 +70,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult): } if (result) { symbol = result.symbol; - span = offsetSpan(result.span, attribute.valueSpan !.start.offset); + span = offsetSpan(result.span, attribute.valueSpan!.start.offset); } return true; } @@ -86,8 +79,8 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult): }; ast.visit( { - visitNgContent(ast) {}, - visitEmbeddedTemplate(ast) {}, + visitNgContent(_ast) {}, + visitEmbeddedTemplate(_ast) {}, visitElement(ast) { const component = ast.directives.find(d => d.directive.isComponent); if (component) { @@ -113,7 +106,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult): symbol = ast.value && info.template.query.getTypeSymbol(tokenReference(ast.value)); span = spanOf(ast); }, - visitVariable(ast) {}, + visitVariable(_ast) {}, visitEvent(ast) { if (!attributeValueSymbol(ast.handler)) { symbol = findOutputBinding(ast, path, info.template.query); @@ -121,7 +114,9 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult): span = spanOf(ast); } }, - visitElementProperty(ast) { attributeValueSymbol(ast.value); }, + visitElementProperty(ast) { + attributeValueSymbol(ast.value); + }, visitAttr(ast) { const element = path.first(ElementAst); if (!element) return; @@ -156,7 +151,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult): } } }, - visitText(ast) {}, + visitText(_ast) {}, visitDirective(ast) { // Need to cast because 'reference' is typed as any staticSymbol = ast.directive.type.reference as StaticSymbol; @@ -188,7 +183,8 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult): const {start, end} = offsetSpan(span, info.template.span.start); return { symbol, - span: tss.createTextSpanFromBounds(start, end), staticSymbol, + span: tss.createTextSpanFromBounds(start, end), + staticSymbol, }; } } @@ -216,7 +212,7 @@ function getSymbolInMicrosyntax(info: AstResult, path: TemplateAstPath, attribut if (inSpan(path.position, tb.value?.ast.sourceSpan)) { const dinfo = diagnosticInfoFromTemplateInfo(info); const scope = getExpressionScope(dinfo, path); - result = getExpressionSymbol(scope, tb.value !, path.position, info.template); + result = getExpressionSymbol(scope, tb.value!, path.position, info.template); } else if (inSpan(path.position, tb.sourceSpan)) { const template = path.first(EmbeddedTemplateAst); if (template) { @@ -277,7 +273,9 @@ function findParentOfBinding( } visitDirective(ast: DirectiveAst) { - const result = this.visitChildren(ast, visit => { visit(ast.inputs); }); + const result = this.visitChildren(ast, visit => { + visit(ast.inputs); + }); return result; } @@ -309,33 +307,63 @@ function findInputBinding(info: AstResult, name: string, directiveAst: Directive */ class OverrideKindSymbol implements Symbol { public readonly kind: DirectiveKind; - constructor(private sym: Symbol, kindOverride: DirectiveKind) { this.kind = kindOverride; } + constructor(private sym: Symbol, kindOverride: DirectiveKind) { + this.kind = kindOverride; + } - get name(): string { return this.sym.name; } + get name(): string { + return this.sym.name; + } - get language(): string { return this.sym.language; } + get language(): string { + return this.sym.language; + } - get type(): Symbol|undefined { return this.sym.type; } + get type(): Symbol|undefined { + return this.sym.type; + } - get container(): Symbol|undefined { return this.sym.container; } + get container(): Symbol|undefined { + return this.sym.container; + } - get public(): boolean { return this.sym.public; } + get public(): boolean { + return this.sym.public; + } - get callable(): boolean { return this.sym.callable; } + get callable(): boolean { + return this.sym.callable; + } - get nullable(): boolean { return this.sym.nullable; } + get nullable(): boolean { + return this.sym.nullable; + } - get definition(): Definition { return this.sym.definition; } + get definition(): Definition { + return this.sym.definition; + } - get documentation(): ts.SymbolDisplayPart[] { return this.sym.documentation; } + get documentation(): ts.SymbolDisplayPart[] { + return this.sym.documentation; + } - members() { return this.sym.members(); } + members() { + return this.sym.members(); + } - signatures() { return this.sym.signatures(); } + signatures() { + return this.sym.signatures(); + } - selectSignature(types: Symbol[]) { return this.sym.selectSignature(types); } + selectSignature(types: Symbol[]) { + return this.sym.selectSignature(types); + } - indexed(argument: Symbol) { return this.sym.indexed(argument); } + indexed(argument: Symbol) { + return this.sym.indexed(argument); + } - typeArguments(): Symbol[]|undefined { return this.sym.typeArguments(); } + typeArguments(): Symbol[]|undefined { + return this.sym.typeArguments(); + } } diff --git a/packages/language-service/src/reflector_host.ts b/packages/language-service/src/reflector_host.ts index 13ba23a885449..6a1b83793236c 100644 --- a/packages/language-service/src/reflector_host.ts +++ b/packages/language-service/src/reflector_host.ts @@ -7,7 +7,7 @@ */ import {StaticSymbolResolverHost} from '@angular/compiler'; -import {MetadataCollector, MetadataReaderHost, createMetadataReaderCache, readMetadata} from '@angular/compiler-cli/src/language_services'; +import {createMetadataReaderCache, MetadataCollector, MetadataReaderHost, readMetadata} from '@angular/compiler-cli/src/language_services'; import * as path from 'path'; import * as ts from 'typescript'; @@ -26,10 +26,10 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost, Me private readonly tsLSHost: ts.LanguageServiceHost, private readonly getProgram: () => ts.Program) { if (tsLSHost.directoryExists) { - this.directoryExists = directoryName => tsLSHost.directoryExists !(directoryName); + this.directoryExists = directoryName => tsLSHost.directoryExists!(directoryName); } if (tsLSHost.realpath) { - this.realpath = path => tsLSHost.realpath !(path); + this.realpath = path => tsLSHost.realpath!(path); } } @@ -53,14 +53,14 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost, Me // resolution, and it's used by Angular to read metadata.json during // metadata resolution. if (this.tsLSHost.readFile) { - return this.tsLSHost.readFile(fileName) !; + return this.tsLSHost.readFile(fileName)!; } // As a fallback, read the JSON files from the editor snapshot. const snapshot = this.tsLSHost.getScriptSnapshot(fileName); if (!snapshot) { // MetadataReaderHost readFile() declaration should be // `readFile(fileName: string): string | undefined` - return undefined !; + return undefined!; } return snapshot.getText(0, snapshot.getLength()); } @@ -120,5 +120,7 @@ export class ReflectorHost implements StaticSymbolResolverHost { return resolved ? resolved.resolvedFileName : null; } - getOutputName(filePath: string) { return filePath; } + getOutputName(filePath: string) { + return filePath; + } } diff --git a/packages/language-service/src/symbols.ts b/packages/language-service/src/symbols.ts index da3f851e24dc2..a3d3426db0005 100644 --- a/packages/language-service/src/symbols.ts +++ b/packages/language-service/src/symbols.ts @@ -40,7 +40,7 @@ export interface Location { /** * A defnition location(s). */ -export type Definition = Location[] | undefined; +export type Definition = Location[]|undefined; /** * A symbol describing a language element that can be referenced by expressions @@ -192,42 +192,47 @@ export enum BuiltinType { /** * The type is a type that can hold any other type. */ - Any, + Any = -1, // equivalent to b11..11 = String | Union | ... + + /** Unknown types are functionally identical to any. */ + Unknown = -1, /** * The type of a string literal. */ - String, + String = 1 << 0, /** * The type of a numeric literal. */ - Number, + Number = 1 << 1, /** * The type of the `true` and `false` literals. */ - Boolean, + Boolean = 1 << 2, /** * The type of the `undefined` literal. */ - Undefined, + Undefined = 1 << 3, /** * the type of the `null` literal. */ - Null, + Null = 1 << 4, /** * the type is an unbound type parameter. */ - Unbound, + Unbound = 1 << 5, /** * Not a built-in type. */ - Other + Other = 1 << 6, + + Object = 1 << 7, } /** @@ -235,8 +240,8 @@ export enum BuiltinType { * * @publicApi */ -export type DeclarationKind = 'attribute' | 'html attribute' | 'component' | 'element' | 'entity' | - 'key' | 'method' | 'pipe' | 'property' | 'type' | 'reference' | 'variable'; +export type DeclarationKind = 'attribute'|'html attribute'|'component'|'element'|'entity'|'key'| + 'method'|'pipe'|'property'|'type'|'reference'|'variable'; /** * Describes a symbol to type binding used to build a symbol table. @@ -287,7 +292,7 @@ export interface PipeInfo { * * @publicApi */ -export type Pipes = PipeInfo[] | undefined; +export type Pipes = PipeInfo[]|undefined; /** * Describes the language context in which an Angular expression is evaluated. diff --git a/packages/language-service/src/template.ts b/packages/language-service/src/template.ts index e882b1ca81017..5e88bf97243d4 100644 --- a/packages/language-service/src/template.ts +++ b/packages/language-service/src/template.ts @@ -39,7 +39,9 @@ abstract class BaseTemplate implements ng.TemplateSource { /** * Return the Angular StaticSymbol for the class that contains this template. */ - get type() { return this.classSymbol; } + get type() { + return this.classSymbol; + } /** * Return a Map-like data structure that allows users to retrieve some or all @@ -130,62 +132,3 @@ export class ExternalTemplate extends BaseTemplate { }; } } - -/** - * Returns a property assignment from the assignment value, or `undefined` if there is no - * assignment. - */ -export function getPropertyAssignmentFromValue(value: ts.Node): ts.PropertyAssignment|undefined { - if (!value.parent || !ts.isPropertyAssignment(value.parent)) { - return; - } - return value.parent; -} - -/** - * Given a decorator property assignment, return the ClassDeclaration node that corresponds to the - * directive class the property applies to. - * If the property assignment is not on a class decorator, no declaration is returned. - * - * For example, - * - * @Component({ - * template: '<div></div>' - * ^^^^^^^^^^^^^^^^^^^^^^^---- property assignment - * }) - * class AppComponent {} - * ^---- class declaration node - * - * @param propAsgn property assignment - */ -export function getClassDeclFromDecoratorProp(propAsgnNode: ts.PropertyAssignment): - ts.ClassDeclaration|undefined { - if (!propAsgnNode.parent || !ts.isObjectLiteralExpression(propAsgnNode.parent)) { - return; - } - const objLitExprNode = propAsgnNode.parent; - if (!objLitExprNode.parent || !ts.isCallExpression(objLitExprNode.parent)) { - return; - } - const callExprNode = objLitExprNode.parent; - if (!callExprNode.parent || !ts.isDecorator(callExprNode.parent)) { - return; - } - const decorator = callExprNode.parent; - if (!decorator.parent || !ts.isClassDeclaration(decorator.parent)) { - return; - } - const classDeclNode = decorator.parent; - return classDeclNode; -} - -/** - * Determines if a property assignment is on a class decorator. - * See `getClassDeclFromDecoratorProperty`, which gets the class the decorator is applied to, for - * more details. - * - * @param prop property assignment - */ -export function isClassDecoratorProperty(propAsgn: ts.PropertyAssignment): boolean { - return !!getClassDeclFromDecoratorProp(propAsgn); -} diff --git a/packages/language-service/src/ts_plugin.ts b/packages/language-service/src/ts_plugin.ts index e2d4594cc9c10..d7774bf222c8c 100644 --- a/packages/language-service/src/ts_plugin.ts +++ b/packages/language-service/src/ts_plugin.ts @@ -27,8 +27,7 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService { const ngLS = createLanguageService(ngLSHost); function getCompletionsAtPosition( - fileName: string, position: number, - options: tss.GetCompletionsAtPositionOptions | undefined) { + fileName: string, position: number, options: tss.GetCompletionsAtPositionOptions|undefined) { if (!angularOnly) { const results = tsLS.getCompletionsAtPosition(fileName, position, options); if (results && results.entries.length) { @@ -93,8 +92,11 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService { {}, tsLS, // Then override the methods supported by Angular language service { - getCompletionsAtPosition, getQuickInfoAtPosition, getSemanticDiagnostics, - getDefinitionAtPosition, getDefinitionAndBoundSpan, + getCompletionsAtPosition, + getQuickInfoAtPosition, + getSemanticDiagnostics, + getDefinitionAtPosition, + getDefinitionAndBoundSpan, }); return proxy; } diff --git a/packages/language-service/src/types.ts b/packages/language-service/src/types.ts index cb349eea4d3bb..c76d83a373815 100644 --- a/packages/language-service/src/types.ts +++ b/packages/language-service/src/types.ts @@ -6,26 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDirectiveMetadata, NgAnalyzedModules, StaticSymbol} from '@angular/compiler'; +import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CssSelector, NgAnalyzedModules, Node as HtmlAst, ParseError, Parser, StaticSymbol, TemplateAst} from '@angular/compiler'; import * as ts from 'typescript'; -import {AstResult} from './common'; -import {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols'; - -export { - BuiltinType, - DeclarationKind, - Definition, - PipeInfo, - Pipes, - Signature, - Span, - StaticSymbol, - Symbol, - SymbolDeclaration, - SymbolQuery, - SymbolTable -}; +import {Span, Symbol, SymbolQuery, SymbolTable} from './symbols'; + +export {StaticSymbol} from '@angular/compiler'; +export {BuiltinType, Definition, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols'; /** * The information `LanguageService` needs from the `LanguageServiceHost` to describe the content of @@ -67,15 +54,6 @@ export interface TemplateSource { readonly fileName: string; } -/** - * A sequence of template sources. - * - * A host type; see `LanguageServiceHost`. - * - * @publicApi - */ -export type TemplateSources = TemplateSource[] | undefined; - /** * Error information found getting declaration information * @@ -132,15 +110,6 @@ export interface Declaration { readonly errors: DeclarationError[]; } -/** - * A sequence of declarations. - * - * A host type; see `LanguageServiceHost`. - * - * @publicApi - */ -export type Declarations = Declaration[]; - /** * The host for a `LanguageService`. This provides all the `LanguageService` requires to respond * to the `LanguageService` requests. @@ -178,7 +147,7 @@ export interface LanguageServiceHost { /** * Returns the Angular declarations in the given file. */ - getDeclarations(fileName: string): Declarations; + getDeclarations(fileName: string): Declaration[]; /** * Return a summary of all Angular modules in the project. @@ -196,45 +165,6 @@ export interface LanguageServiceHost { getTemplateAstAtPosition(fileName: string, position: number): AstResult|undefined; } -/** - * An item of the completion result to be displayed by an editor. - * - * A `LanguageService` interface. - * - * @publicApi - */ -export interface Completion { - /** - * The kind of completion. - */ - kind: DeclarationKind; - - /** - * The name of the completion to be displayed - */ - name: string; - - /** - * The key to use to sort the completions for display. - */ - sort: string; -} - -/** - * A sequence of completions. - * - * @deprecated - */ -export type Completions = Completion[]; - -/** - * A file and span. - */ -export interface Location { - fileName: string; - span: Span; -} - /** * The type of Angular directive. Used for QuickInfo in template. */ @@ -264,7 +194,7 @@ export enum CompletionKind { VARIABLE = 'variable', } -export type CompletionEntry = Omit<ts.CompletionEntry, 'kind'>& { +export type CompletionEntry = Omit<ts.CompletionEntry, 'kind'>&{ kind: CompletionKind, }; @@ -312,45 +242,6 @@ export interface Diagnostic { message: string|DiagnosticMessageChain; } -/** - * A sequence of diagnostic message. - * - * @deprecated - */ -export type Diagnostics = Diagnostic[]; - -/** - * A section of hover text. If the text is code then language should be provided. - * Otherwise the text is assumed to be Markdown text that will be sanitized. - */ -export interface HoverTextSection { - /** - * Source code or markdown text describing the symbol a the hover location. - */ - readonly text: string; - - /** - * The language of the source if `text` is a source code fragment. - */ - readonly language?: string; -} - -/** - * Hover information for a symbol at the hover location. - */ -export interface Hover { - /** - * The hover text to display for the symbol at the hover location. If the text includes - * source code, the section will specify which language it should be interpreted as. - */ - readonly text: HoverTextSection[]; - - /** - * The span of source the hover covers. - */ - readonly span: Span; -} - /** * An instance of an Angular language service created by `createLanguageService()`. * @@ -361,5 +252,41 @@ export interface Hover { * @publicApi */ export type LanguageService = Pick< - ts.LanguageService, 'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'| - 'getQuickInfoAtPosition'|'getSemanticDiagnostics'>; + ts.LanguageService, + 'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'|'getQuickInfoAtPosition'| + 'getSemanticDiagnostics'>; + +/** Information about an Angular template AST. */ +export interface AstResult { + htmlAst: HtmlAst[]; + templateAst: TemplateAst[]; + directive: CompileDirectiveMetadata; + directives: CompileDirectiveSummary[]; + pipes: CompilePipeSummary[]; + parseErrors?: ParseError[]; + expressionParser: Parser; + template: TemplateSource; +} + +/** Information about a directive's selectors. */ +export type SelectorInfo = { + selectors: CssSelector[], + map: Map<CssSelector, CompileDirectiveSummary> +}; + +export interface SymbolInfo { + symbol: Symbol; + span: ts.TextSpan; + staticSymbol?: StaticSymbol; +} + +/** TODO: this should probably be merged with AstResult */ +export interface DiagnosticTemplateInfo { + fileName?: string; + offset: number; + query: SymbolQuery; + members: SymbolTable; + htmlAst: HtmlAst[]; + templateAst: TemplateAst[]; + source: string; +} diff --git a/packages/language-service/src/typescript_host.ts b/packages/language-service/src/typescript_host.ts index e7a6e8d9ef82a..836b8cc9569b8 100644 --- a/packages/language-service/src/typescript_host.ts +++ b/packages/language-service/src/typescript_host.ts @@ -6,16 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotSummaryResolver, CompileDirectiveSummary, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver, isFormattedError} from '@angular/compiler'; +import {analyzeNgModules, AotSummaryResolver, CompileDirectiveSummary, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, createOfflineCompileUrlResolver, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, isFormattedError, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, Parser, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser} from '@angular/compiler'; import {SchemaMetadata, ViewEncapsulation, ɵConsole as Console} from '@angular/core'; import * as tss from 'typescript/lib/tsserverlibrary'; -import {AstResult} from './common'; import {createLanguageService} from './language_service'; import {ReflectorHost} from './reflector_host'; -import {ExternalTemplate, InlineTemplate, getClassDeclFromDecoratorProp, getPropertyAssignmentFromValue} from './template'; -import {Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types'; -import {findTightestNode, getDirectiveClassLike} from './utils'; +import {ExternalTemplate, InlineTemplate} from './template'; +import {AstResult, Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types'; +import {findTightestNode, getClassDeclFromDecoratorProp, getDirectiveClassLike, getPropertyAssignmentFromValue} from './utils'; /** @@ -35,14 +34,18 @@ export function createLanguageServiceFromTypescript( * syntactically incorrect templates. */ export class DummyHtmlParser extends HtmlParser { - parse(): ParseTreeResult { return new ParseTreeResult([], []); } + parse(): ParseTreeResult { + return new ParseTreeResult([], []); + } } /** * Avoid loading resources in the language servcie by using a dummy loader. */ export class DummyResourceLoader extends ResourceLoader { - get(url: string): Promise<string> { return Promise.resolve(''); } + get(_url: string): Promise<string> { + return Promise.resolve(''); + } } /** @@ -74,10 +77,18 @@ export class TypeScriptServiceHost implements LanguageServiceHost { readonly tsLsHost: tss.LanguageServiceHost, private readonly tsLS: tss.LanguageService) { this.summaryResolver = new AotSummaryResolver( { - loadSummary(filePath: string) { return null; }, - isSourceFile(sourceFilePath: string) { return true; }, - toSummaryFileName(sourceFilePath: string) { return sourceFilePath; }, - fromSummaryFileName(filePath: string): string{return filePath;}, + loadSummary(_filePath: string) { + return null; + }, + isSourceFile(_sourceFilePath: string) { + return true; + }, + toSummaryFileName(sourceFilePath: string) { + return sourceFilePath; + }, + fromSummaryFileName(filePath: string): string { + return filePath; + }, }, this.staticSymbolCache); this.reflectorHost = new ReflectorHost(() => this.program, tsLsHost); @@ -159,7 +170,11 @@ export class TypeScriptServiceHost implements LanguageServiceHost { this.collectedErrors.clear(); this.resolver.clearCache(); - const analyzeHost = {isSourceFile(filePath: string) { return true; }}; + const analyzeHost = { + isSourceFile(_filePath: string) { + return true; + } + }; const programFiles = this.program.getSourceFiles().map(sf => sf.fileName); this.analyzedModules = analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver); @@ -168,7 +183,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost { const urlResolver = createOfflineCompileUrlResolver(); for (const ngModule of this.analyzedModules.ngModules) { for (const directive of ngModule.declaredDirectives) { - const {metadata} = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference) !; + const {metadata} = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference)!; if (metadata.isComponent && metadata.template && metadata.template.templateUrl) { const templateName = urlResolver.resolve( this.reflector.componentModuleUrl(directive.reference), @@ -494,9 +509,9 @@ export class TypeScriptServiceHost implements LanguageServiceHost { const parser = new TemplateParser( new CompilerConfig(), this.reflector, expressionParser, new DomElementSchemaRegistry(), htmlParser, - null !, // console - [] // tranforms - ); + null!, // console + [] // tranforms + ); const htmlResult = htmlParser.parse(template.source, fileName, { tokenizeExpansionForms: true, preserveLineEndings: true, // do not convert CRLF to LF @@ -509,8 +524,12 @@ export class TypeScriptServiceHost implements LanguageServiceHost { return { htmlAst: htmlResult.rootNodes, templateAst: parseResult.templateAst, - directive: data.metadata, directives, pipes, - parseErrors: parseResult.errors, expressionParser, template, + directive: data.metadata, + directives, + pipes, + parseErrors: parseResult.errors, + expressionParser, + template, }; } @@ -574,7 +593,7 @@ function spanOf(node: tss.Node): Span { function spanAt(sourceFile: tss.SourceFile, line: number, column: number): Span|undefined { if (line != null && column != null) { const position = tss.getPositionOfLineAndCharacter(sourceFile, line, column); - const findChild = function findChild(node: tss.Node): tss.Node | undefined { + const findChild = function findChild(node: tss.Node): tss.Node|undefined { if (node.kind > tss.SyntaxKind.LastToken && node.pos <= position && node.end > position) { const betterNode = tss.forEachChild(node, findChild); return betterNode || node; diff --git a/packages/language-service/src/typescript_symbols.ts b/packages/language-service/src/typescript_symbols.ts index 69812f295fc1f..dabd77245909e 100644 --- a/packages/language-service/src/typescript_symbols.ts +++ b/packages/language-service/src/typescript_symbols.ts @@ -57,8 +57,14 @@ export function getClassMembersFromDeclaration( return new TypeWrapper(type, {node: source, program, checker}).members(); } -export function getClassFromStaticSymbol( - program: ts.Program, type: StaticSymbol): ts.ClassDeclaration|undefined { +export function getPipesTable( + source: ts.SourceFile, program: ts.Program, checker: ts.TypeChecker, + pipes: CompilePipeSummary[]): SymbolTable { + return new PipesTable(pipes, {program, checker, node: source}); +} + +function getClassFromStaticSymbol(program: ts.Program, type: StaticSymbol): ts.ClassDeclaration| + undefined { const source = program.getSourceFile(type.filePath); if (source) { return ts.forEachChild(source, child => { @@ -68,18 +74,12 @@ export function getClassFromStaticSymbol( return classDeclaration; } } - }) as(ts.ClassDeclaration | undefined); + }) as (ts.ClassDeclaration | undefined); } return undefined; } -export function getPipesTable( - source: ts.SourceFile, program: ts.Program, checker: ts.TypeChecker, - pipes: CompilePipeSummary[]): SymbolTable { - return new PipesTable(pipes, {program, checker, node: source}); -} - class TypeScriptSymbolQuery implements SymbolQuery { private typeCache = new Map<BuiltinType, Symbol>(); private pipesCache: SymbolTable|undefined; @@ -123,14 +123,18 @@ class TypeScriptSymbolQuery implements SymbolQuery { return result || this.getBuiltinType(BuiltinType.Any); } - getArrayType(type: Symbol): Symbol { return this.getBuiltinType(BuiltinType.Any); } + getArrayType(_type: Symbol): Symbol { + return this.getBuiltinType(BuiltinType.Any); + } getElementType(type: Symbol): Symbol|undefined { if (type instanceof TypeWrapper) { - const tSymbol = type.tsType.symbol; - const tArgs = type.typeArguments(); - if (!tSymbol || tSymbol.name !== 'Array' || !tArgs || tArgs.length != 1) return; - return tArgs[0]; + const ty = type.tsType; + const tyArgs = type.typeArguments(); + // TODO(ayazhafiz): Track https://github.com/microsoft/TypeScript/issues/37711 to expose + // `isArrayLikeType` as a public method. + if (!(this.checker as any).isArrayLikeType(ty) || tyArgs?.length !== 1) return; + return tyArgs[0]; } } @@ -191,13 +195,13 @@ class TypeScriptSymbolQuery implements SymbolQuery { private getTemplateRefContextType(typeSymbol: ts.Symbol, context: TypeContext): Symbol|undefined { const type = this.checker.getTypeOfSymbolAtLocation(typeSymbol, this.source); const constructor = type.symbol && type.symbol.members && - getFromSymbolTable(type.symbol.members !, '__constructor'); + getFromSymbolTable(type.symbol.members!, '__constructor'); if (constructor) { - const constructorDeclaration = constructor.declarations ![0] as ts.ConstructorTypeNode; + const constructorDeclaration = constructor.declarations![0] as ts.ConstructorTypeNode; for (const parameter of constructorDeclaration.parameters) { - const type = this.checker.getTypeAtLocation(parameter.type !); - if (type.symbol !.name == 'TemplateRef' && isReferenceType(type)) { + const type = this.checker.getTypeAtLocation(parameter.type!); + if (type.symbol!.name == 'TemplateRef' && isReferenceType(type)) { const typeWrapper = new TypeWrapper(type, context); const typeArguments = typeWrapper.typeArguments(); if (typeArguments && typeArguments.length === 1) { @@ -218,7 +222,7 @@ function signaturesOf(type: ts.Type, context: TypeContext): Signature[] { return type.getCallSignatures().map(s => new SignatureWrapper(s, context)); } -function selectSignature(type: ts.Type, context: TypeContext, types: Symbol[]): Signature| +function selectSignature(type: ts.Type, context: TypeContext, _types: Symbol[]): Signature| undefined { // TODO: Do a better job of selecting the right signature. TypeScript does not currently support a // Type Relationship API (see https://github.com/angular/vscode-ng-language-service/issues/143). @@ -235,7 +239,9 @@ class TypeWrapper implements Symbol { } } - get name(): string { return this.context.checker.typeToString(this.tsType); } + get name(): string { + return this.context.checker.typeToString(this.tsType); + } public readonly kind: DeclarationKind = 'type'; @@ -247,7 +253,9 @@ class TypeWrapper implements Symbol { public readonly public: boolean = true; - get callable(): boolean { return typeCallable(this.tsType); } + get callable(): boolean { + return typeCallable(this.tsType); + } get nullable(): boolean { return this.context.checker.getNonNullableType(this.tsType) != this.tsType; @@ -274,7 +282,9 @@ class TypeWrapper implements Symbol { return new SymbolTableWrapper(this.tsType.getApparentProperties(), this.context, this.tsType); } - signatures(): Signature[] { return signaturesOf(this.tsType, this.context); } + signatures(): Signature[] { + return signaturesOf(this.tsType, this.context); + } selectSignature(types: Symbol[]): Signature|undefined { return selectSignature(this.tsType, this.context, types); @@ -330,30 +340,44 @@ class SymbolWrapper implements Symbol { symbol: ts.Symbol, /** TypeScript type context of the symbol. */ private context: TypeContext, - /** Type of the TypeScript symbol, if known. If not provided, the type of the symbol - * will be determined dynamically; see `SymbolWrapper#tsType`. */ + /** + * Type of the TypeScript symbol, if known. If not provided, the type of the symbol + * will be determined dynamically; see `SymbolWrapper#tsType`. + */ private _tsType?: ts.Type) { this.symbol = symbol && context && (symbol.flags & ts.SymbolFlags.Alias) ? context.checker.getAliasedSymbol(symbol) : symbol; } - get name(): string { return this.symbol.name; } + get name(): string { + return this.symbol.name; + } - get kind(): DeclarationKind { return this.callable ? 'method' : 'property'; } + get kind(): DeclarationKind { + return this.callable ? 'method' : 'property'; + } - get type(): TypeWrapper { return new TypeWrapper(this.tsType, this.context); } + get type(): TypeWrapper { + return new TypeWrapper(this.tsType, this.context); + } - get container(): Symbol|undefined { return getContainerOf(this.symbol, this.context); } + get container(): Symbol|undefined { + return getContainerOf(this.symbol, this.context); + } get public(): boolean { // Symbols that are not explicitly made private are public. return !isSymbolPrivate(this.symbol); } - get callable(): boolean { return typeCallable(this.tsType); } + get callable(): boolean { + return typeCallable(this.tsType); + } - get definition(): Definition { return definitionFromTsSymbol(this.symbol); } + get definition(): Definition { + return definitionFromTsSymbol(this.symbol); + } get documentation(): ts.SymbolDisplayPart[] { return this.symbol.getDocumentationComment(this.context.checker); @@ -366,21 +390,27 @@ class SymbolWrapper implements Symbol { const typeWrapper = new TypeWrapper(declaredType, this.context); this._members = typeWrapper.members(); } else { - this._members = new SymbolTableWrapper(this.symbol.members !, this.context, this.tsType); + this._members = new SymbolTableWrapper(this.symbol.members!, this.context, this.tsType); } } return this._members; } - signatures(): Signature[] { return signaturesOf(this.tsType, this.context); } + signatures(): Signature[] { + return signaturesOf(this.tsType, this.context); + } selectSignature(types: Symbol[]): Signature|undefined { return selectSignature(this.tsType, this.context, types); } - indexed(argument: Symbol): Symbol|undefined { return undefined; } + indexed(_argument: Symbol): Symbol|undefined { + return undefined; + } - typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); } + typeArguments(): Symbol[]|undefined { + return this.type.typeArguments(); + } private get tsType(): ts.Type { let type = this._tsType; @@ -401,29 +431,53 @@ class DeclaredSymbol implements Symbol { constructor(private declaration: SymbolDeclaration) {} - get name() { return this.declaration.name; } + get name() { + return this.declaration.name; + } - get kind() { return this.declaration.kind; } + get kind() { + return this.declaration.kind; + } - get container(): Symbol|undefined { return undefined; } + get container(): Symbol|undefined { + return undefined; + } - get type(): Symbol { return this.declaration.type; } + get type(): Symbol { + return this.declaration.type; + } - get callable(): boolean { return this.type.callable; } + get callable(): boolean { + return this.type.callable; + } - get definition(): Definition { return this.declaration.definition; } + get definition(): Definition { + return this.declaration.definition; + } - get documentation(): ts.SymbolDisplayPart[] { return this.declaration.type.documentation; } + get documentation(): ts.SymbolDisplayPart[] { + return this.declaration.type.documentation; + } - members(): SymbolTable { return this.type.members(); } + members(): SymbolTable { + return this.type.members(); + } - signatures(): Signature[] { return this.type.signatures(); } + signatures(): Signature[] { + return this.type.signatures(); + } - selectSignature(types: Symbol[]): Signature|undefined { return this.type.selectSignature(types); } + selectSignature(types: Symbol[]): Signature|undefined { + return this.type.selectSignature(types); + } - typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); } + typeArguments(): Symbol[]|undefined { + return this.type.typeArguments(); + } - indexed(argument: Symbol): Symbol|undefined { return undefined; } + indexed(_argument: Symbol): Symbol|undefined { + return undefined; + } } class SignatureWrapper implements Signature { @@ -433,18 +487,24 @@ class SignatureWrapper implements Signature { return new SymbolTableWrapper(this.signature.getParameters(), this.context); } - get result(): Symbol { return new TypeWrapper(this.signature.getReturnType(), this.context); } + get result(): Symbol { + return new TypeWrapper(this.signature.getReturnType(), this.context); + } } class SignatureResultOverride implements Signature { constructor(private signature: Signature, private resultType: Symbol) {} - get arguments(): SymbolTable { return this.signature.arguments; } + get arguments(): SymbolTable { + return this.signature.arguments; + } - get result(): Symbol { return this.resultType; } + get result(): Symbol { + return this.resultType; + } } -export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable { +function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable { // ∀ Typescript version >= 2.2, `SymbolTable` is implemented as an ES6 `Map` const result = new Map<string, ts.Symbol>(); for (const symbol of symbols) { @@ -454,7 +514,7 @@ export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable { return result as ts.SymbolTable; } -function toSymbols(symbolTable: ts.SymbolTable | undefined): ts.Symbol[] { +function toSymbols(symbolTable: ts.SymbolTable|undefined): ts.Symbol[] { if (!symbolTable) return []; const table = symbolTable as any; @@ -488,8 +548,7 @@ class SymbolTableWrapper implements SymbolTable { * @param context program context * @param type original TypeScript type of entity owning the symbols, if known */ - constructor( - symbols: ts.SymbolTable|ts.Symbol[], private context: TypeContext, private type?: ts.Type) { + constructor(symbols: ts.SymbolTable|ts.Symbol[], private context: TypeContext, type?: ts.Type) { symbols = symbols || []; if (Array.isArray(symbols)) { @@ -505,7 +564,9 @@ class SymbolTableWrapper implements SymbolTable { } } - get size(): number { return this.symbols.length; } + get size(): number { + return this.symbols.length; + } get(key: string): Symbol|undefined { const symbol = getFromSymbolTable(this.symbolTable, key); @@ -533,20 +594,26 @@ class SymbolTableWrapper implements SymbolTable { this.stringIndexType !== undefined; } - values(): Symbol[] { return this.symbols.map(s => new SymbolWrapper(s, this.context)); } + values(): Symbol[] { + return this.symbols.map(s => new SymbolWrapper(s, this.context)); + } } class MapSymbolTable implements SymbolTable { private map = new Map<string, Symbol>(); private _values: Symbol[] = []; - get size(): number { return this.map.size; } + get size(): number { + return this.map.size; + } - get(key: string): Symbol|undefined { return this.map.get(key); } + get(key: string): Symbol|undefined { + return this.map.get(key); + } add(symbol: Symbol) { if (this.map.has(symbol.name)) { - const previous = this.map.get(symbol.name) !; + const previous = this.map.get(symbol.name)!; this._values[this._values.indexOf(previous)] = symbol; } this.map.set(symbol.name, symbol); @@ -559,7 +626,9 @@ class MapSymbolTable implements SymbolTable { } } - has(key: string): boolean { return this.map.has(key); } + has(key: string): boolean { + return this.map.has(key); + } values(): Symbol[] { // Switch to this.map.values once iterables are supported by the target language. @@ -570,7 +639,9 @@ class MapSymbolTable implements SymbolTable { class PipesTable implements SymbolTable { constructor(private pipes: CompilePipeSummary[], private context: TypeContext) {} - get size() { return this.pipes.length; } + get size() { + return this.pipes.length; + } get(key: string): Symbol|undefined { const pipe = this.pipes.find(pipe => pipe.name == key); @@ -579,9 +650,13 @@ class PipesTable implements SymbolTable { } } - has(key: string): boolean { return this.pipes.find(pipe => pipe.name == key) != null; } + has(key: string): boolean { + return this.pipes.find(pipe => pipe.name == key) != null; + } - values(): Symbol[] { return this.pipes.map(pipe => new PipeSymbol(pipe, this.context)); } + values(): Symbol[] { + return this.pipes.map(pipe => new PipeSymbol(pipe, this.context)); + } } // This matches .d.ts files that look like ".../<package-name>/<package-name>.d.ts", @@ -598,9 +673,13 @@ class PipeSymbol implements Symbol { constructor(private pipe: CompilePipeSummary, private context: TypeContext) {} - get name(): string { return this.pipe.name; } + get name(): string { + return this.pipe.name; + } - get type(): TypeWrapper { return new TypeWrapper(this.tsType, this.context); } + get type(): TypeWrapper { + return new TypeWrapper(this.tsType, this.context); + } get definition(): Definition|undefined { const symbol = this.tsType.getSymbol(); @@ -615,12 +694,16 @@ class PipeSymbol implements Symbol { return symbol.getDocumentationComment(this.context.checker); } - members(): SymbolTable { return EmptyTable.instance; } + members(): SymbolTable { + return EmptyTable.instance; + } - signatures(): Signature[] { return signaturesOf(this.tsType, this.context); } + signatures(): Signature[] { + return signaturesOf(this.tsType, this.context); + } selectSignature(types: Symbol[]): Signature|undefined { - let signature = selectSignature(this.tsType, this.context, types) !; + let signature = selectSignature(this.tsType, this.context, types)!; if (types.length > 0) { const parameterType = types[0]; let resultType: Symbol|undefined = undefined; @@ -643,16 +726,20 @@ class PipeSymbol implements Symbol { return signature; } - indexed(argument: Symbol): Symbol|undefined { return undefined; } + indexed(_argument: Symbol): Symbol|undefined { + return undefined; + } - typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); } + typeArguments(): Symbol[]|undefined { + return this.type.typeArguments(); + } private get tsType(): ts.Type { let type = this._tsType; if (!type) { const classSymbol = this.findClassSymbol(this.pipe.type.reference); if (classSymbol) { - type = this._tsType = this.findTransformMethodType(classSymbol) !; + type = this._tsType = this.findTransformMethodType(classSymbol)!; } if (!type) { type = this._tsType = getTsTypeFromBuiltinType(BuiltinType.Any, this.context); @@ -698,9 +785,15 @@ function findClassSymbolInContext(type: StaticSymbol, context: TypeContext): ts. class EmptyTable implements SymbolTable { public readonly size: number = 0; - get(key: string): Symbol|undefined { return undefined; } - has(key: string): boolean { return false; } - values(): Symbol[] { return []; } + get(_key: string): Symbol|undefined { + return undefined; + } + has(_key: string): boolean { + return false; + } + values(): Symbol[] { + return []; + } static instance = new EmptyTable(); } @@ -741,7 +834,7 @@ function getTsTypeFromBuiltinType(builtinType: BuiltinType, ctx: TypeContext): t function spanAt(sourceFile: ts.SourceFile, line: number, column: number): Span|undefined { if (line != null && column != null) { const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column); - const findChild = function findChild(node: ts.Node): ts.Node | undefined { + const findChild = function findChild(node: ts.Node): ts.Node|undefined { if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) { const betterNode = ts.forEachChild(node, findChild); return betterNode || node; @@ -777,7 +870,7 @@ function parentDeclarationOf(node: ts.Node): ts.Node|undefined { case ts.SyntaxKind.SourceFile: return undefined; } - node = node.parent !; + node = node.parent!; } } @@ -795,7 +888,7 @@ function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol|undefin } } -function typeKindOf(type: ts.Type | undefined): BuiltinType { +function typeKindOf(type: ts.Type|undefined): BuiltinType { if (type) { if (type.flags & ts.TypeFlags.Any) { return BuiltinType.Any; @@ -804,25 +897,20 @@ function typeKindOf(type: ts.Type | undefined): BuiltinType { return BuiltinType.String; } else if (type.flags & (ts.TypeFlags.Number | ts.TypeFlags.NumberLike)) { return BuiltinType.Number; + } else if (type.flags & ts.TypeFlags.Object) { + return BuiltinType.Object; } else if (type.flags & (ts.TypeFlags.Undefined)) { return BuiltinType.Undefined; } else if (type.flags & (ts.TypeFlags.Null)) { return BuiltinType.Null; } else if (type.flags & ts.TypeFlags.Union) { - // If all the constituent types of a union are the same kind, it is also that kind. - let candidate: BuiltinType|null = null; const unionType = type as ts.UnionType; - if (unionType.types.length > 0) { - candidate = typeKindOf(unionType.types[0]); - for (const subType of unionType.types) { - if (candidate != typeKindOf(subType)) { - return BuiltinType.Other; - } - } - } - if (candidate != null) { - return candidate; + if (unionType.types.length === 0) return BuiltinType.Other; + let ty: BuiltinType = 0; + for (const subType of unionType.types) { + ty |= typeKindOf(subType); } + return ty; } else if (type.flags & ts.TypeFlags.TypeParameter) { return BuiltinType.Unbound; } diff --git a/packages/language-service/src/utils.ts b/packages/language-service/src/utils.ts index 6d00eace56774..a73fac69a6bb7 100644 --- a/packages/language-service/src/utils.ts +++ b/packages/language-service/src/utils.ts @@ -6,27 +6,25 @@ * found in the LICENSE file at https://angular.io/license */ -import {AstPath, BoundEventAst, CompileDirectiveSummary, CompileTypeMetadata, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, HtmlAstPath, Identifiers, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, RecursiveVisitor, TemplateAst, TemplateAstPath, identifierName, templateVisitAll, visitAll} from '@angular/compiler'; +import {AstPath, BoundEventAst, CompileDirectiveSummary, CompileTypeMetadata, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, HtmlAstPath, identifierName, Identifiers, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, RecursiveVisitor, TemplateAst, TemplateAstPath, templateVisitAll, visitAll} from '@angular/compiler'; import * as ts from 'typescript'; -import {AstResult, SelectorInfo} from './common'; -import {DiagnosticTemplateInfo} from './expression_diagnostics'; -import {Span, Symbol, SymbolQuery} from './types'; +import {AstResult, DiagnosticTemplateInfo, SelectorInfo, Span, Symbol, SymbolQuery} from './types'; -export interface SpanHolder { +interface SpanHolder { sourceSpan: ParseSourceSpan; endSourceSpan?: ParseSourceSpan|null; children?: SpanHolder[]; } -export function isParseSourceSpan(value: any): value is ParseSourceSpan { +function isParseSourceSpan(value: any): value is ParseSourceSpan { return value && !!value.start; } export function spanOf(span: SpanHolder): Span; export function spanOf(span: ParseSourceSpan): Span; -export function spanOf(span: SpanHolder | ParseSourceSpan | undefined): Span|undefined; -export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined { +export function spanOf(span: SpanHolder|ParseSourceSpan|undefined): Span|undefined; +export function spanOf(span?: SpanHolder|ParseSourceSpan): Span|undefined { if (!span) return undefined; if (isParseSourceSpan(span)) { return {start: span.start.offset, end: span.end.offset}; @@ -36,7 +34,7 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined { } else if (span.children && span.children.length) { return { start: span.sourceSpan.start.offset, - end: spanOf(span.children[span.children.length - 1]) !.end + end: spanOf(span.children[span.children.length - 1])!.end }; } return {start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset}; @@ -44,8 +42,9 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined { } export function inSpan(position: number, span?: Span, exclusive?: boolean): boolean { - return span != null && (exclusive ? position >= span.start && position < span.end : - position >= span.start && position <= span.end); + return span != null && + (exclusive ? position >= span.start && position < span.end : + position >= span.start && position <= span.end); } export function offsetSpan(span: Span, amount: number): Span { @@ -71,7 +70,7 @@ export function getSelectors(info: AstResult): SelectorInfo { const map = new Map<CssSelector, CompileDirectiveSummary>(); const results: CssSelector[] = []; for (const directive of info.directives) { - const selectors: CssSelector[] = CssSelector.parse(directive.selector !); + const selectors: CssSelector[] = CssSelector.parse(directive.selector!); for (const selector of selectors) { results.push(selector); map.set(selector, directive); @@ -80,16 +79,6 @@ export function getSelectors(info: AstResult): SelectorInfo { return {selectors: results, map}; } -export function isTypescriptVersion(low: string, high?: string) { - const version = ts.version; - - if (version.substring(0, low.length) < low) return false; - - if (high && (version.substring(0, high.length) > high)) return false; - - return true; -} - export function diagnosticInfoFromTemplateInfo(info: AstResult): DiagnosticTemplateInfo { return { fileName: info.template.fileName, @@ -141,7 +130,9 @@ export function findTemplateAstAt(ast: TemplateAst[], position: number): Templat visitDirective(ast: DirectiveAst, context: any): any { // Ignore the host properties of a directive - const result = this.visitChildren(context, visit => { visit(ast.inputs); }); + const result = this.visitChildren(context, visit => { + visit(ast.inputs); + }); // We never care about the diretive itself, just its inputs. if (path[path.length - 1] === ast) { path.pop(); @@ -286,3 +277,62 @@ export function findOutputBinding( } } } + +/** + * Returns a property assignment from the assignment value, or `undefined` if there is no + * assignment. + */ +export function getPropertyAssignmentFromValue(value: ts.Node): ts.PropertyAssignment|undefined { + if (!value.parent || !ts.isPropertyAssignment(value.parent)) { + return; + } + return value.parent; +} + +/** + * Given a decorator property assignment, return the ClassDeclaration node that corresponds to the + * directive class the property applies to. + * If the property assignment is not on a class decorator, no declaration is returned. + * + * For example, + * + * @Component({ + * template: '<div></div>' + * ^^^^^^^^^^^^^^^^^^^^^^^---- property assignment + * }) + * class AppComponent {} + * ^---- class declaration node + * + * @param propAsgn property assignment + */ +export function getClassDeclFromDecoratorProp(propAsgnNode: ts.PropertyAssignment): + ts.ClassDeclaration|undefined { + if (!propAsgnNode.parent || !ts.isObjectLiteralExpression(propAsgnNode.parent)) { + return; + } + const objLitExprNode = propAsgnNode.parent; + if (!objLitExprNode.parent || !ts.isCallExpression(objLitExprNode.parent)) { + return; + } + const callExprNode = objLitExprNode.parent; + if (!callExprNode.parent || !ts.isDecorator(callExprNode.parent)) { + return; + } + const decorator = callExprNode.parent; + if (!decorator.parent || !ts.isClassDeclaration(decorator.parent)) { + return; + } + const classDeclNode = decorator.parent; + return classDeclNode; +} + +/** + * Determines if a property assignment is on a class decorator. + * See `getClassDeclFromDecoratorProperty`, which gets the class the decorator is applied to, for + * more details. + * + * @param prop property assignment + */ +export function isClassDecoratorProperty(propAsgn: ts.PropertyAssignment): boolean { + return !!getClassDeclFromDecoratorProp(propAsgn); +} diff --git a/packages/language-service/test/BUILD.bazel b/packages/language-service/test/BUILD.bazel index c033a2006701c..4dcdc45adb1e5 100644 --- a/packages/language-service/test/BUILD.bazel +++ b/packages/language-service/test/BUILD.bazel @@ -47,7 +47,6 @@ ts_library( "html_info_spec.ts", "language_service_spec.ts", "reflector_host_spec.ts", - "template_spec.ts", "ts_plugin_spec.ts", "typescript_host_spec.ts", "utils_spec.ts", diff --git a/packages/language-service/test/completions_spec.ts b/packages/language-service/test/completions_spec.ts index ed8f3e081f5ae..ea44326f69fd4 100644 --- a/packages/language-service/test/completions_spec.ts +++ b/packages/language-service/test/completions_spec.ts @@ -25,7 +25,9 @@ describe('completions', () => { const ngHost = new TypeScriptServiceHost(mockHost, tsLS); const ngLS = createLanguageService(ngHost); - beforeEach(() => { mockHost.reset(); }); + beforeEach(() => { + mockHost.reset(); + }); it('should be able to get entity completions', () => { const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'entity-amp'); @@ -102,7 +104,7 @@ describe('completions', () => { const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'h2-hero'); const completions = ngLS.getCompletionsAtPosition(APP_COMPONENT, marker.start); expect(completions).toBeDefined(); - const internal = completions !.entries.find(e => e.name === 'internal'); + const internal = completions!.entries.find(e => e.name === 'internal'); expect(internal).toBeUndefined(); }); @@ -166,9 +168,10 @@ describe('completions', () => { }); it('should be able to get completions in an empty interpolation', () => { - const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'empty-interpolation'); - const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start); - expectContain(completions, CompletionKind.PROPERTY, ['title', 'subTitle']); + mockHost.override(TEST_TEMPLATE, `{{ ~{cursor} }}`); + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); + expectContain(completions, CompletionKind.PROPERTY, ['title', 'hero']); }); it('should suggest $any() type cast function in an interpolation', () => { @@ -202,7 +205,7 @@ describe('completions', () => { const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); expect(completions).toBeDefined(); - expect(completions !.entries).toContain(jasmine.objectContaining({ + expect(completions!.entries).toContain(jasmine.objectContaining({ name: 'myClick', kind: CompletionKind.METHOD as any, insertText: 'myClick()', @@ -214,7 +217,7 @@ describe('completions', () => { const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'pipe-method'); const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); expect(completions).toBeDefined(); - expect(completions !.entries).toContain(jasmine.objectContaining({ + expect(completions!.entries).toContain(jasmine.objectContaining({ name: 'lowercase', kind: CompletionKind.PIPE as any, insertText: 'lowercase', @@ -234,7 +237,7 @@ describe('completions', () => { const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, location); const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); expect(completions).toBeDefined(); - const {entries} = completions !; + const {entries} = completions!; expect(entries).not.toContain(jasmine.objectContaining({name: 'div'})); expect(entries).not.toContain(jasmine.objectContaining({name: 'h1'})); expect(entries).not.toContain(jasmine.objectContaining({name: 'h2'})); @@ -257,7 +260,7 @@ describe('completions', () => { const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'h1-after-space'); const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); expect(completions).toBeDefined(); - const {entries} = completions !; + const {entries} = completions!; expect(entries).not.toContain(jasmine.objectContaining({name: 'class'})); expect(entries).not.toContain(jasmine.objectContaining({name: 'id'})); expect(entries).not.toContain(jasmine.objectContaining({name: 'onclick'})); @@ -280,9 +283,14 @@ describe('completions', () => { describe('with a *ngIf', () => { it('should be able to get completions for exported *ngIf variable', () => { - const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'promised-person-name'); - const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start); - expectContain(completions, CompletionKind.PROPERTY, ['name', 'age', 'street']); + mockHost.override(TEST_TEMPLATE, ` + <div *ngIf="heroP | async as h"> + {{ h.~{cursor} }} + </div> + `); + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); + expectContain(completions, CompletionKind.PROPERTY, ['id', 'name']); }); }); @@ -366,9 +374,14 @@ describe('completions', () => { }); it('should be able to infer the type of a ngForOf with an async pipe', () => { - const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'async-person-name'); - const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start); - expectContain(completions, CompletionKind.PROPERTY, ['name', 'age', 'street']); + mockHost.override(TEST_TEMPLATE, ` + <div *ngFor="let h of heroesP | async"> + {{ h.~{cursor} }} + </div> + `); + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); + expectContain(completions, CompletionKind.PROPERTY, ['id', 'name']); }); it('should be able to resolve variable in nested loop', () => { @@ -467,21 +480,24 @@ describe('completions', () => { describe('for pipes', () => { it('should be able to get a list of pipe values', () => { - for (const location of ['before-pipe', 'in-pipe', 'after-pipe']) { - const marker = mockHost.getLocationMarkerFor(PARSING_CASES, location); - const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start); - expectContain(completions, CompletionKind.PIPE, [ - 'async', - 'uppercase', - 'lowercase', - 'titlecase', - ]); - } + // TODO(kyliau): does not work for case {{ title | ~{cursor} }} + // space before and after pipe ^^^ + mockHost.override(TEST_TEMPLATE, `{{ title|~{cursor} }}`); + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); + expectContain(completions, CompletionKind.PIPE, [ + 'async', + 'lowercase', + 'slice', + 'titlecase', + 'uppercase', + ]); }); it('should be able to resolve lowercase', () => { - const marker = mockHost.getLocationMarkerFor(EXPRESSION_CASES, 'string-pipe'); - const completions = ngLS.getCompletionsAtPosition(EXPRESSION_CASES, marker.start); + mockHost.override(TEST_TEMPLATE, `{{ (title | lowercase).~{cursor} }}`); + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); expectContain(completions, CompletionKind.METHOD, [ 'charAt', 'replace', @@ -493,14 +509,27 @@ describe('completions', () => { describe('with references', () => { it('should list references', () => { - const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'test-comp-content'); - const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start); - expectContain(completions, CompletionKind.REFERENCE, ['div', 'test1', 'test2']); + mockHost.override(TEST_TEMPLATE, ` + <div #myDiv> + <test-comp #test1> + {{ ~{cursor} }} + </test-comp> + </div> + <test-comp #test2></test-comp> + `); + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); + expectContain(completions, CompletionKind.REFERENCE, ['myDiv', 'test1', 'test2']); }); it('should reference the component', () => { - const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'test-comp-after-test'); - const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start); + mockHost.override(TEST_TEMPLATE, ` + <test-comp #test1> + {{ test1.~{cursor} }} + </test-comp> + `); + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); expectContain(completions, CompletionKind.PROPERTY, ['name', 'testEvent']); }); @@ -536,9 +565,9 @@ describe('completions', () => { } `); const location = mockHost.getLocationMarkerFor(fileName, 'key'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === 'key') !; + const completion = completions.entries.find(entry => entry.name === 'key')!; expect(completion).toBeDefined(); expect(completion.kind).toBe('property'); expect(completion.replacementSpan).toBeUndefined(); @@ -553,9 +582,9 @@ describe('completions', () => { export class FooComponent {} `); const location = mockHost.getLocationMarkerFor(fileName, 'start'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === 'acronym') !; + const completion = completions.entries.find(entry => entry.name === 'acronym')!; expect(completion).toBeDefined(); expect(completion.kind).toBe('html element'); expect(completion.replacementSpan).toEqual({start: location.start, length: 3}); @@ -570,9 +599,9 @@ describe('completions', () => { export class FooComponent {} `); const location = mockHost.getLocationMarkerFor(fileName, 'end'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === 'acronym') !; + const completion = completions.entries.find(entry => entry.name === 'acronym')!; expect(completion).toBeDefined(); expect(completion.kind).toBe('html element'); expect(completion.replacementSpan).toEqual({start: location.start - 4, length: 4}); @@ -591,9 +620,9 @@ describe('completions', () => { } `); const location = mockHost.getLocationMarkerFor(fileName, 'key'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === 'key') !; + const completion = completions.entries.find(entry => entry.name === 'key')!; expect(completion).toBeDefined(); expect(completion.kind).toBe('property'); expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 5}); @@ -612,9 +641,9 @@ describe('completions', () => { } `); const location = mockHost.getLocationMarkerFor(fileName, 'field'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === '$title_1') !; + const completion = completions.entries.find(entry => entry.name === '$title_1')!; expect(completion).toBeDefined(); expect(completion.kind).toBe('property'); expect(completion.replacementSpan).toEqual({start: location.start, length: 8}); @@ -631,9 +660,9 @@ describe('completions', () => { export class FooComponent {} `); const location = mockHost.getLocationMarkerFor(fileName, 'click'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === 'click') !; + const completion = completions.entries.find(entry => entry.name === 'click')!; expect(completion).toBeDefined(); expect(completion.kind).toBe(CompletionKind.ATTRIBUTE); expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2}); @@ -652,9 +681,9 @@ describe('completions', () => { } `); const location = mockHost.getLocationMarkerFor(fileName, 'handleClick'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === 'handleClick') !; + const completion = completions.entries.find(entry => entry.name === 'handleClick')!; expect(completion).toBeDefined(); expect(completion.kind).toBe('method'); expect(completion.replacementSpan).toEqual({start: location.start - 3, length: 3}); @@ -665,18 +694,18 @@ describe('completions', () => { @Component({ selector: 'foo-component', template: \` - <di~{div}></div> + <test-comp~{test-comp}></test-comp> \`, }) export class FooComponent {} `); - const location = mockHost.getLocationMarkerFor(fileName, 'div'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const location = mockHost.getLocationMarkerFor(fileName, 'test-comp'); + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === 'div') !; + const completion = completions.entries.find(entry => entry.name === 'test-comp')!; expect(completion).toBeDefined(); - expect(completion.kind).toBe('html element'); - expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2}); + expect(completion.kind).toBe('component'); + expect(completion.replacementSpan).toEqual({start: location.start - 9, length: 9}); }); it('should work for bindings', () => { @@ -690,9 +719,9 @@ describe('completions', () => { export class FooComponent {} `); const location = mockHost.getLocationMarkerFor(fileName, 'model'); - const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !; + const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!; expect(completions).toBeDefined(); - const completion = completions.entries.find(entry => entry.name === 'ngModel') !; + const completion = completions.entries.find(entry => entry.name === 'ngModel')!; expect(completion).toBeDefined(); expect(completion.kind).toBe(CompletionKind.ATTRIBUTE); expect(completion.replacementSpan).toEqual({start: location.start - 5, length: 5}); @@ -741,14 +770,14 @@ describe('completions', () => { it('should be able to get the completions (ref- prefix)', () => { mockHost.override(TEST_TEMPLATE, `<form ref-itemForm="ngF~{reference}"></form>`); const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'reference'); - const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start) !; + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start)!; expectContain(completions, CompletionKind.REFERENCE, ['ngForm']); }); it('should be able to get the completions (# prefix)', () => { mockHost.override(TEST_TEMPLATE, `<form #itemForm="ngF~{reference}"></form>`); const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'reference'); - const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start) !; + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start)!; expectContain(completions, CompletionKind.REFERENCE, ['ngForm']); }); }); @@ -789,9 +818,9 @@ describe('completions', () => { }); function expectContain( - completions: ts.CompletionInfo | undefined, kind: CompletionKind, names: string[]) { + completions: ts.CompletionInfo|undefined, kind: CompletionKind, names: string[]) { expect(completions).toBeDefined(); for (const name of names) { - expect(completions !.entries).toContain(jasmine.objectContaining({ name, kind } as any)); + expect(completions!.entries).toContain(jasmine.objectContaining({name, kind} as any)); } } diff --git a/packages/language-service/test/definitions_spec.ts b/packages/language-service/test/definitions_spec.ts index 69d34a2f38cc5..dcf5f25bea4b9 100644 --- a/packages/language-service/test/definitions_spec.ts +++ b/packages/language-service/test/definitions_spec.ts @@ -22,7 +22,9 @@ describe('definitions', () => { const ngHost = new TypeScriptServiceHost(mockHost, service); const ngService = createLanguageService(ngHost); - beforeEach(() => { mockHost.reset(); }); + beforeEach(() => { + mockHost.reset(); + }); it('should be able to find field in an interpolation', () => { const fileName = mockHost.addCode(` @@ -36,12 +38,12 @@ describe('definitions', () => { const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const def = definitions ![0]; + expect(definitions!.length).toBe(1); + const def = definitions![0]; expect(def.fileName).toBe(fileName); expect(def.name).toBe('name'); @@ -55,20 +57,20 @@ describe('definitions', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const def = definitions ![0]; + expect(definitions!.length).toBe(1); + const def = definitions![0]; expect(def.fileName).toBe(PARSING_CASES); expect(def.name).toBe('title'); expect(def.kind).toBe('property'); const fileContent = mockHost.readFile(def.fileName); - expect(fileContent !.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length)) + expect(fileContent!.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length)) .toEqual(`title = 'Some title';`); }); @@ -84,12 +86,12 @@ describe('definitions', () => { const marker = mockHost.getReferenceMarkerFor(fileName, 'myClick'); const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual(mockHost.getLocationMarkerFor(fileName, 'my')); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const def = definitions ![0]; + expect(definitions!.length).toBe(1); + const def = definitions![0]; expect(def.fileName).toBe(fileName); expect(def.name).toBe('myClick'); @@ -109,12 +111,12 @@ describe('definitions', () => { const marker = mockHost.getReferenceMarkerFor(fileName, 'include'); const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const def = definitions ![0]; + expect(definitions!.length).toBe(1); + const def = definitions![0]; expect(def.fileName).toBe(fileName); expect(def.name).toBe('include'); @@ -134,7 +136,7 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; // Get the marker for bounded text in the code added above. const boundedText = mockHost.getLocationMarkerFor(fileName, 'my'); @@ -142,14 +144,14 @@ describe('definitions', () => { // There should be exactly 1 definition expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const def = definitions ![0]; + expect(definitions!.length).toBe(1); + const def = definitions![0]; const refFileName = '/app/parsing-cases.ts'; expect(def.fileName).toBe(refFileName); expect(def.name).toBe('TestComponent'); expect(def.kind).toBe('component'); - const content = mockHost.readFile(refFileName) !; + const content = mockHost.readFile(refFileName)!; const begin = '/*BeginTestComponent*/ '; const start = content.indexOf(begin) + begin.length; const end = content.indexOf(' /*EndTestComponent*/'); @@ -171,7 +173,7 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; // Get the marker for bounded text in the code added above const boundedText = mockHost.getLocationMarkerFor(fileName, 'my'); @@ -179,14 +181,14 @@ describe('definitions', () => { // There should be exactly 1 definition expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const def = definitions ![0]; + expect(definitions!.length).toBe(1); + const def = definitions![0]; const refFileName = '/app/parsing-cases.ts'; expect(def.fileName).toBe(refFileName); expect(def.name).toBe('testEvent'); expect(def.kind).toBe('event'); - const content = mockHost.readFile(refFileName) !; + const content = mockHost.readFile(refFileName)!; const ref = `@Output('test') testEvent = new EventEmitter();`; expect(def.textSpan).toEqual({ start: content.indexOf(ref), @@ -208,7 +210,7 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; // Get the marker for bounded text in the code added above const boundedText = mockHost.getLocationMarkerFor(fileName, 'my'); @@ -216,14 +218,14 @@ describe('definitions', () => { // There should be exactly 1 definition expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const def = definitions ![0]; + expect(definitions!.length).toBe(1); + const def = definitions![0]; const refFileName = '/app/parsing-cases.ts'; expect(def.fileName).toBe(refFileName); expect(def.name).toBe('name'); expect(def.kind).toBe('property'); - const content = mockHost.readFile(refFileName) !; + const content = mockHost.readFile(refFileName)!; const ref = `@Input('tcName') name = 'test';`; expect(def.textSpan).toEqual({ start: content.indexOf(ref), @@ -245,14 +247,14 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(4); + expect(definitions!.length).toBe(4); const refFileName = '/node_modules/@angular/common/common.d.ts'; - for (const def of definitions !) { + for (const def of definitions!) { expect(def.fileName).toBe(refFileName); expect(def.name).toBe('async'); expect(def.kind).toBe('pipe'); @@ -268,14 +270,14 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); + expect(definitions!.length).toBe(1); const refFileName = '/node_modules/@angular/common/common.d.ts'; - for (const def of definitions !) { + for (const def of definitions!) { expect(def.fileName).toBe(refFileName); expect(def.name).toBe('date'); expect(def.kind).toBe('pipe'); @@ -292,17 +294,17 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; // Get the marker for bounded text in the code added above const boundedText = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'my'); expect(textSpan).toEqual(boundedText); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); + expect(definitions!.length).toBe(1); const refFileName = '/node_modules/@angular/common/common.d.ts'; - const def = definitions ![0]; + const def = definitions![0]; expect(def.fileName).toBe(refFileName); expect(def.name).toBe('NgForOf'); expect(def.kind).toBe('directive'); @@ -317,17 +319,17 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; // Get the marker for bounded text in the code added above expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); // The two definitions are setter and getter of 'ngForTrackBy'. - expect(definitions !.length).toBe(2); + expect(definitions!.length).toBe(2); const refFileName = '/node_modules/@angular/common/common.d.ts'; - definitions !.forEach(def => { + definitions!.forEach(def => { expect(def.fileName).toBe(refFileName); expect(def.name).toBe('ngForTrackBy'); expect(def.kind).toBe('method'); @@ -343,19 +345,19 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); + expect(definitions!.length).toBe(1); const refFileName = '/app/parsing-cases.ts'; - const def = definitions ![0]; + const def = definitions![0]; expect(def.fileName).toBe(refFileName); expect(def.name).toBe('heroes'); expect(def.kind).toBe('property'); - const content = mockHost.readFile(refFileName) !; + const content = mockHost.readFile(refFileName)!; expect(content.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length)) .toEqual(`heroes: Hero[] = [this.hero];`); }); @@ -370,28 +372,28 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; // Get the marker for bounded text in the code added above const boundedText = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'my'); expect(textSpan).toEqual(boundedText); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(2); - const [def1, def2] = definitions !; + expect(definitions!.length).toBe(2); + const [def1, def2] = definitions!; const refFileName = '/app/parsing-cases.ts'; expect(def1.fileName).toBe(refFileName); expect(def1.name).toBe('model'); expect(def1.kind).toBe('property'); - let content = mockHost.readFile(refFileName) !; + let content = mockHost.readFile(refFileName)!; expect(content.substring(def1.textSpan.start, def1.textSpan.start + def1.textSpan.length)) .toEqual(`@Input() model: string = 'model';`); expect(def2.fileName).toBe(refFileName); expect(def2.name).toBe('modelChange'); expect(def2.kind).toBe('event'); - content = mockHost.readFile(refFileName) !; + content = mockHost.readFile(refFileName)!; expect(content.substring(def2.textSpan.start, def2.textSpan.start + def2.textSpan.length)) .toEqual(`@Output() modelChange: EventEmitter<string> = new EventEmitter();`); }); @@ -407,13 +409,13 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual({start: marker.start - 2, length: 9}); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const [def] = definitions !; + expect(definitions!.length).toBe(1); + const [def] = definitions!; expect(def.fileName).toBe('/app/test.ng'); expect(def.textSpan).toEqual({start: 0, length: 0}); }); @@ -430,13 +432,13 @@ describe('definitions', () => { const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual({start: marker.start - 2, length: 10}); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const [def] = definitions !; + expect(definitions!.length).toBe(1); + const [def] = definitions!; expect(def.fileName).toBe('/app/test.css'); expect(def.textSpan).toEqual({start: 0, length: 0}); }); @@ -453,12 +455,12 @@ describe('definitions', () => { const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start); expect(result).toBeDefined(); - const {textSpan, definitions} = result !; + const {textSpan, definitions} = result!; expect(textSpan).toEqual(marker); expect(definitions).toBeDefined(); - expect(definitions !.length).toBe(1); - const def = definitions ![0]; + expect(definitions!.length).toBe(1); + const def = definitions![0]; expect(def.fileName).toBe(fileName); expect(def.name).toBe('name'); diff --git a/packages/language-service/test/diagnostic_messages_spec.ts b/packages/language-service/test/diagnostic_messages_spec.ts index a4dd95e6ecb7d..cc44671fa0f55 100644 --- a/packages/language-service/test/diagnostic_messages_spec.ts +++ b/packages/language-service/test/diagnostic_messages_spec.ts @@ -7,7 +7,8 @@ */ import * as ts from 'typescript'; -import {DiagnosticMessage, createDiagnostic} from '../src/diagnostic_messages'; + +import {createDiagnostic, DiagnosticMessage} from '../src/diagnostic_messages'; describe('create diagnostic', () => { it('should format and create diagnostics correctly', () => { diff --git a/packages/language-service/test/diagnostics_spec.ts b/packages/language-service/test/diagnostics_spec.ts index dd51b1d13f24e..22a1e937c7cd7 100644 --- a/packages/language-service/test/diagnostics_spec.ts +++ b/packages/language-service/test/diagnostics_spec.ts @@ -23,8 +23,6 @@ import {MockTypescriptHost} from './test_utils'; * as well. */ -const EXPRESSION_CASES = '/app/expression-cases.ts'; -const NG_FOR_CASES = '/app/ng-for-cases.ts'; const TEST_TEMPLATE = '/app/test.ng'; const APP_COMPONENT = '/app/app.component.ts'; @@ -34,7 +32,9 @@ describe('diagnostics', () => { const ngHost = new TypeScriptServiceHost(mockHost, tsLS); const ngLS = createLanguageService(ngHost); - beforeEach(() => { mockHost.reset(); }); + beforeEach(() => { + mockHost.reset(); + }); it('should produce no diagnostics for test.ng', () => { // there should not be any errors on existing external template @@ -119,8 +119,41 @@ describe('diagnostics', () => { expect(diagnostics).toEqual([]); }); + describe('diagnostics for expression comparisons', () => { + for (let [left, right, leftTy, rightTy] of [ + ['\'abc\'', 1, 'string', 'number'], + ['hero', 2, 'object', 'number'], + ['strOrNumber', 'hero', 'string|number', 'object'], + ]) { + it(`it should report errors for mismtched types in a comparison: ${leftTy} and ${rightTy}`, + () => { + mockHost.override(TEST_TEMPLATE, `{{ ${left} != ${right} }}`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); + expect(diags.length).toBe(1); + expect(diags[0].messageText).toBe(`Expected operands to be of comparable types or any`); + }); + } + + for (let [left, right, leftTy, rightTy] of [ + ['\'abc\'', 'anyValue', 'string', 'any'], + ['\'abc\'', null, 'string', 'null'], + ['\'abc\'', undefined, 'string', 'undefined'], + [null, null, 'null', 'null'], + ['{a: 1}', '{b: 2}', 'object', 'object'], + ['strOrNumber', '1', 'string|number', 'number'], + ]) { + it(`it should not report errors for compatible types in a comparison: ${leftTy} and ${ + rightTy}`, + () => { + mockHost.override(TEST_TEMPLATE, `{{ ${left} != ${right} }}`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); + expect(diags.length).toBe(0); + }); + } + }); + describe('diagnostics for ngFor exported values', () => { - it('should report errors for mistmatched exported types', () => { + it('should report errors for mismatched exported types', () => { mockHost.override(TEST_TEMPLATE, ` <div *ngFor="let hero of heroes; let i = index; let isFirst = first"> 'i' is a number; 'isFirst' is a boolean @@ -129,7 +162,7 @@ describe('diagnostics', () => { `); const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); expect(diags.length).toBe(1); - expect(diags[0].messageText).toBe(`Expected operands to be of similar type or any`); + expect(diags[0].messageText).toBe(`Expected operands to be of comparable types or any`); }); it('should not report errors for matching exported type', () => { @@ -244,48 +277,50 @@ describe('diagnostics', () => { expect(diags).toEqual([]); }); - describe('in expression-cases.ts', () => { - it('should report access to an unknown field', () => { - const diags = ngLS.getSemanticDiagnostics(EXPRESSION_CASES).map(d => d.messageText); - expect(diags).toContain( - `Identifier 'foo' is not defined. ` + - `The component declaration, template variable declarations, ` + - `and element references do not contain such a member`); - }); + it('should report access to an unknown field', () => { + mockHost.override(TEST_TEMPLATE, `{{ foo }}`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE).map(d => d.messageText); + expect(diags).toContain( + `Identifier 'foo' is not defined. ` + + `The component declaration, template variable declarations, ` + + `and element references do not contain such a member`); + }); - it('should report access to an unknown sub-field', () => { - const diags = ngLS.getSemanticDiagnostics(EXPRESSION_CASES).map(d => d.messageText); - expect(diags).toContain( - `Identifier 'nam' is not defined. 'Person' does not contain such a member`); - }); + it('should report access to an unknown sub-field', () => { + mockHost.override(TEST_TEMPLATE, `{{ hero.nam }}`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE).map(d => d.messageText); + expect(diags).toContain( + `Identifier 'nam' is not defined. 'Hero' does not contain such a member`); + }); - it('should report access to a private member', () => { - const diags = ngLS.getSemanticDiagnostics(EXPRESSION_CASES).map(d => d.messageText); - expect(diags).toContain(`Identifier 'myField' refers to a private member of the component`); - }); + it('should report access to a private member', () => { + mockHost.override(TEST_TEMPLATE, `{{ myField }}`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE).map(d => d.messageText); + expect(diags).toContain(`Identifier 'myField' refers to a private member of the component`); + }); - it('should report numeric operator errors', () => { - const diags = ngLS.getSemanticDiagnostics(EXPRESSION_CASES).map(d => d.messageText); - expect(diags).toContain('Expected a number type'); - }); + it('should report numeric operator errors', () => { + mockHost.override(TEST_TEMPLATE, `{{ 'a' % 2 }}`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE).map(d => d.messageText); + expect(diags).toContain('Expected a number type'); }); - describe('in ng-for-cases.ts', () => { - it('should report an unknown field', () => { - const diags = ngLS.getSemanticDiagnostics(NG_FOR_CASES).map(d => d.messageText); - expect(diags).toContain( - `Identifier 'people_1' is not defined. ` + - `The component declaration, template variable declarations, ` + - `and element references do not contain such a member`); - }); + it('should report an unknown field', () => { + mockHost.override(TEST_TEMPLATE, `<div *ngFor="let person of people"></div>`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE).map(d => d.messageText); + expect(diags).toContain( + `Identifier 'people' is not defined. ` + + `The component declaration, template variable declarations, ` + + `and element references do not contain such a member`); + }); - it('should report an unknown value in a key expression', () => { - const diags = ngLS.getSemanticDiagnostics(NG_FOR_CASES).map(d => d.messageText); - expect(diags).toContain( - `Identifier 'trackBy_1' is not defined. ` + - `The component declaration, template variable declarations, ` + - `and element references do not contain such a member`); - }); + it('should report an unknown value in a key expression', () => { + mockHost.override(TEST_TEMPLATE, `<div *ngFor="let hero of heroes; trackBy: trackByFn"></div>`); + const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE).map(d => d.messageText); + expect(diags).toContain( + `Identifier 'trackByFn' is not defined. ` + + `The component declaration, template variable declarations, ` + + `and element references do not contain such a member`); }); describe('embedded templates', () => { @@ -300,7 +335,8 @@ describe('diagnostics', () => { expect(messageText) .toBe( `The template context of 'CounterDirective' does not define an implicit value.\n` + - `If the context type is a base type or 'any', consider refining it to a more specific type.`, ); + `If the context type is a base type or 'any', consider refining it to a more specific type.`, + ); const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb'); expect(start).toBe(span.start); @@ -337,7 +373,8 @@ describe('diagnostics', () => { expect(category).toBe(ts.DiagnosticCategory.Error); expect(messageText) .toBe( - `Identifier 'missingField' is not defined. '{ implicitPerson: Person; }' does not contain such a member`, ); + `Identifier 'missingField' is not defined. '{ implicitPerson: Hero; }' does not contain such a member`, + ); const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb'); expect(start).toBe(span.start); expect(length).toBe(span.length); @@ -355,7 +392,8 @@ describe('diagnostics', () => { expect(category).toBe(ts.DiagnosticCategory.Error); expect(messageText) .toBe( - `Identifier 'missingField' is not defined. 'Person' does not contain such a member`, ); + `Identifier 'missingField' is not defined. 'Hero' does not contain such a member`, + ); const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb'); expect(start).toBe(span.start); expect(length).toBe(span.length); @@ -396,7 +434,7 @@ describe('diagnostics', () => { it('should reject it when not in an event binding', () => { const content = mockHost.override(TEST_TEMPLATE, '<div [tabIndex]="$event"></div>'); - const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE) !; + const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE)!; expect(diagnostics.length).toBe(1); const {messageText, start, length} = diagnostics[0]; expect(messageText) @@ -410,7 +448,7 @@ describe('diagnostics', () => { it('should reject invalid properties on an event type', () => { const content = mockHost.override( TEST_TEMPLATE, '<div string-model (modelChange)="$event.notSubstring()"></div>'); - const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE) !; + const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE)!; expect(diagnostics.length).toBe(1); const {messageText, start, length} = diagnostics[0]; expect(messageText) @@ -436,14 +474,14 @@ describe('diagnostics', () => { template: '<div></div>' }) export class MyComponent {}`); - const diagnostics = ngLS.getSemanticDiagnostics(fileName) !; + const diagnostics = ngLS.getSemanticDiagnostics(fileName)!; expect(diagnostics.length).toBe(1); const {messageText, start, length} = diagnostics[0]; expect(messageText) .toBe( `Component 'MyComponent' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration.`); - const content = mockHost.readFile(fileName) !; - expect(content.substring(start !, start ! + length !)).toBe('MyComponent'); + const content = mockHost.readFile(fileName)!; + expect(content.substring(start!, start! + length!)).toBe('MyComponent'); }); @@ -495,7 +533,7 @@ describe('diagnostics', () => { }`); const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT); expect(tsDiags).toEqual([]); - const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT) !; + const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT)!; expect(ngDiags.length).toBe(1); const {messageText, start, length} = ngDiags[0]; const keyword = `() => 'foo'`; @@ -504,9 +542,9 @@ describe('diagnostics', () => { // messageText is a three-part chain const firstPart = messageText as ts.DiagnosticMessageChain; expect(firstPart.messageText).toBe(`Error during template compile of 'AppComponent'`); - const secondPart = firstPart.next !; + const secondPart = firstPart.next!; expect(secondPart[0].messageText).toBe('Function expressions are not supported in decorators'); - const thirdPart = secondPart[0].next !; + const thirdPart = secondPart[0].next!; expect(thirdPart[0].messageText) .toBe('Consider changing the function expression into an exported function'); expect(thirdPart[0].next).toBeFalsy(); @@ -568,7 +606,7 @@ describe('diagnostics', () => { export class AppComponent { onClick() { } }`); - const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT) !; + const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT)!; const {messageText, start, length} = diagnostics[0]; expect(messageText).toBe('Unexpected callable expression. Expected a method call'); const keyword = `"onClick"`; @@ -576,26 +614,6 @@ describe('diagnostics', () => { expect(length).toBe(keyword.length - 2); // exclude leading and trailing quotes }); - // #13412 - it('should not report an error for using undefined under non-strict mode', () => { - mockHost.override(APP_COMPONENT, ` - import { Component } from '@angular/core'; - - @Component({ - template: '<div *ngIf="something === undefined"></div>' - }) - export class AppComponent { - something = 'foo'; - }`); - mockHost.overrideOptions({ - strict: false, // TODO: This test fails in strict mode - }); - const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT); - expect(tsDiags).toEqual([]); - const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT); - expect(ngDiags).toEqual([]); - }); - // Issue #13326 it('should report a narrow span for invalid pipes', () => { const content = mockHost.override(APP_COMPONENT, ` @@ -682,7 +700,7 @@ describe('diagnostics', () => { .toBe( 'Invalid providers for "AppComponent in /app/app.component.ts" - only instances of Provider and Type are allowed, got: [?null?]'); // TODO: Looks like this is the wrong span. Should point to 'null' instead. - expect(content.substring(start !, start ! + length !)).toBe('AppComponent'); + expect(content.substring(start!, start! + length!)).toBe('AppComponent'); }); // Issue #15768 @@ -815,12 +833,12 @@ describe('diagnostics', () => { const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile'); - const diagnostics = ngLS.getSemanticDiagnostics(fileName) !; + const diagnostics = ngLS.getSemanticDiagnostics(fileName)!; const urlDiagnostic = diagnostics.find(d => d.messageText === 'URL does not point to a valid file'); expect(urlDiagnostic).toBeDefined(); - const {start, length} = urlDiagnostic !; + const {start, length} = urlDiagnostic!; expect(start).toBe(marker.start); expect(length).toBe(marker.length); }); @@ -832,7 +850,7 @@ describe('diagnostics', () => { }) export class MyComponent {}`); - const diagnostics = ngLS.getSemanticDiagnostics(fileName) !; + const diagnostics = ngLS.getSemanticDiagnostics(fileName)!; const urlDiagnostic = diagnostics.find(d => d.messageText === 'URL does not point to a valid file'); expect(urlDiagnostic).toBeUndefined(); @@ -849,9 +867,9 @@ describe('diagnostics', () => { const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT); expect(diags.length).toBe(1); const {file, messageText, start, length} = diags[0]; - expect(file !.fileName).toBe(APP_COMPONENT); + expect(file!.fileName).toBe(APP_COMPONENT); expect(messageText).toBe(`Component 'AppComponent' must have a template or templateUrl`); - expect(content.substring(start !, start ! + length !)).toBe('AppComponent'); + expect(content.substring(start!, start! + length!)).toBe('AppComponent'); }); it('should report diagnostic for both template and templateUrl', () => { @@ -867,10 +885,10 @@ describe('diagnostics', () => { const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT); expect(diags.length).toBe(1); const {file, messageText, start, length} = diags[0]; - expect(file !.fileName).toBe(APP_COMPONENT); + expect(file!.fileName).toBe(APP_COMPONENT); expect(messageText) .toBe(`Component 'AppComponent' must not have both template and templateUrl`); - expect(content.substring(start !, start ! + length !)).toBe('AppComponent'); + expect(content.substring(start!, start! + length!)).toBe('AppComponent'); }); it('should report errors for invalid styleUrls', () => { @@ -882,12 +900,12 @@ describe('diagnostics', () => { const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile'); - const diagnostics = ngLS.getSemanticDiagnostics(fileName) !; + const diagnostics = ngLS.getSemanticDiagnostics(fileName)!; const urlDiagnostic = diagnostics.find(d => d.messageText === 'URL does not point to a valid file'); expect(urlDiagnostic).toBeDefined(); - const {start, length} = urlDiagnostic !; + const {start, length} = urlDiagnostic!; expect(start).toBe(marker.start); expect(length).toBe(marker.length); }); @@ -902,7 +920,7 @@ describe('diagnostics', () => { }) export class AppComponent {}`); - const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT) !; + const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT)!; expect(diagnostics.length).toBe(0); }); }); @@ -926,14 +944,14 @@ describe('diagnostics', () => { `element references do not contain such a member`); // Assert that the span is actually highlight the bounded text. The span // would be off if CRLF endings are not handled properly. - expect(content.substring(start !, start ! + length !)).toBe(`line${i}`); + expect(content.substring(start!, start! + length!)).toBe(`line${i}`); } }); it('should work correctly with CRLF endings in inline template', () => { const fileName = mockHost.addCode( '\n@Component({template:`\r\n\r\n{{line}}`})export class ComponentCRLF {}'); - const content = mockHost.readFile(fileName) !; + const content = mockHost.readFile(fileName)!; const ngDiags = ngLS.getSemanticDiagnostics(fileName); expect(ngDiags.length).toBeGreaterThan(0); const {messageText, start, length} = ngDiags[0]; @@ -942,7 +960,7 @@ describe('diagnostics', () => { `Identifier 'line' is not defined. ` + `The component declaration, template variable declarations, and ` + `element references do not contain such a member`); - expect(content.substring(start !, start ! + length !)).toBe('line'); + expect(content.substring(start!, start! + length!)).toBe('line'); }); it('should not produce diagnostics for non-exported directives', () => { @@ -972,8 +990,7 @@ describe('diagnostics', () => { `Consider using the safe navigation operator (optional?.toLowerCase) ` + `or non-null assertion operator (optional!.toLowerCase).`); expect(category).toBe(ts.DiagnosticCategory.Suggestion); - expect(content.substring(start !, start ! + length !)).toBe('optional.toLowerCase()'); - + expect(content.substring(start!, start! + length!)).toBe('optional.toLowerCase()'); }); it('should suggest ? or ! operator if property receiver is nullable', () => { @@ -987,7 +1004,7 @@ describe('diagnostics', () => { `Consider using the safe navigation operator (optional?.length) ` + `or non-null assertion operator (optional!.length).`); expect(category).toBe(ts.DiagnosticCategory.Suggestion); - expect(content.substring(start !, start ! + length !)).toBe('optional.length'); + expect(content.substring(start!, start! + length!)).toBe('optional.length'); }); it('should report error if method is not found on non-nullable receiver', () => { @@ -1003,7 +1020,7 @@ describe('diagnostics', () => { expect(messageText) .toBe(`Identifier 'someMethod' is not defined. 'string' does not contain such a member`); expect(category).toBe(ts.DiagnosticCategory.Error); - expect(content.substring(start !, start ! + length !)).toBe(expression); + expect(content.substring(start!, start! + length!)).toBe(expression); } }); @@ -1020,7 +1037,7 @@ describe('diagnostics', () => { expect(messageText) .toBe(`Identifier 'someProp' is not defined. 'string' does not contain such a member`); expect(category).toBe(ts.DiagnosticCategory.Error); - expect(content.substring(start !, start ! + length !)).toBe(expression); + expect(content.substring(start!, start! + length!)).toBe(expression); } }); @@ -1030,12 +1047,12 @@ describe('diagnostics', () => { <p>{{myClick()()}}</p> `); - const content = mockHost.readFile(TEST_TEMPLATE) !; + const content = mockHost.readFile(TEST_TEMPLATE)!; const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); expect(diags.length).toBe(1); - const {messageText, start, length} = diags[0] !; + const {messageText, start, length} = diags[0]!; expect(messageText).toBe(`Call target 'myClick()' has non-callable type 'void'.`); - expect(content.substring(start !, start ! + length !)).toBe('myClick()()'); + expect(content.substring(start!, start! + length!)).toBe('myClick()()'); }); }); }); diff --git a/packages/language-service/test/expression_diagnostics_spec.ts b/packages/language-service/test/expression_diagnostics_spec.ts index 8bc33a2a70e68..43640d2443512 100644 --- a/packages/language-service/test/expression_diagnostics_spec.ts +++ b/packages/language-service/test/expression_diagnostics_spec.ts @@ -13,7 +13,7 @@ import * as ts from 'typescript'; import {getTemplateExpressionDiagnostics} from '../src/expression_diagnostics'; -import {DiagnosticContext, MockLanguageServiceHost, getDiagnosticTemplateInfo} from './mocks'; +import {DiagnosticContext, getDiagnosticTemplateInfo, MockLanguageServiceHost} from './mocks'; describe('expression diagnostics', () => { let registry: ts.DocumentRegistry; @@ -27,15 +27,15 @@ describe('expression diagnostics', () => { registry = ts.createDocumentRegistry(false, '/src'); host = new MockLanguageServiceHost(['app/app.component.ts'], FILES, '/src'); service = ts.createLanguageService(host, registry); - const program = service.getProgram() !; + const program = service.getProgram()!; const checker = program.getTypeChecker(); - const symbolResolverHost = new ReflectorHost(() => program !, host); - context = new DiagnosticContext(service, program !, checker, symbolResolverHost); + const symbolResolverHost = new ReflectorHost(() => program!, host); + context = new DiagnosticContext(service, program!, checker, symbolResolverHost); type = context.getStaticSymbol('app/app.component.ts', 'AppComponent'); }); it('should have no diagnostics in default app', () => { - function messageToString(messageText: string | ts.DiagnosticMessageChain): string { + function messageToString(messageText: string|ts.DiagnosticMessageChain): string { if (typeof messageText == 'string') { return messageText; } else { @@ -107,13 +107,14 @@ describe('expression diagnostics', () => { {{p.name.first}} {{p.name.last}} </div> `)); - it('should reject misspelled field in *ngFor', () => reject( - ` + it('should reject misspelled field in *ngFor', + () => reject( + ` <div *ngFor="let p of people"> {{p.names.first}} {{p.name.last}} </div> `, - 'Identifier \'names\' is not defined')); + 'Identifier \'names\' is not defined')); it('should accept an async expression', () => accept('{{(promised_person | async)?.name.first || ""}}')); it('should reject an async misspelled field', @@ -124,25 +125,27 @@ describe('expression diagnostics', () => { {{p.name.first}} {{p.name.last}} </div> `)); - it('should reject misspelled field an async *ngFor', () => reject( - ` + it('should reject misspelled field an async *ngFor', + () => reject( + ` <div *ngFor="let p of promised_people | async"> {{p.name.first}} {{p.nume.last}} </div> `, - 'Identifier \'nume\' is not defined')); + 'Identifier \'nume\' is not defined')); it('should accept an async *ngIf', () => accept(` <div *ngIf="promised_person | async as p"> {{p.name.first}} {{p.name.last}} </div> `)); - it('should reject misspelled field in async *ngIf', () => reject( - ` + it('should reject misspelled field in async *ngIf', + () => reject( + ` <div *ngIf="promised_person | async as p"> {{p.name.first}} {{p.nume.last}} </div> `, - 'Identifier \'nume\' is not defined')); + 'Identifier \'nume\' is not defined')); it('should reject access to potentially undefined field', () => reject( `<div>{{maybe_person.name.first}}`, diff --git a/packages/language-service/test/global_symbols_spec.ts b/packages/language-service/test/global_symbols_spec.ts index f85241bb42bd7..bb0aee8a775d2 100644 --- a/packages/language-service/test/global_symbols_spec.ts +++ b/packages/language-service/test/global_symbols_spec.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript/lib/tsserverlibrary'; -import {EMPTY_SYMBOL_TABLE, createGlobalSymbolTable} from '../src/global_symbols'; +import {createGlobalSymbolTable, EMPTY_SYMBOL_TABLE} from '../src/global_symbols'; import {getSymbolQuery} from '../src/typescript_symbols'; import {MockTypescriptHost} from './test_utils'; @@ -18,7 +18,7 @@ describe('GlobalSymbolTable', () => { const tsLS = ts.createLanguageService(mockHost); it(`contains $any()`, () => { - const program = tsLS.getProgram() !; + const program = tsLS.getProgram()!; const typeChecker = program.getTypeChecker(); const source = ts.createSourceFile('foo.ts', '', ts.ScriptTarget.ES2015); const query = getSymbolQuery(program, typeChecker, source, () => EMPTY_SYMBOL_TABLE); diff --git a/packages/language-service/test/hover_spec.ts b/packages/language-service/test/hover_spec.ts index dc3bb7098b4fa..425d9078f4da6 100644 --- a/packages/language-service/test/hover_spec.ts +++ b/packages/language-service/test/hover_spec.ts @@ -22,7 +22,9 @@ describe('hover', () => { const ngLSHost = new TypeScriptServiceHost(mockHost, tsLS); const ngLS = createLanguageService(ngLSHost); - beforeEach(() => { mockHost.reset(); }); + beforeEach(() => { + mockHost.reset(); + }); describe('location of hover', () => { it('should find members in a text interpolation', () => { @@ -30,7 +32,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TemplateReference.title: string'); }); @@ -40,7 +42,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TemplateReference.title: string'); }); @@ -50,7 +52,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TemplateReference.title: string'); }); @@ -60,7 +62,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TemplateReference.title: string'); }); @@ -70,7 +72,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TemplateReference.title: string'); }); @@ -80,7 +82,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'anyValue'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TemplateReference.anyValue: any'); }); @@ -92,19 +94,51 @@ describe('hover', () => { const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeDefined(); - const documentation = toText(quickInfo !.documentation); + const documentation = toText(quickInfo!.documentation); expect(documentation).toBe('This is the title of the `TemplateReference` Component.'); }); - it('should work for property reads', () => { - mockHost.override(TEST_TEMPLATE, `<div>{{«title»}}</div>`); - const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); - const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); - expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; - expect(textSpan).toEqual(marker); - expect(textSpan.length).toBe('title'.length); - expect(toText(displayParts)).toBe('(property) TemplateReference.title: string'); + describe('property reads', () => { + it('should work for class members', () => { + mockHost.override(TEST_TEMPLATE, `<div>{{«title»}}</div>`); + const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); + const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); + expect(quickInfo).toBeTruthy(); + const {textSpan, displayParts} = quickInfo!; + expect(textSpan).toEqual(marker); + expect(toText(displayParts)).toBe('(property) TemplateReference.title: string'); + }); + + it('should work for array members', () => { + mockHost.override(TEST_TEMPLATE, `<div *ngFor="let hero of heroes">{{«hero»}}</div>`); + const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero'); + const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); + expect(quickInfo).toBeTruthy(); + const {textSpan, displayParts} = quickInfo!; + expect(textSpan).toEqual(marker); + expect(toText(displayParts)).toBe('(variable) hero: Hero'); + }); + + it('should work for ReadonlyArray members (#36191)', () => { + mockHost.override( + TEST_TEMPLATE, `<div *ngFor="let hero of readonlyHeroes">{{«hero»}}</div>`); + const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero'); + const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); + expect(quickInfo).toBeTruthy(); + const {textSpan, displayParts} = quickInfo!; + expect(textSpan).toEqual(marker); + expect(toText(displayParts)).toBe('(variable) hero: Readonly<Hero>'); + }); + + it('should work for const array members (#36191)', () => { + mockHost.override(TEST_TEMPLATE, `<div *ngFor="let name of constNames">{{«name»}}</div>`); + const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'name'); + const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); + expect(quickInfo).toBeTruthy(); + const {textSpan, displayParts} = quickInfo!; + expect(textSpan).toEqual(marker); + expect(toText(displayParts)).toBe('(variable) name: { readonly name: "name"; }'); + }); }); it('should work for method calls', () => { @@ -112,7 +146,7 @@ describe('hover', () => { const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'myClick'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(method) TemplateReference.myClick: (event: any) => void'); }); @@ -122,7 +156,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'trackBy'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(method) NgForOf<T, U>.ngForTrackBy: TrackByFunction<T>'); }); @@ -132,7 +166,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'heroes'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TemplateReference.heroes: Hero[]'); }); @@ -143,7 +177,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'date'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)) .toBe( @@ -155,7 +189,7 @@ describe('hover', () => { const position = content.indexOf('$any'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, position); expect(quickInfo).toBeDefined(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual({ start: position, length: '$any(title)'.length, @@ -170,7 +204,7 @@ describe('hover', () => { const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeDefined(); - const documentation = toText(quickInfo !.documentation); + const documentation = toText(quickInfo!.documentation); expect(documentation).toBe('This Component provides the `test-comp` selector.'); }); @@ -179,7 +213,7 @@ describe('hover', () => { const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeDefined(); - const {displayParts, documentation} = quickInfo !; + const {displayParts, documentation} = quickInfo!; expect(toText(displayParts)) .toBe('(component) AppModule.TestComponent: typeof TestComponent'); expect(toText(documentation)).toBe('This Component provides the `test-comp` selector.'); @@ -190,7 +224,7 @@ describe('hover', () => { const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeDefined(); - const {displayParts, textSpan} = quickInfo !; + const {displayParts, textSpan} = quickInfo!; expect(toText(displayParts)).toBe('(directive) AppModule.StringModel: typeof StringModel'); expect(content.substring(textSpan.start, textSpan.start + textSpan.length)) .toBe('string-model'); @@ -201,7 +235,7 @@ describe('hover', () => { const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'test'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(event) TestComponent.testEvent: EventEmitter<any>'); }); @@ -211,7 +245,7 @@ describe('hover', () => { const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'tcName'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TestComponent.name: string'); }); @@ -222,7 +256,7 @@ describe('hover', () => { const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'model'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) StringModel.model: string'); }); @@ -232,7 +266,7 @@ describe('hover', () => { const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'ngFor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(directive) NgForOf: typeof NgForOf'); }); @@ -240,12 +274,12 @@ describe('hover', () => { describe('hovering on TypeScript nodes', () => { it('should work for component TypeScript declarations', () => { - const content = mockHost.readFile(PARSING_CASES) !; + const content = mockHost.readFile(PARSING_CASES)!; const position = content.indexOf('TemplateReference'); expect(position).toBeGreaterThan(0); const quickInfo = ngLS.getQuickInfoAtPosition(PARSING_CASES, position); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual({ start: position, length: 'TemplateReference'.length, @@ -254,12 +288,12 @@ describe('hover', () => { }); it('should work for directive TypeScript declarations', () => { - const content = mockHost.readFile(PARSING_CASES) !; + const content = mockHost.readFile(PARSING_CASES)!; const position = content.indexOf('StringModel'); expect(position).toBeGreaterThan(0); const quickInfo = ngLS.getQuickInfoAtPosition(PARSING_CASES, position); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual({ start: position, length: 'StringModel'.length, @@ -281,7 +315,7 @@ describe('hover', () => { const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); - const {textSpan, displayParts} = quickInfo !; + const {textSpan, displayParts} = quickInfo!; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TemplateReference.title: string'); }); diff --git a/packages/language-service/test/html_info_spec.ts b/packages/language-service/test/html_info_spec.ts index 62cefdbfc22ce..9007a3f49365a 100644 --- a/packages/language-service/test/html_info_spec.ts +++ b/packages/language-service/test/html_info_spec.ts @@ -28,11 +28,10 @@ describe('html_info', () => { const elements = SchemaInformation.instance.allKnownElements(); for (const element of elements) { for (const prop of SchemaInformation.instance.propertiesOf(element)) { - expect(domRegistry.hasProperty(element, prop, [])); + expect(domRegistry.hasProperty(element, prop, [])).toBeTrue(); } } }); - }); function uniqueElements<T>(a: T[], b: T[]): T[] { @@ -49,4 +48,4 @@ function uniqueElements<T>(a: T[], b: T[]): T[] { } } return result; -} \ No newline at end of file +} diff --git a/packages/language-service/test/language_service_spec.ts b/packages/language-service/test/language_service_spec.ts index a271928230bd0..3fd10de9cd4d2 100644 --- a/packages/language-service/test/language_service_spec.ts +++ b/packages/language-service/test/language_service_spec.ts @@ -21,20 +21,25 @@ describe('service without angular', () => { const fileName = '/app/test.ng'; const position = mockHost.getLocationMarkerFor(fileName, 'h1-content').start; - beforeEach(() => { mockHost.reset(); }); + beforeEach(() => { + mockHost.reset(); + }); - it('should not crash a get diagnostics', - () => { expect(() => ngService.getSemanticDiagnostics(fileName)).not.toThrow(); }); + it('should not crash a get diagnostics', () => { + expect(() => ngService.getSemanticDiagnostics(fileName)).not.toThrow(); + }); - it('should not crash a completion', - () => { expect(() => ngService.getCompletionsAtPosition(fileName, position)).not.toThrow(); }); + it('should not crash a completion', () => { + expect(() => ngService.getCompletionsAtPosition(fileName, position)).not.toThrow(); + }); it('should not crash a get definition', () => { expect(() => ngService.getDefinitionAndBoundSpan(fileName, position)).not.toThrow(); }); - it('should not crash a hover', - () => { expect(() => ngService.getQuickInfoAtPosition(fileName, position)).not.toThrow(); }); + it('should not crash a hover', () => { + expect(() => ngService.getQuickInfoAtPosition(fileName, position)).not.toThrow(); + }); it('should not crash with an incomplete class', () => { mockHost.addCode('\nexport class'); diff --git a/packages/language-service/test/mocks.ts b/packages/language-service/test/mocks.ts index b58ea156519dd..4046ae37ad3b9 100644 --- a/packages/language-service/test/mocks.ts +++ b/packages/language-service/test/mocks.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver} from '@angular/compiler'; +import {analyzeNgModules, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, createOfflineCompileUrlResolver, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, Parser, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, TemplateParser} from '@angular/compiler'; import {Directory, MockAotContext} from '@angular/compiler-cli/test/mocks'; import {setup} from '@angular/compiler-cli/test/test_support'; import {ViewEncapsulation, ɵConsole as Console} from '@angular/core'; @@ -14,7 +14,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; -import {DiagnosticTemplateInfo} from '../src/expression_diagnostics'; +import {DiagnosticTemplateInfo} from '../src/types'; import {getClassMembers, getPipesTable, getSymbolQuery} from '../src/typescript_symbols'; const realFiles = new Map<string, string>(); @@ -45,11 +45,17 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost { this.context = new MockAotContext(currentDirectory, files); } - getCompilationSettings(): ts.CompilerOptions { return this.options; } + getCompilationSettings(): ts.CompilerOptions { + return this.options; + } - getScriptFileNames(): string[] { return this.scripts; } + getScriptFileNames(): string[] { + return this.scripts; + } - getScriptVersion(fileName: string): string { return '0'; } + getScriptVersion(fileName: string): string { + return '0'; + } getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined { const content = this.internalReadFile(fileName); @@ -58,15 +64,25 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost { } } - getCurrentDirectory(): string { return this.context.currentDirectory; } + getCurrentDirectory(): string { + return this.context.currentDirectory; + } - getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'lib.d.ts'; + } - readFile(fileName: string): string { return this.internalReadFile(fileName) as string; } + readFile(fileName: string): string { + return this.internalReadFile(fileName) as string; + } - readResource(fileName: string): Promise<string> { return Promise.resolve(''); } + readResource(fileName: string): Promise<string> { + return Promise.resolve(''); + } - assumeFileExists(fileName: string): void { this.assumedExist.add(fileName); } + assumeFileExists(fileName: string): void { + this.assumedExist.add(fileName); + } fileExists(fileName: string): boolean { return this.assumedExist.has(fileName) || this.internalReadFile(fileName) != null; @@ -99,10 +115,18 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost { const staticSymbolCache = new StaticSymbolCache(); const summaryResolver = new AotSummaryResolver( { - loadSummary(filePath: string) { return null; }, - isSourceFile(sourceFilePath: string) { return true; }, - toSummaryFileName(sourceFilePath: string) { return sourceFilePath; }, - fromSummaryFileName(filePath: string): string{return filePath;}, + loadSummary(filePath: string) { + return null; + }, + isSourceFile(sourceFilePath: string) { + return true; + }, + toSummaryFileName(sourceFilePath: string) { + return sourceFilePath; + }, + fromSummaryFileName(filePath: string): string { + return filePath; + }, }, staticSymbolCache); @@ -117,7 +141,9 @@ export class DiagnosticContext { public service: ts.LanguageService, public program: ts.Program, public checker: ts.TypeChecker, public host: StaticSymbolResolverHost) {} - private collectError(e: any, path?: string) { this._errors.push({e, path}); } + private collectError(e: any, path?: string) { + this._errors.push({e, path}); + } private get staticSymbolResolver(): StaticSymbolResolver { let result = this._staticSymbolResolver; @@ -133,7 +159,7 @@ export class DiagnosticContext { if (!this._reflector) { const ssr = this.staticSymbolResolver; const result = this._reflector = new StaticReflector( - summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath !)); + summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath!)); this._reflector = result; return result; } @@ -148,11 +174,15 @@ export class DiagnosticContext { const pipeResolver = new PipeResolver(this.reflector); const elementSchemaRegistry = new DomElementSchemaRegistry(); const resourceLoader = new class extends ResourceLoader { - get(url: string): Promise<string> { return Promise.resolve(''); } + get(url: string): Promise<string> { + return Promise.resolve(''); + } }; const urlResolver = createOfflineCompileUrlResolver(); const htmlParser = new class extends HtmlParser { - parse(): ParseTreeResult { return new ParseTreeResult([], []); } + parse(): ParseTreeResult { + return new ParseTreeResult([], []); + } }; // This tracks the CompileConfig in codegen.ts. Currently these options @@ -174,7 +204,11 @@ export class DiagnosticContext { get analyzedModules(): NgAnalyzedModules { let analyzedModules = this._analyzedModules; if (!analyzedModules) { - const analyzeHost = {isSourceFile(filePath: string) { return true; }}; + const analyzeHost = { + isSourceFile(filePath: string) { + return true; + } + }; const programFiles = this.program.getSourceFiles().map(sf => sf.fileName); analyzedModules = this._analyzedModules = analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver); @@ -198,7 +232,7 @@ function compileTemplate(context: DiagnosticContext, type: StaticSymbol, templat const config = new CompilerConfig(); const parser = new TemplateParser( config, context.reflector, expressionParser, new DomElementSchemaRegistry(), htmlParser, - null !, []); + null!, []); const htmlResult = htmlParser.parse(template, '', {tokenizeExpansionForms: true}); const analyzedModules = context.analyzedModules; // let errors: Diagnostic[]|undefined = undefined; @@ -214,8 +248,11 @@ function compileTemplate(context: DiagnosticContext, type: StaticSymbol, templat return { htmlAst: htmlResult.rootNodes, templateAst: parseResult.templateAst, - directive: metadata, directives, pipes, - parseErrors: parseResult.errors, expressionParser + directive: metadata, + directives, + pipes, + parseErrors: parseResult.errors, + expressionParser }; } } @@ -236,7 +273,9 @@ export function getDiagnosticTemplateInfo( sourceFile, context.program, context.checker, compiledTemplate.pipes)); return { fileName: templateFile, - offset: 0, query, members, + offset: 0, + query, + members, htmlAst: compiledTemplate.htmlAst, templateAst: compiledTemplate.templateAst, source: sourceFile.text, @@ -246,6 +285,6 @@ export function getDiagnosticTemplateInfo( } } -function removeMissing<T>(values: (T | null | undefined)[]): T[] { +function removeMissing<T>(values: (T|null|undefined)[]): T[] { return values.filter(e => !!e) as T[]; } diff --git a/packages/language-service/test/project/app/expression-cases.ts b/packages/language-service/test/project/app/expression-cases.ts deleted file mode 100644 index decc717a62baf..0000000000000 --- a/packages/language-service/test/project/app/expression-cases.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @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 {Component} from '@angular/core'; - -export interface Person { - name: string; - age: number; -} - -@Component({ - template: '{{~{start-foo}foo~{end-foo}}}', -}) -export class WrongFieldReference { - bar = 'bar'; -} - -@Component({ - template: '{{~{start-nam}person.nam~{end-nam}}}', -}) -export class WrongSubFieldReference { - person: Person = {name: 'Bob', age: 23}; -} - -@Component({ - template: '{{~{start-myField}myField~{end-myField}}}', -}) -export class PrivateReference { - private myField = 'My Field'; -} - -@Component({ - template: '{{~{start-mod}"a" ~{end-mod}% 2}}', -}) -export class ExpectNumericType { -} - -@Component({ - template: '{{ (name | lowercase).~{string-pipe}substring }}', -}) -export class LowercasePipe { - name: string = 'name'; -} diff --git a/packages/language-service/test/project/app/main.ts b/packages/language-service/test/project/app/main.ts index daa4ef3c0bd5b..20b18045287ce 100644 --- a/packages/language-service/test/project/app/main.ts +++ b/packages/language-service/test/project/app/main.ts @@ -9,37 +9,20 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {FormsModule} from '@angular/forms'; - import {AppComponent} from './app.component'; -import * as ExpressionCases from './expression-cases'; -import * as NgForCases from './ng-for-cases'; -import * as NgIfCases from './ng-if-cases'; import * as ParsingCases from './parsing-cases'; @NgModule({ imports: [CommonModule, FormsModule], declarations: [ AppComponent, - ExpressionCases.ExpectNumericType, - ExpressionCases.LowercasePipe, - ExpressionCases.PrivateReference, - ExpressionCases.WrongFieldReference, - ExpressionCases.WrongSubFieldReference, - NgForCases.UnknownEven, - NgForCases.UnknownPeople, - NgForCases.UnknownTrackBy, - NgIfCases.ShowIf, - ParsingCases.AsyncForUsingComponent, ParsingCases.CaseIncompleteOpen, ParsingCases.CaseMissingClosing, ParsingCases.CaseUnknown, ParsingCases.CounterDirective, - ParsingCases.EmptyInterpolation, ParsingCases.HintModel, ParsingCases.NoValueAttribute, ParsingCases.NumberModel, - ParsingCases.Pipes, - ParsingCases.References, ParsingCases.StringModel, ParsingCases.TemplateReference, ParsingCases.TestComponent, @@ -51,4 +34,4 @@ export class AppModule { declare function bootstrap(v: any): void; - bootstrap(AppComponent); +bootstrap(AppComponent); diff --git a/packages/language-service/test/project/app/ng-for-cases.ts b/packages/language-service/test/project/app/ng-for-cases.ts deleted file mode 100644 index 9b46ed5dc3dca..0000000000000 --- a/packages/language-service/test/project/app/ng-for-cases.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @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 {Component} from '@angular/core'; - -export interface Person { - name: string; - age: number; -} - -@Component({ - template: ` - <div *ngFor="let person of ~{start-people_1}people_1~{end-people_1}"> - <span>{{person.name}}</span> - </div>`, -}) -export class UnknownPeople { -} - -@Component({ - template: ` - <div ~{start-even_1}*ngFor="let person of people; let e = even_1"~{end-even_1}> - <span>{{person.name}}</span> - </div>`, -}) -export class UnknownEven { - people: Person[] = []; -} - -@Component({ - template: ` - <div *ngFor="let person of people; trackBy ~{start-trackBy_1}trackBy_1~{end-trackBy_1}"> - <span>{{person.name}}</span> - </div>`, -}) -export class UnknownTrackBy { - people: Person[] = []; -} diff --git a/packages/language-service/test/project/app/ng-if-cases.ts b/packages/language-service/test/project/app/ng-if-cases.ts deleted file mode 100644 index 3c9917d772280..0000000000000 --- a/packages/language-service/test/project/app/ng-if-cases.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @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 {Component} from '@angular/core'; - -@Component({ - template: ` - <div ~{start-implicit}*ngIf="show; let l=unknown"~{end-implicit}> - Showing now! - </div>`, -}) -export class ShowIf { - show = false; -} diff --git a/packages/language-service/test/project/app/parsing-cases.ts b/packages/language-service/test/project/app/parsing-cases.ts index 1d5c01c70e5f0..36a79948a5ee2 100644 --- a/packages/language-service/test/project/app/parsing-cases.ts +++ b/packages/language-service/test/project/app/parsing-cases.ts @@ -31,13 +31,6 @@ export class CaseMissingClosing { export class CaseUnknown { } -@Component({ - template: '<h1>{{data | ~{before-pipe}lowe~{in-pipe}rcase~{after-pipe} }}', -}) -export class Pipes { - data = 'Some string'; -} - @Component({ template: '<h1 h~{no-value-attribute}></h1>', }) @@ -70,45 +63,6 @@ export class HintModel { hintChange: EventEmitter<string> = new EventEmitter(); } -interface Person { - name: string; - age: number; - street: string; -} - -@Component({ - template: ` - <div *ngFor="let person of people | async"> - {{person.~{async-person-name}name}} - </div> - <div *ngIf="promisedPerson | async as person"> - {{person.~{promised-person-name}name}} - </div> - `, -}) -export class AsyncForUsingComponent { - people: Promise<Person[]> = Promise.resolve([]); - promisedPerson: Promise<Person> = Promise.resolve({ - name: 'John Doe', - age: 42, - street: '123 Angular Ln', - }); -} - -@Component({ - template: ` - <div #div> - <test-comp #test1> - {{~{test-comp-content}}} - {{test1.~{test-comp-after-test}name}} - {{div.~{test-comp-after-div}.innerText}} - </test-comp> - </div> - <test-comp #test2></test-comp>`, -}) -export class References { -} - class CounterDirectiveContext<T> { constructor(public $implicit: T) {} } @@ -128,8 +82,8 @@ export class CounterDirective implements OnChanges { } interface WithContextDirectiveContext { - $implicit: {implicitPerson: Person;}; - nonImplicitPerson: Person; + $implicit: {implicitPerson: Hero;}; + nonImplicitPerson: Hero; } @Directive({selector: '[withContext]'}) @@ -163,7 +117,9 @@ export class TemplateReference { */ title = 'Some title'; hero: Hero = {id: 1, name: 'Windstorm'}; + heroP = Promise.resolve(this.hero); heroes: Hero[] = [this.hero]; + heroesP = Promise.resolve(this.heroes); tupleArray: [string, Hero] = ['test', this.hero]; league: Hero[][] = [this.heroes]; heroesByName: {[name: string]: Hero} = {}; @@ -174,12 +130,8 @@ export class TemplateReference { index = null; myClick(event: any) {} birthday = new Date(); -} - -@Component({ - template: '{{~{empty-interpolation}}}', -}) -export class EmptyInterpolation { - title = 'Some title'; - subTitle = 'Some sub title'; + readonlyHeroes: ReadonlyArray<Readonly<Hero>> = this.heroes; + constNames = [{name: 'name'}] as const; + private myField = 'My Field'; + strOrNumber: string|number = ''; } diff --git a/packages/language-service/test/reflector_host_spec.ts b/packages/language-service/test/reflector_host_spec.ts index 717c91e5368cd..39aa4cc922911 100644 --- a/packages/language-service/test/reflector_host_spec.ts +++ b/packages/language-service/test/reflector_host_spec.ts @@ -15,7 +15,6 @@ import {TypeScriptServiceHost} from '../src/typescript_host'; import {MockTypescriptHost} from './test_utils'; describe('reflector_host_spec', () => { - // Regression #21811 it('should be able to find angular under windows', () => { const originalJoin = path.join; @@ -24,15 +23,16 @@ describe('reflector_host_spec', () => { new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], 'node_modules', { ...path, join: (...args: string[]) => originalJoin.apply(path, args), - posix: - {...path.posix, join: (...args: string[]) => originalPosixJoin.apply(path, args)} + posix: {...path.posix, join: (...args: string[]) => originalPosixJoin.apply(path, args)} }); const reflectorHost = new ReflectorHost(() => undefined as any, mockHost); if (process.platform !== 'win32') { // If we call this in Windows it will cause a 'Maximum call stack size exceeded error' // Because we are spying on the same function that we are call faking - spyOn(path, 'join').and.callFake((...args: string[]) => { return path.win32.join(...args); }); + spyOn(path, 'join').and.callFake((...args: string[]) => { + return path.win32.join(...args); + }); } const result = reflectorHost.moduleNameToFileName('@angular/core'); diff --git a/packages/language-service/test/template_spec.ts b/packages/language-service/test/template_spec.ts deleted file mode 100644 index 7617e2779baa4..0000000000000 --- a/packages/language-service/test/template_spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @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 * as ts from 'typescript'; -import {getClassDeclFromDecoratorProp} from '../src/template'; -import {MockTypescriptHost} from './test_utils'; - -describe('getClassDeclFromTemplateNode', () => { - - it('should find class declaration in syntax-only mode', () => { - const sourceFile = ts.createSourceFile( - 'foo.ts', ` - @Component({ - template: '<div></div>' - }) - class MyComponent {}`, - ts.ScriptTarget.ES2015, true /* setParentNodes */); - function visit(node: ts.Node): ts.ClassDeclaration|undefined { - if (ts.isPropertyAssignment(node)) { - return getClassDeclFromDecoratorProp(node); - } - return node.forEachChild(visit); - } - const classDecl = sourceFile.forEachChild(visit); - expect(classDecl).toBeTruthy(); - expect(classDecl !.kind).toBe(ts.SyntaxKind.ClassDeclaration); - expect((classDecl as ts.ClassDeclaration).name !.text).toBe('MyComponent'); - }); - - - it('should return class declaration for AppComponent', () => { - const host = new MockTypescriptHost(['/app/app.component.ts']); - const tsLS = ts.createLanguageService(host); - const sourceFile = tsLS.getProgram() !.getSourceFile('/app/app.component.ts'); - expect(sourceFile).toBeTruthy(); - const classDecl = sourceFile !.forEachChild(function visit(node): ts.Node | undefined { - if (ts.isPropertyAssignment(node)) { - return getClassDeclFromDecoratorProp(node); - } - return node.forEachChild(visit); - }); - expect(classDecl).toBeTruthy(); - expect(ts.isClassDeclaration(classDecl !)).toBe(true); - expect((classDecl as ts.ClassDeclaration).name !.text).toBe('AppComponent'); - }); -}); diff --git a/packages/language-service/test/test_utils.ts b/packages/language-service/test/test_utils.ts index 7973e2e37bda9..c58e76ee6f15e 100644 --- a/packages/language-service/test/test_utils.ts +++ b/packages/language-service/test/test_utils.ts @@ -57,11 +57,11 @@ function isFile(path: string) { function loadTourOfHeroes(): ReadonlyMap<string, string> { const {TEST_SRCDIR} = process.env; const root = - path.join(TEST_SRCDIR !, 'angular', 'packages', 'language-service', 'test', 'project'); + path.join(TEST_SRCDIR!, 'angular', 'packages', 'language-service', 'test', 'project'); const dirs = [root]; const files = new Map<string, string>(); while (dirs.length) { - const dirPath = dirs.pop() !; + const dirPath = dirs.pop()!; for (const filePath of fs.readdirSync(dirPath)) { const absPath = path.join(dirPath, filePath); if (isFile(absPath)) { @@ -140,11 +140,17 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { this.projectVersion++; } - getCompilationSettings(): ts.CompilerOptions { return {...this.options}; } + getCompilationSettings(): ts.CompilerOptions { + return {...this.options}; + } - getProjectVersion(): string { return this.projectVersion.toString(); } + getProjectVersion(): string { + return this.projectVersion.toString(); + } - getScriptFileNames(): string[] { return this.scriptNames; } + getScriptFileNames(): string[] { + return this.scriptNames; + } getScriptVersion(fileName: string): string { return (this.scriptVersion.get(fileName) || 0).toString(); @@ -156,9 +162,13 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { return undefined; } - getCurrentDirectory(): string { return '/'; } + getCurrentDirectory(): string { + return '/'; + } - getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'lib.d.ts'; + } directoryExists(directoryName: string): boolean { if (this.overrideDirectory.has(directoryName)) return true; @@ -172,7 +182,9 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { return this.pathExists(effectiveName); } - fileExists(fileName: string): boolean { return this.getRawFileContent(fileName) != null; } + fileExists(fileName: string): boolean { + return this.getRawFileContent(fileName) != null; + } readFile(fileName: string): string|undefined { const content = this.getRawFileContent(fileName); @@ -242,7 +254,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { private pathExists(path: string): boolean { if (this.existsCache.has(path)) { - return this.existsCache.get(path) !; + return this.existsCache.get(path)!; } const exists = fs.existsSync(path); @@ -411,8 +423,9 @@ function getReferenceMarkers(value: string): ReferenceResult { let adjustment = 0; const text = value.replace( - referenceMarker, (match: string, text: string, reference: string, _: string, - definition: string, definitionName: string, index: number): string => { + referenceMarker, + (match: string, text: string, reference: string, _: string, definition: string, + definitionName: string, index: number): string => { const result = reference ? text : text.replace(/ᐱ/g, ''); const span: Span = {start: index - adjustment, end: index - adjustment + result.length}; const markers = reference ? references : definitions; diff --git a/packages/language-service/test/ts_plugin_spec.ts b/packages/language-service/test/ts_plugin_spec.ts index 37c373d39862a..3831e67fe2076 100644 --- a/packages/language-service/test/ts_plugin_spec.ts +++ b/packages/language-service/test/ts_plugin_spec.ts @@ -26,7 +26,7 @@ const mockProject = { describe('plugin', () => { const mockHost = new MockTypescriptHost(['/app/main.ts']); const tsLS = ts.createLanguageService(mockHost); - const program = tsLS.getProgram() !; + const program = tsLS.getProgram()!; const plugin = create({ languageService: tsLS, languageServiceHost: mockHost, @@ -35,7 +35,9 @@ describe('plugin', () => { config: {}, }); - beforeEach(() => { mockHost.reset(); }); + beforeEach(() => { + mockHost.reset(); + }); it('should produce TypeScript diagnostics', () => { const fileName = '/foo.ts'; @@ -55,8 +57,8 @@ describe('plugin', () => { const compilerDiags = tsLS.getCompilerOptionsDiagnostics(); expect(compilerDiags).toEqual([]); const sourceFiles = program.getSourceFiles().filter(f => !f.fileName.endsWith('.d.ts')); - // there are six .ts files in the test project - expect(sourceFiles.length).toBe(6); + // there are three .ts files in the test project + expect(sourceFiles.length).toBe(3); for (const {fileName} of sourceFiles) { const syntacticDiags = tsLS.getSyntacticDiagnostics(fileName); expect(syntacticDiags).toEqual([]); @@ -117,7 +119,7 @@ describe('plugin', () => { const marker = mockHost.getLocationMarkerFor(MY_COMPONENT, 'tree'); const completions = plugin.getCompletionsAtPosition(MY_COMPONENT, marker.start, undefined); expect(completions).toBeDefined(); - expect(completions !.entries).toEqual([ + expect(completions!.entries).toEqual([ { name: 'children', kind: CompletionKind.PROPERTY as any, diff --git a/packages/language-service/test/typescript_host_spec.ts b/packages/language-service/test/typescript_host_spec.ts index dadf9e9e7c585..a8a724aa535f4 100644 --- a/packages/language-service/test/typescript_host_spec.ts +++ b/packages/language-service/test/typescript_host_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {TypeScriptServiceHost} from '../src/typescript_host'; -import {MockTypescriptHost, findDirectiveMetadataByName} from './test_utils'; +import {findDirectiveMetadataByName, MockTypescriptHost} from './test_utils'; describe('TypeScriptServiceHost', () => { @@ -62,7 +62,7 @@ describe('TypeScriptServiceHost', () => { expect(oldModules.ngModules).toEqual([]); // Now add a script, this would change the program const fileName = '/app/main.ts'; - const content = tsLSHost.readFile(fileName) !; + const content = tsLSHost.readFile(fileName)!; tsLSHost.addScript(fileName, content); // If the caches are not cleared, we would get back an empty array. // But if the caches are cleared then the analyzed modules will be non-empty. @@ -94,7 +94,7 @@ describe('TypeScriptServiceHost', () => { const tsLS = ts.createLanguageService(tsLSHost); const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS); const templates = ngLSHost.getTemplates('/app/parsing-cases.ts'); - expect(templates.length).toBe(9); + expect(templates.length).toBe(5); }); it('should be able to find external template', () => { @@ -121,8 +121,8 @@ describe('TypeScriptServiceHost', () => { expect(oldModules.symbolsMissingModule).toEqual([]); // Expect to find AppComponent in the old modules const oldFile = oldModules.files.find(f => f.fileName === fileName); - expect(oldFile !.directives.length).toBe(1); - const appComp = oldFile !.directives[0]; + expect(oldFile!.directives.length).toBe(1); + const appComp = oldFile!.directives[0]; expect(appComp.name).toBe('AppComponent'); expect(oldModules.ngModuleByPipeOrDirective.has(appComp)).toBe(true); @@ -154,8 +154,8 @@ describe('TypeScriptServiceHost', () => { expect(newModules.symbolsMissingModule).toEqual([]); // Expect to find HelloComponent in the new modules const newFile = newModules.files.find(f => f.fileName === fileName); - expect(newFile !.directives.length).toBe(1); - const helloComp = newFile !.directives[0]; + expect(newFile!.directives.length).toBe(1); + const helloComp = newFile!.directives[0]; expect(helloComp.name).toBe('HelloComponent'); expect(newModules.ngModuleByPipeOrDirective.has(helloComp)).toBe(true); expect(newModules.ngModuleByPipeOrDirective.has(appComp)).toBe(false); diff --git a/packages/language-service/test/typescript_symbols_spec.ts b/packages/language-service/test/typescript_symbols_spec.ts index aa4064949b885..7c901f9edaea6 100644 --- a/packages/language-service/test/typescript_symbols_spec.ts +++ b/packages/language-service/test/typescript_symbols_spec.ts @@ -11,16 +11,22 @@ import {ReflectorHost} from '@angular/language-service/src/reflector_host'; import * as ts from 'typescript'; import {BuiltinType, Symbol, SymbolQuery, SymbolTable} from '../src/symbols'; -import {getSymbolQuery, toSymbolTableFactory} from '../src/typescript_symbols'; +import {getSymbolQuery} from '../src/typescript_symbols'; import {DiagnosticContext, MockLanguageServiceHost} from './mocks'; function emptyPipes(): SymbolTable { return { size: 0, - get(key: string) { return undefined; }, - has(key: string) { return false; }, - values(): Symbol[]{return [];} + get(key: string) { + return undefined; + }, + has(key: string) { + return false; + }, + values(): Symbol[] { + return []; + } }; } @@ -35,9 +41,9 @@ describe('symbol query', () => { const host = new MockLanguageServiceHost( ['/quickstart/app/app.component.ts'], QUICKSTART, '/quickstart'); const service = ts.createLanguageService(host, registry); - program = service.getProgram() !; + program = service.getProgram()!; checker = program.getTypeChecker(); - sourceFile = program.getSourceFile('/quickstart/app/app.component.ts') !; + sourceFile = program.getSourceFile('/quickstart/app/app.component.ts')!; const symbolResolverHost = new ReflectorHost(() => program, host); context = new DiagnosticContext(service, program, checker, symbolResolverHost); query = getSymbolQuery(program, checker, sourceFile, emptyPipes); @@ -67,20 +73,12 @@ describe('symbol query', () => { } else { const symbol = query.getBuiltinType(builtinType); const got: ts.TypeFlags = (symbol as any).tsType.flags; - expect(got).toBe(want !); + expect(got).toBe(want!); } } }); }); -describe('toSymbolTableFactory(tsVersion)', () => { - it('should return a Map for versions of TypeScript >= 2.2', () => { - const a = { name: 'a' } as ts.Symbol; - const b = { name: 'b' } as ts.Symbol; - expect(toSymbolTableFactory([a, b]) instanceof Map).toEqual(true); - }); -}); - function appComponentSource(template: string): string { return ` import {Component} from '@angular/core'; diff --git a/packages/language-service/test/utils_spec.ts b/packages/language-service/test/utils_spec.ts index ce50568219374..c4d2159a99df3 100644 --- a/packages/language-service/test/utils_spec.ts +++ b/packages/language-service/test/utils_spec.ts @@ -9,7 +9,8 @@ import * as ng from '@angular/compiler'; import * as ts from 'typescript'; -import {getDirectiveClassLike, getPathToNodeAtPosition} from '../src/utils'; +import {getClassDeclFromDecoratorProp, getDirectiveClassLike, getPathToNodeAtPosition} from '../src/utils'; +import {MockTypescriptHost} from './test_utils'; describe('getDirectiveClassLike', () => { it('should return a directive class', () => { @@ -28,7 +29,7 @@ describe('getDirectiveClassLike', () => { } }); expect(result).toBeTruthy(); - const {decoratorId, classId} = result !; + const {decoratorId, classId} = result!; expect(decoratorId.kind).toBe(ts.SyntaxKind.Identifier); expect(decoratorId.text).toBe('NgModule'); expect(classId.text).toBe('AppModule'); @@ -80,3 +81,42 @@ describe('getPathToNodeAtPosition', () => { expect(path.tail instanceof ng.Attribute).toBe(true); }); }); + +describe('getClassDeclFromTemplateNode', () => { + it('should find class declaration in syntax-only mode', () => { + const sourceFile = ts.createSourceFile( + 'foo.ts', ` + @Component({ + template: '<div></div>' + }) + class MyComponent {}`, + ts.ScriptTarget.ES2015, true /* setParentNodes */); + function visit(node: ts.Node): ts.ClassDeclaration|undefined { + if (ts.isPropertyAssignment(node)) { + return getClassDeclFromDecoratorProp(node); + } + return node.forEachChild(visit); + } + const classDecl = sourceFile.forEachChild(visit); + expect(classDecl).toBeTruthy(); + expect(classDecl!.kind).toBe(ts.SyntaxKind.ClassDeclaration); + expect((classDecl as ts.ClassDeclaration).name!.text).toBe('MyComponent'); + }); + + + it('should return class declaration for AppComponent', () => { + const host = new MockTypescriptHost(['/app/app.component.ts']); + const tsLS = ts.createLanguageService(host); + const sourceFile = tsLS.getProgram()!.getSourceFile('/app/app.component.ts'); + expect(sourceFile).toBeTruthy(); + const classDecl = sourceFile!.forEachChild(function visit(node): ts.Node|undefined { + if (ts.isPropertyAssignment(node)) { + return getClassDeclFromDecoratorProp(node); + } + return node.forEachChild(visit); + }); + expect(classDecl).toBeTruthy(); + expect(ts.isClassDeclaration(classDecl!)).toBe(true); + expect((classDecl as ts.ClassDeclaration).name!.text).toBe('AppComponent'); + }); +}); diff --git a/packages/localize/init/index.ts b/packages/localize/init/index.ts index 536786ba0f74e..6361e0371ef40 100644 --- a/packages/localize/init/index.ts +++ b/packages/localize/init/index.ts @@ -5,7 +5,7 @@ * 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 {$localize, LocalizeFn, _global} from '../src/localize'; +import {$localize, _global, LocalizeFn} from '../src/localize'; export {LocalizeFn, TranslateFn} from '../src/localize'; diff --git a/packages/localize/private.ts b/packages/localize/private.ts index 902c2af0a48b4..824a2e69d1279 100644 --- a/packages/localize/private.ts +++ b/packages/localize/private.ts @@ -8,4 +8,4 @@ // This file exports all the `utils` as private exports so that other parts of `@angular/localize` // can make use of them. -export {MessageId as ɵMessageId, MissingTranslationError as ɵMissingTranslationError, ParsedMessage as ɵParsedMessage, ParsedTranslation as ɵParsedTranslation, ParsedTranslations as ɵParsedTranslations, SourceMessage as ɵSourceMessage, TargetMessage as ɵTargetMessage, computeMsgId as ɵcomputeMsgId, findEndOfBlock as ɵfindEndOfBlock, isMissingTranslationError as ɵisMissingTranslationError, makeParsedTranslation as ɵmakeParsedTranslation, makeTemplateObject as ɵmakeTemplateObject, parseMessage as ɵparseMessage, parseMetadata as ɵparseMetadata, parseTranslation as ɵparseTranslation, splitBlock as ɵsplitBlock, translate as ɵtranslate} from './src/utils'; +export {computeMsgId as ɵcomputeMsgId, findEndOfBlock as ɵfindEndOfBlock, isMissingTranslationError as ɵisMissingTranslationError, makeParsedTranslation as ɵmakeParsedTranslation, makeTemplateObject as ɵmakeTemplateObject, MessageId as ɵMessageId, MissingTranslationError as ɵMissingTranslationError, ParsedMessage as ɵParsedMessage, ParsedTranslation as ɵParsedTranslation, ParsedTranslations as ɵParsedTranslations, parseMessage as ɵparseMessage, parseMetadata as ɵparseMetadata, parseTranslation as ɵparseTranslation, SourceMessage as ɵSourceMessage, splitBlock as ɵsplitBlock, TargetMessage as ɵTargetMessage, translate as ɵtranslate} from './src/utils'; diff --git a/packages/localize/schematics/ng-add/index.ts b/packages/localize/schematics/ng-add/index.ts index 903d3eddca066..94ec56b5a018a 100644 --- a/packages/localize/schematics/ng-add/index.ts +++ b/packages/localize/schematics/ng-add/index.ts @@ -9,7 +9,7 @@ */ import {virtualFs} from '@angular-devkit/core'; -import {Rule, Tree, chain} from '@angular-devkit/schematics'; +import {chain, Rule, Tree} from '@angular-devkit/schematics'; import {getWorkspace} from '@schematics/angular/utility/config'; import {getProjectTargets} from '@schematics/angular/utility/project-targets'; import {validateProjectName} from '@schematics/angular/utility/validation'; @@ -25,13 +25,12 @@ function getAllOptionValues<T>( const targets = getProjectTargets(host, projectName); // Find all targets of a specific build in a project. - const builderTargets: (BrowserBuilderTarget | ServeBuilderTarget)[] = - Object.values(targets).filter( - (target: BrowserBuilderTarget | ServeBuilderTarget) => target.builder === builderName); + const builderTargets: (BrowserBuilderTarget|ServeBuilderTarget)[] = Object.values(targets).filter( + (target: BrowserBuilderTarget|ServeBuilderTarget) => target.builder === builderName); // Get all options contained in target configuration partials. const configurationOptions = builderTargets.filter(t => t.configurations) - .map(t => Object.values(t.configurations !)) + .map(t => Object.values(t.configurations!)) .reduce((acc, cur) => acc.concat(...cur), []); // Now we have all option sets. We can use it to find all references to a given property. diff --git a/packages/localize/schematics/ng-add/index_spec.ts b/packages/localize/schematics/ng-add/index_spec.ts index 68588f8f4ef63..9d4cb6fedfd7d 100644 --- a/packages/localize/schematics/ng-add/index_spec.ts +++ b/packages/localize/schematics/ng-add/index_spec.ts @@ -13,7 +13,6 @@ import {localizePolyfill} from './index'; describe('ng-add schematic', () => { - const countInstances = (str: string, substr: string) => str.split(substr).length - 1; const defaultOptions = {name: 'demo'}; let host: UnitTestTree; @@ -112,25 +111,25 @@ export { renderModule, renderModuleFactory } from '@angular/platform-server';`; new SchematicTestRunner('@angular/localize', require.resolve('../collection.json')); }); - it('should add localize polyfill to polyfill files', async() => { + it('should add localize polyfill to polyfill files', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); expect(host.readContent('/src/polyfills.ts')).toContain(localizePolyfill); expect(host.readContent('/src/another-polyfills.ts')).toContain(localizePolyfill); }); - it('should add localize polyfill to server main files', async() => { + it('should add localize polyfill to server main files', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); expect(host.readContent('/src/main.server.ts')).toContain(localizePolyfill); expect(host.readContent('/src/another-main.server.ts')).toContain(localizePolyfill); }); - it('should add localize polyfill at the start of file', async() => { + it('should add localize polyfill at the start of file', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const content = host.readContent('/src/polyfills.ts'); expect(content.indexOf(localizePolyfill)).toBeLessThan(content.indexOf(polyfillsContent)); }); - it('should not add localize polyfill to files referenced in other targets files', async() => { + it('should not add localize polyfill to files referenced in other targets files', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); expect(host.readContent('/src/unrelated-polyfills.ts')).not.toContain(localizePolyfill); expect(host.readContent('/src/another-unrelated-polyfills.ts')).not.toContain(localizePolyfill); @@ -139,13 +138,13 @@ export { renderModule, renderModuleFactory } from '@angular/platform-server';`; .not.toContain(localizePolyfill); }); - it('should only add localize polyfill once if multiple builds reference it', async() => { + it('should only add localize polyfill once if multiple builds reference it', async () => { host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise(); const content = host.readContent('/src/polyfills.ts'); expect(countInstances(content, localizePolyfill)).toBe(1); }); - it('should not add localize polyfill if it\'s already there', async() => { + it('should not add localize polyfill if it\'s already there', async () => { const polyfillVariation = localizePolyfill.replace(/'/g, '"'); host.overwrite('/src/polyfills.ts', `${localizePolyfill}\n${polyfillsContent}`); host.overwrite('/src/another-polyfills.ts', `${polyfillVariation}\n${polyfillsContent}`); @@ -154,7 +153,7 @@ export { renderModule, renderModuleFactory } from '@angular/platform-server';`; expect(countInstances(host.readContent('/src/another-polyfills.ts'), localizePolyfill)).toBe(0); }); - it('should not break when there are no polyfills', async() => { + it('should not break when there are no polyfills', async () => { host.overwrite('angular.json', JSON.stringify({ projects: { 'demo': { diff --git a/packages/localize/src/localize/test/localize_spec.ts b/packages/localize/src/localize/test/localize_spec.ts index 53c2d039beabe..6fdf9f5a16f88 100644 --- a/packages/localize/src/localize/test/localize_spec.ts +++ b/packages/localize/src/localize/test/localize_spec.ts @@ -11,77 +11,87 @@ describe('$localize tag', () => { describe('with no `translate()` defined (the default)', () => { it('should render template literals as-is', () => { expect($localize.translate).toBeUndefined(); - expect($localize `abc`).toEqual('abc'); - expect($localize `abc${1 + 2 + 3}`).toEqual('abc6'); - expect($localize `abc${1 + 2 + 3}def`).toEqual('abc6def'); - expect($localize `abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('abc6def15'); + expect($localize`abc`).toEqual('abc'); + expect($localize`abc${1 + 2 + 3}`).toEqual('abc6'); + expect($localize`abc${1 + 2 + 3}def`).toEqual('abc6def'); + expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('abc6def15'); const getName = () => 'World'; - expect($localize `Hello, ${getName()}!`).toEqual('Hello, World!'); + expect($localize`Hello, ${getName()}!`).toEqual('Hello, World!'); }); it('should strip metadata block from message parts', () => { expect($localize.translate).toBeUndefined(); - expect($localize `:meaning|description@@custom-id:abcdef`).toEqual('abcdef'); + expect($localize`:meaning|description@@custom-id:abcdef`).toEqual('abcdef'); }); it('should ignore escaped metadata block marker', () => { expect($localize.translate).toBeUndefined(); - expect($localize `\:abc:def`).toEqual(':abc:def'); + expect($localize`\:abc:def`).toEqual(':abc:def'); }); it('should strip metadata block containing escaped block markers', () => { expect($localize.translate).toBeUndefined(); - expect($localize `:abc\:def:content`).toEqual('content'); + expect($localize`:abc\:def:content`).toEqual('content'); }); it('should strip placeholder names from message parts', () => { expect($localize.translate).toBeUndefined(); - expect($localize `abc${1 + 2 + 3}:ph1:def${4 + 5 + 6}:ph2:`).toEqual('abc6def15'); + expect($localize`abc${1 + 2 + 3}:ph1:def${4 + 5 + 6}:ph2:`).toEqual('abc6def15'); }); it('should ignore escaped placeholder name marker', () => { expect($localize.translate).toBeUndefined(); - expect($localize `abc${1 + 2 + 3}\:ph1:def${4 + 5 + 6}\:ph2:`).toEqual('abc6:ph1:def15:ph2:'); + expect($localize`abc${1 + 2 + 3}\:ph1:def${4 + 5 + 6}\:ph2:`).toEqual('abc6:ph1:def15:ph2:'); }); }); describe('with `translate()` defined as an identity', () => { - beforeEach(() => { $localize.translate = identityTranslate; }); - afterEach(() => { $localize.translate = undefined; }); + beforeEach(() => { + $localize.translate = identityTranslate; + }); + afterEach(() => { + $localize.translate = undefined; + }); it('should render template literals as-is', () => { - - expect($localize `abc`).toEqual('abc'); - expect($localize `abc${1 + 2 + 3}`).toEqual('abc6'); - expect($localize `abc${1 + 2 + 3}def`).toEqual('abc6def'); - expect($localize `abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('abc6def15'); + expect($localize`abc`).toEqual('abc'); + expect($localize`abc${1 + 2 + 3}`).toEqual('abc6'); + expect($localize`abc${1 + 2 + 3}def`).toEqual('abc6def'); + expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('abc6def15'); const getName = () => 'World'; - expect($localize `Hello, ${getName()}!`).toEqual('Hello, World!'); + expect($localize`Hello, ${getName()}!`).toEqual('Hello, World!'); }); }); describe('with `translate()` defined to upper-case messageParts', () => { - beforeEach(() => { $localize.translate = upperCaseTranslate; }); - afterEach(() => { $localize.translate = undefined; }); + beforeEach(() => { + $localize.translate = upperCaseTranslate; + }); + afterEach(() => { + $localize.translate = undefined; + }); it('should render template literals with messages upper-cased', () => { - - expect($localize `abc`).toEqual('ABC'); - expect($localize `abc${1 + 2 + 3}`).toEqual('ABC6'); - expect($localize `abc${1 + 2 + 3}def`).toEqual('ABC6DEF'); - expect($localize `abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('ABC6DEF15'); + expect($localize`abc`).toEqual('ABC'); + expect($localize`abc${1 + 2 + 3}`).toEqual('ABC6'); + expect($localize`abc${1 + 2 + 3}def`).toEqual('ABC6DEF'); + expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('ABC6DEF15'); const getName = () => 'World'; - expect($localize `Hello, ${getName()}!`).toEqual('HELLO, World!'); + expect($localize`Hello, ${getName()}!`).toEqual('HELLO, World!'); }); }); describe('with `translate()` defined to reverse expressions', () => { - beforeEach(() => { $localize.translate = reverseTranslate; }); - afterEach(() => { $localize.translate = undefined; }); + beforeEach(() => { + $localize.translate = reverseTranslate; + }); + afterEach(() => { + $localize.translate = undefined; + }); it('should render template literals with expressions reversed', () => { const getName = () => 'World'; - expect($localize `abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`) + expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`) .toEqual('abcWorlddef15 - Hello, 6!'); }); }); diff --git a/packages/localize/src/tools/src/diagnostics.ts b/packages/localize/src/tools/src/diagnostics.ts index 1be6a7cccbf01..73f2945b68bb7 100644 --- a/packages/localize/src/tools/src/diagnostics.ts +++ b/packages/localize/src/tools/src/diagnostics.ts @@ -11,13 +11,19 @@ * of the tools. */ export class Diagnostics { - readonly messages: {type: 'warning' | 'error', message: string}[] = []; - get hasErrors() { return this.messages.some(m => m.type === 'error'); } - warn(message: string) { this.messages.push({type: 'warning', message}); } - error(message: string) { this.messages.push({type: 'error', message}); } + readonly messages: {type: 'warning'|'error', message: string}[] = []; + get hasErrors() { + return this.messages.some(m => m.type === 'error'); + } + warn(message: string) { + this.messages.push({type: 'warning', message}); + } + error(message: string) { + this.messages.push({type: 'error', message}); + } formatDiagnostics(message: string): string { - const errors = this.messages !.filter(d => d.type === 'error').map(d => ' - ' + d.message); - const warnings = this.messages !.filter(d => d.type === 'warning').map(d => ' - ' + d.message); + const errors = this.messages!.filter(d => d.type === 'error').map(d => ' - ' + d.message); + const warnings = this.messages!.filter(d => d.type === 'warning').map(d => ' - ' + d.message); if (errors.length) { message += '\nERRORS:\n' + errors.join('\n'); } diff --git a/packages/localize/src/tools/src/file_utils.ts b/packages/localize/src/tools/src/file_utils.ts index 4477e7036cf03..d34f57abcc49b 100644 --- a/packages/localize/src/tools/src/file_utils.ts +++ b/packages/localize/src/tools/src/file_utils.ts @@ -9,9 +9,13 @@ import * as fs from 'fs'; import * as path from 'path'; export class FileUtils { - static readFile(absolutePath: string): string { return fs.readFileSync(absolutePath, 'utf8'); } + static readFile(absolutePath: string): string { + return fs.readFileSync(absolutePath, 'utf8'); + } - static readFileBuffer(absolutePath: string): Buffer { return fs.readFileSync(absolutePath); } + static readFileBuffer(absolutePath: string): Buffer { + return fs.readFileSync(absolutePath); + } static writeFile(absolutePath: string, contents: string|Buffer) { FileUtils.ensureDir(path.dirname(absolutePath)); @@ -25,7 +29,7 @@ export class FileUtils { absolutePath = path.dirname(absolutePath); } while (parents.length) { - fs.mkdirSync(parents.pop() !); + fs.mkdirSync(parents.pop()!); } } diff --git a/packages/localize/src/tools/src/translate/asset_files/asset_translation_handler.ts b/packages/localize/src/tools/src/translate/asset_files/asset_translation_handler.ts index ed552d0514197..ae70ba7c86c0f 100644 --- a/packages/localize/src/tools/src/translate/asset_files/asset_translation_handler.ts +++ b/packages/localize/src/tools/src/translate/asset_files/asset_translation_handler.ts @@ -14,7 +14,9 @@ import {TranslationBundle, TranslationHandler} from '../translator'; * Translate an asset file by simply copying it to the appropriate translation output paths. */ export class AssetTranslationHandler implements TranslationHandler { - canTranslate(_relativeFilePath: string, _contents: Buffer): boolean { return true; } + canTranslate(_relativeFilePath: string, _contents: Buffer): boolean { + return true; + } translate( diagnostics: Diagnostics, _sourceRoot: string, relativeFilePath: string, contents: Buffer, outputPathFn: OutputPathFn, translations: TranslationBundle[], sourceLocale?: string): void { diff --git a/packages/localize/src/tools/src/translate/main.ts b/packages/localize/src/tools/src/translate/main.ts index e811b870714ac..8ac671cee643c 100644 --- a/packages/localize/src/tools/src/translate/main.ts +++ b/packages/localize/src/tools/src/translate/main.ts @@ -88,8 +88,16 @@ if (require.main === module) { const sourceLocale: string|undefined = options['l']; const translationFileLocales: string[] = options['target-locales'] || []; - translateFiles({sourceRootPath, sourceFilePaths, translationFilePaths, translationFileLocales, - outputPathFn, diagnostics, missingTranslation, sourceLocale}); + translateFiles({ + sourceRootPath, + sourceFilePaths, + translationFilePaths, + translationFileLocales, + outputPathFn, + diagnostics, + missingTranslation, + sourceLocale + }); diagnostics.messages.forEach(m => console.warn(`${m.type}: ${m.message}`)); process.exit(diagnostics.hasErrors ? 1 : 0); @@ -135,9 +143,16 @@ export interface TranslateFilesOptions { sourceLocale?: string; } -export function translateFiles({sourceRootPath, sourceFilePaths, translationFilePaths, - translationFileLocales, outputPathFn, diagnostics, - missingTranslation, sourceLocale}: TranslateFilesOptions) { +export function translateFiles({ + sourceRootPath, + sourceFilePaths, + translationFilePaths, + translationFileLocales, + outputPathFn, + diagnostics, + missingTranslation, + sourceLocale +}: TranslateFilesOptions) { const translationLoader = new TranslationLoader( [ new Xliff2TranslationParser(), diff --git a/packages/localize/src/tools/src/translate/output_path.ts b/packages/localize/src/tools/src/translate/output_path.ts index 89f33cb0d5203..f33a65c5cecbc 100644 --- a/packages/localize/src/tools/src/translate/output_path.ts +++ b/packages/localize/src/tools/src/translate/output_path.ts @@ -7,7 +7,9 @@ */ import {join} from 'path'; -export interface OutputPathFn { (locale: string, relativePath: string): string; } +export interface OutputPathFn { + (locale: string, relativePath: string): string; +} /** * Create a function that will compute the absolute path to where a translated file should be diff --git a/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts b/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts index 51d1e44f9a928..4c8c48a63264a 100644 --- a/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts +++ b/packages/localize/src/tools/src/translate/source_files/es2015_translate_plugin.ts @@ -8,8 +8,10 @@ import {ɵParsedTranslation} from '@angular/localize'; import {NodePath, PluginObj} from '@babel/core'; import {TaggedTemplateExpression} from '@babel/types'; + import {Diagnostics} from '../../diagnostics'; -import {TranslatePluginOptions, buildCodeFrameError, buildLocalizeReplacement, isBabelParseError, isLocalize, translate, unwrapMessagePartsFromTemplateLiteral} from './source_file_utils'; + +import {buildCodeFrameError, buildLocalizeReplacement, isBabelParseError, isLocalize, translate, TranslatePluginOptions, unwrapMessagePartsFromTemplateLiteral} from './source_file_utils'; export function makeEs2015TranslatePlugin( diagnostics: Diagnostics, translations: Record<string, ɵParsedTranslation>, diff --git a/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts b/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts index 30f87cfcede55..787579f64dd60 100644 --- a/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts +++ b/packages/localize/src/tools/src/translate/source_files/es5_translate_plugin.ts @@ -8,8 +8,10 @@ import {ɵParsedTranslation} from '@angular/localize'; import {NodePath, PluginObj} from '@babel/core'; import {CallExpression} from '@babel/types'; + import {Diagnostics} from '../../diagnostics'; -import {TranslatePluginOptions, buildCodeFrameError, buildLocalizeReplacement, isBabelParseError, isLocalize, translate, unwrapMessagePartsFromLocalizeCall, unwrapSubstitutionsFromLocalizeCall} from './source_file_utils'; + +import {buildCodeFrameError, buildLocalizeReplacement, isBabelParseError, isLocalize, translate, TranslatePluginOptions, unwrapMessagePartsFromLocalizeCall, unwrapSubstitutionsFromLocalizeCall} from './source_file_utils'; export function makeEs5TranslatePlugin( diagnostics: Diagnostics, translations: Record<string, ɵParsedTranslation>, diff --git a/packages/localize/src/tools/src/translate/source_files/locale_plugin.ts b/packages/localize/src/tools/src/translate/source_files/locale_plugin.ts index aaa0237628fa1..e4a81041cb1bc 100644 --- a/packages/localize/src/tools/src/translate/source_files/locale_plugin.ts +++ b/packages/localize/src/tools/src/translate/source_files/locale_plugin.ts @@ -8,7 +8,7 @@ import {NodePath, PluginObj} from '@babel/core'; import {MemberExpression, stringLiteral} from '@babel/types'; -import {TranslatePluginOptions, isLocalize} from './source_file_utils'; +import {isLocalize, TranslatePluginOptions} from './source_file_utils'; /** * This Babel plugin will replace the following code forms with a string literal containing the diff --git a/packages/localize/src/tools/src/translate/source_files/source_file_utils.ts b/packages/localize/src/tools/src/translate/source_files/source_file_utils.ts index 04eb96ebc7b57..9a264b0e9303d 100644 --- a/packages/localize/src/tools/src/translate/source_files/source_file_utils.ts +++ b/packages/localize/src/tools/src/translate/source_files/source_file_utils.ts @@ -5,9 +5,10 @@ * 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 {ɵParsedTranslation, ɵisMissingTranslationError, ɵmakeTemplateObject, ɵtranslate} from '@angular/localize'; +import {ɵisMissingTranslationError, ɵmakeTemplateObject, ɵParsedTranslation, ɵtranslate} from '@angular/localize'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; + import {Diagnostics} from '../../diagnostics'; /** @@ -33,18 +34,18 @@ export function isNamedIdentifier( } /** -* Is the given `identifier` declared globally. -* @param identifier The identifier to check. -*/ + * Is the given `identifier` declared globally. + * @param identifier The identifier to check. + */ export function isGlobalIdentifier(identifier: NodePath<t.Identifier>) { return !identifier.scope || !identifier.scope.hasBinding(identifier.node.name); } /** -* Build a translated expression to replace the call to `$localize`. -* @param messageParts The static parts of the message. -* @param substitutions The expressions to substitute into the message. -*/ + * Build a translated expression to replace the call to `$localize`. + * @param messageParts The static parts of the message. + * @param substitutions The expressions to substitute into the message. + */ export function buildLocalizeReplacement( messageParts: TemplateStringsArray, substitutions: readonly t.Expression[]): t.Expression { let mappedString: t.Expression = t.stringLiteral(messageParts[0]); @@ -57,13 +58,13 @@ export function buildLocalizeReplacement( } /** -* Extract the message parts from the given `call` (to `$localize`). -* -* The message parts will either by the first argument to the `call` or it will be wrapped in call -* to a helper function like `__makeTemplateObject`. -* -* @param call The AST node of the call to process. -*/ + * Extract the message parts from the given `call` (to `$localize`). + * + * The message parts will either by the first argument to the `call` or it will be wrapped in call + * to a helper function like `__makeTemplateObject`. + * + * @param call The AST node of the call to process. + */ export function unwrapMessagePartsFromLocalizeCall(call: NodePath<t.CallExpression>): TemplateStringsArray { let cooked = call.get('arguments')[0]; @@ -144,7 +145,7 @@ export function unwrapMessagePartsFromLocalizeCall(call: NodePath<t.CallExpressi export function unwrapSubstitutionsFromLocalizeCall(call: t.CallExpression): t.Expression[] { const expressions = call.arguments.splice(1); if (!isArrayOfExpressions(expressions)) { - const badExpression = expressions.find(expression => !t.isExpression(expression)) !; + const badExpression = expressions.find(expression => !t.isExpression(expression))!; throw new BabelParseError( badExpression, 'Invalid substitutions for `$localize` (expected all substitution arguments to be expressions).'); @@ -166,12 +167,12 @@ export function unwrapMessagePartsFromTemplateLiteral(elements: t.TemplateElemen } /** -* Wrap the given `expression` in parentheses if it is a binary expression. -* -* This ensures that this expression is evaluated correctly if it is embedded in another expression. -* -* @param expression The expression to potentially wrap. -*/ + * Wrap the given `expression` in parentheses if it is a binary expression. + * + * This ensures that this expression is evaluated correctly if it is embedded in another expression. + * + * @param expression The expression to potentially wrap. + */ export function wrapInParensIfNecessary(expression: t.Expression): t.Expression { if (t.isBinaryExpression(expression)) { return t.parenthesizedExpression(expression); @@ -181,9 +182,9 @@ export function wrapInParensIfNecessary(expression: t.Expression): t.Expression } /** -* Extract the string values from an `array` of string literals. -* @param array The array to unwrap. -*/ + * Extract the string values from an `array` of string literals. + * @param array The array to unwrap. + */ export function unwrapStringLiteralArray(array: t.Expression): string[] { if (!isStringLiteralArray(array)) { throw new BabelParseError( @@ -280,19 +281,19 @@ function getReturnedExpression(fn: NodePath<t.FunctionDeclaration>): NodePath<t. } /** -* Is the given `node` an array of literal strings? -* -* @param node The node to test. -*/ + * Is the given `node` an array of literal strings? + * + * @param node The node to test. + */ export function isStringLiteralArray(node: t.Node): node is t.Expression& {elements: t.StringLiteral[]} { return t.isArrayExpression(node) && node.elements.every(element => t.isStringLiteral(element)); } /** -* Are all the given `nodes` expressions? -* @param nodes The nodes to test. -*/ + * Are all the given `nodes` expressions? + * @param nodes The nodes to test. + */ export function isArrayOfExpressions(nodes: t.Node[]): nodes is t.Expression[] { return nodes.every(element => t.isExpression(element)); } @@ -306,7 +307,7 @@ export interface TranslatePluginOptions { /** * How to handle missing translations. */ -export type MissingTranslationStrategy = 'error' | 'warning' | 'ignore'; +export type MissingTranslationStrategy = 'error'|'warning'|'ignore'; /** * Translate the text of the given message, using the given translations. @@ -340,7 +341,9 @@ export function translate( export class BabelParseError extends Error { private readonly type = 'BabelParseError'; - constructor(public node: t.Node, message: string) { super(message); } + constructor(public node: t.Node, message: string) { + super(message); + } } export function isBabelParseError(e: any): e is BabelParseError { diff --git a/packages/localize/src/tools/src/translate/translation_files/message_serialization/message_serializer.ts b/packages/localize/src/tools/src/translate/translation_files/message_serialization/message_serializer.ts index bea939fe4153b..f9ac0fef76b3f 100644 --- a/packages/localize/src/tools/src/translate/translation_files/message_serialization/message_serializer.ts +++ b/packages/localize/src/tools/src/translate/translation_files/message_serialization/message_serializer.ts @@ -9,7 +9,7 @@ import {Element, Expansion, ExpansionCase, Node, Text, visitAll} from '@angular/ import {BaseVisitor} from '../base_visitor'; import {TranslationParseError} from '../translation_parsers/translation_parse_error'; -import {getAttrOrThrow, getAttribute} from '../translation_parsers/translation_utils'; +import {getAttribute, getAttrOrThrow} from '../translation_parsers/translation_utils'; import {MessageRenderer} from './message_renderer'; @@ -55,7 +55,9 @@ export class MessageSerializer<T> extends BaseVisitor { } } - visitText(text: Text): void { this.renderer.text(text.value); } + visitText(text: Text): void { + this.renderer.text(text.value); + } visitExpansion(expansion: Expansion): void { this.renderer.startIcu(); @@ -109,6 +111,6 @@ export class MessageSerializer<T> extends BaseVisitor { } private isPlaceholderContainer(node: Node): boolean { - return node instanceof Element && node.name === this.config.placeholderContainer !.elementName; + return node instanceof Element && node.name === this.config.placeholderContainer!.elementName; } } diff --git a/packages/localize/src/tools/src/translate/translation_files/message_serialization/target_message_renderer.ts b/packages/localize/src/tools/src/translate/translation_files/message_serialization/target_message_renderer.ts index 449dfaf7123ac..e4f43317d14dd 100644 --- a/packages/localize/src/tools/src/translate/translation_files/message_serialization/target_message_renderer.ts +++ b/packages/localize/src/tools/src/translate/translation_files/message_serialization/target_message_renderer.ts @@ -5,7 +5,8 @@ * 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 {ɵParsedTranslation, ɵmakeParsedTranslation} from '@angular/localize'; +import {ɵmakeParsedTranslation, ɵParsedTranslation} from '@angular/localize'; + import {MessageRenderer} from './message_renderer'; /** @@ -20,11 +21,21 @@ export class TargetMessageRenderer implements MessageRenderer<ɵParsedTranslatio return ɵmakeParsedTranslation(messageParts, placeholderNames); } startRender(): void {} - endRender(): void { this.storeMessagePart(); } - text(text: string): void { this.current.text += text; } - placeholder(name: string, body: string|undefined): void { this.renderPlaceholder(name); } - startPlaceholder(name: string): void { this.renderPlaceholder(name); } - closePlaceholder(name: string): void { this.renderPlaceholder(name); } + endRender(): void { + this.storeMessagePart(); + } + text(text: string): void { + this.current.text += text; + } + placeholder(name: string, body: string|undefined): void { + this.renderPlaceholder(name); + } + startPlaceholder(name: string): void { + this.renderPlaceholder(name); + } + closePlaceholder(name: string): void { + this.renderPlaceholder(name); + } startContainer(): void {} closeContainer(): void {} startIcu(): void { @@ -35,7 +46,9 @@ export class TargetMessageRenderer implements MessageRenderer<ɵParsedTranslatio this.icuDepth--; this.text('}'); } - private normalizePlaceholderName(name: string) { return name.replace(/-/g, '_'); } + private normalizePlaceholderName(name: string) { + return name.replace(/-/g, '_'); + } private renderPlaceholder(name: string) { name = this.normalizePlaceholderName(name); if (this.icuDepth > 0) { diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_loader.ts b/packages/localize/src/tools/src/translate/translation_files/translation_loader.ts index 8c37a985faab3..a0abd83781c7f 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_loader.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_loader.ts @@ -51,14 +51,15 @@ export class TranslationLoader { const providedLocale = translationFileLocales[index]; const locale = providedLocale || parsedLocale; if (locale === undefined) { - throw new Error( - `The translation file "${filePath}" does not contain a target locale and no explicit locale was provided for this file.`); + throw new Error(`The translation file "${ + filePath}" does not contain a target locale and no explicit locale was provided for this file.`); } if (parsedLocale !== undefined && providedLocale !== undefined && parsedLocale !== providedLocale) { diagnostics.warn( - `The provided locale "${providedLocale}" does not match the target locale "${parsedLocale}" found in the translation file "${filePath}".`); + `The provided locale "${providedLocale}" does not match the target locale "${ + parsedLocale}" found in the translation file "${filePath}".`); } // If we were passed a diagnostics object then copy the messages over to it. diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts index e21299215d845..7a3ce587385b3 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts @@ -24,7 +24,9 @@ import {ParsedTranslationBundle, TranslationParser} from './translation_parser'; * ``` */ export class SimpleJsonTranslationParser implements TranslationParser { - canParse(filePath: string, _contents: string): boolean { return (extname(filePath) === '.json'); } + canParse(filePath: string, _contents: string): boolean { + return (extname(filePath) === '.json'); + } parse(_filePath: string, contents: string): ParsedTranslationBundle { const {locale: parsedLocale, translations} = JSON.parse(contents); diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_parser.ts index 14b2ab71bcf7c..7a45a32d9dbc3 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_parser.ts @@ -9,8 +9,8 @@ import {ɵMessageId, ɵParsedTranslation} from '@angular/localize/private'; import {Diagnostics} from '../../../diagnostics'; /** -* An object that holds translations that have been parsed from a translation file. -*/ + * An object that holds translations that have been parsed from a translation file. + */ export interface ParsedTranslationBundle { locale: string|undefined; translations: Record<ɵMessageId, ɵParsedTranslation>; diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts index 731bfa30b1f52..3edb796917c33 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/translation_utils.ts @@ -47,8 +47,8 @@ export function parseInnerRange(element: Element): Node[] { * @param element The element whose inner range we want to compute. */ function getInnerRange(element: Element): LexerRange { - const start = element.startSourceSpan !.end; - const end = element.endSourceSpan !.start; + const start = element.startSourceSpan!.end; + const end = element.endSourceSpan!.start; return { startPos: start.offset, startLine: start.line, diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts index 2656cc8584905..ab40ba2685500 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.ts @@ -14,7 +14,7 @@ import {MessageSerializer} from '../message_serialization/message_serializer'; import {TargetMessageRenderer} from '../message_serialization/target_message_renderer'; import {ParsedTranslationBundle, TranslationParser} from './translation_parser'; -import {XmlTranslationParserHint, addParseDiagnostic, addParseError, canParseXml, getAttribute, isNamedElement, parseInnerRange} from './translation_utils'; +import {addParseDiagnostic, addParseError, canParseXml, getAttribute, isNamedElement, parseInnerRange, XmlTranslationParserHint} from './translation_utils'; /** * A translation parser that can load XLIFF 1.2 files. @@ -74,7 +74,8 @@ export class Xliff1TranslationParser implements TranslationParser<XmlTranslation if (localesFound.size > 1) { addParseDiagnostic( diagnostics, element.sourceSpan, - `More than one locale found in translation file: ${JSON.stringify(Array.from(localesFound))}. Using "${bundle.locale}"`, + `More than one locale found in translation file: ${ + JSON.stringify(Array.from(localesFound))}. Using "${bundle.locale}"`, ParseErrorLevel.WARNING); } diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts index debeafcd1011f..d60c38535e59e 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.ts @@ -14,7 +14,7 @@ import {MessageSerializer} from '../message_serialization/message_serializer'; import {TargetMessageRenderer} from '../message_serialization/target_message_renderer'; import {ParsedTranslationBundle, TranslationParser} from './translation_parser'; -import {XmlTranslationParserHint, addParseDiagnostic, addParseError, canParseXml, getAttribute, isNamedElement, parseInnerRange} from './translation_utils'; +import {addParseDiagnostic, addParseError, canParseXml, getAttribute, isNamedElement, parseInnerRange, XmlTranslationParserHint} from './translation_utils'; /** * A translation parser that can load translations from XLIFF 2 files. diff --git a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts index c3a6388d185be..6e1e7512d5245 100644 --- a/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts +++ b/packages/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.ts @@ -15,7 +15,7 @@ import {MessageSerializer} from '../message_serialization/message_serializer'; import {TargetMessageRenderer} from '../message_serialization/target_message_renderer'; import {ParsedTranslationBundle, TranslationParser} from './translation_parser'; -import {XmlTranslationParserHint, addParseDiagnostic, addParseError, canParseXml, getAttribute, parseInnerRange} from './translation_utils'; +import {addParseDiagnostic, addParseError, canParseXml, getAttribute, parseInnerRange, XmlTranslationParserHint} from './translation_utils'; /** @@ -94,7 +94,8 @@ class XtbVisitor extends BaseVisitor { } catch (error) { if (typeof error === 'string') { bundle.diagnostics.warn( - `Could not parse message with id "${id}" - perhaps it has an unrecognised ICU format?\n` + + `Could not parse message with id "${ + id}" - perhaps it has an unrecognised ICU format?\n` + error); } else if (error.span && error.msg && error.level) { addParseDiagnostic(bundle.diagnostics, error.span, error.msg, error.level); diff --git a/packages/localize/src/tools/test/translate/integration/main_spec.ts b/packages/localize/src/tools/test/translate/integration/main_spec.ts index 6f15de78075ad..07a9835196f5c 100644 --- a/packages/localize/src/tools/test/translate/integration/main_spec.ts +++ b/packages/localize/src/tools/test/translate/integration/main_spec.ts @@ -19,7 +19,9 @@ describe('translateFiles()', () => { const testDir = resolve(tmpDir, 'translatedFiles_tests'); beforeEach(() => FileUtils.ensureDir(testDir)); - afterEach(() => { FileUtils.remove(testDir); }); + afterEach(() => { + FileUtils.remove(testDir); + }); it('should copy non-code files to the destination folders', () => { const diagnostics = new Diagnostics(); @@ -31,7 +33,8 @@ describe('translateFiles()', () => { translationFilePaths: resolveAll( __dirname + '/locales', ['messages.de.json', 'messages.es.xlf', 'messages.fr.xlf', 'messages.it.xtb']), - translationFileLocales: [], diagnostics, + translationFileLocales: [], + diagnostics, missingTranslation: 'error' }); @@ -60,11 +63,13 @@ describe('translateFiles()', () => { const outputPathFn = getOutputPathFn(resolve(testDir, '{{LOCALE}}')); translateFiles({ sourceRootPath: resolve(__dirname, 'test_files'), - sourceFilePaths: resolveAll(__dirname + '/test_files', ['test.js']), outputPathFn, + sourceFilePaths: resolveAll(__dirname + '/test_files', ['test.js']), + outputPathFn, translationFilePaths: resolveAll( __dirname + '/locales', ['messages.de.json', 'messages.es.xlf', 'messages.fr.xlf', 'messages.it.xtb']), - translationFileLocales: [], diagnostics, + translationFileLocales: [], + diagnostics, missingTranslation: 'error', }); @@ -85,11 +90,13 @@ describe('translateFiles()', () => { const outputPathFn = getOutputPathFn(resolve(testDir, '{{LOCALE}}')); translateFiles({ sourceRootPath: resolve(__dirname, 'test_files'), - sourceFilePaths: resolveAll(__dirname + '/test_files', ['test.js']), outputPathFn, + sourceFilePaths: resolveAll(__dirname + '/test_files', ['test.js']), + outputPathFn, translationFilePaths: resolveAll( __dirname + '/locales', ['messages.de.json', 'messages.es.xlf', 'messages.fr.xlf', 'messages.it.xtb']), - translationFileLocales: ['xde', undefined, 'fr'], diagnostics, + translationFileLocales: ['xde', undefined, 'fr'], + diagnostics, missingTranslation: 'error', }); @@ -97,7 +104,8 @@ describe('translateFiles()', () => { expect(diagnostics.messages).toContain({ type: 'warning', message: - `The provided locale "xde" does not match the target locale "de" found in the translation file "${resolve(__dirname, 'locales', 'messages.de.json')}".` + `The provided locale "xde" does not match the target locale "de" found in the translation file "${ + resolve(__dirname, 'locales', 'messages.de.json')}".` }); expect(FileUtils.readFile(resolve(testDir, 'xde', 'test.js'))) @@ -121,7 +129,8 @@ describe('translateFiles()', () => { translationFilePaths: resolveAll( __dirname + '/locales', ['messages.de.json', 'messages.es.xlf', 'messages.fr.xlf', 'messages.it.xtb']), - translationFileLocales: [], diagnostics, + translationFileLocales: [], + diagnostics, missingTranslation: 'error', }); diff --git a/packages/localize/src/tools/test/translate/integration/test_files/test.js b/packages/localize/src/tools/test/translate/integration/test_files/test.js index 8b3009345fac2..bc82ba72b7625 100644 --- a/packages/localize/src/tools/test/translate/integration/test_files/test.js +++ b/packages/localize/src/tools/test/translate/integration/test_files/test.js @@ -1,2 +1,2 @@ var name = 'World'; -var message = $localize `Hello, ${name}!`; \ No newline at end of file +var message = $localize`Hello, ${name}!`; \ No newline at end of file diff --git a/packages/localize/src/tools/test/translate/source_files/es2015_translate_plugin_spec.ts b/packages/localize/src/tools/test/translate/source_files/es2015_translate_plugin_spec.ts index d25e90d8307d4..783493941a850 100644 --- a/packages/localize/src/tools/test/translate/source_files/es2015_translate_plugin_spec.ts +++ b/packages/localize/src/tools/test/translate/source_files/es2015_translate_plugin_spec.ts @@ -16,24 +16,21 @@ describe('makeEs2015Plugin', () => { it('should transform `$localize` tags with binary expression', () => { const diagnostics = new Diagnostics(); const input = 'const b = 10;\n$localize`try\\n${40 + b}\\n me`;'; - const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('const b = 10;\n"try\\n" + (40 + b) + "\\n me";'); }); it('should strip meta blocks', () => { const diagnostics = new Diagnostics(); const input = 'const b = 10;\n$localize `:description:try\\n${40 + b}\\n me`;'; - const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('const b = 10;\n"try\\n" + (40 + b) + "\\n me";'); }); it('should not strip escaped meta blocks', () => { const diagnostics = new Diagnostics(); const input = 'const b = 10;\n$localize `\\:description:try\\n${40 + b}\\n me`;'; - const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('const b = 10;\n":description:try\\n" + (40 + b) + "\\n me";'); }); @@ -41,24 +38,21 @@ describe('makeEs2015Plugin', () => { it('should transform nested `$localize` tags', () => { const diagnostics = new Diagnostics(); const input = '$localize`a${1}b${$localize`x${5}y${6}z`}c`;'; - const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('"a" + 1 + "b" + ("x" + 5 + "y" + 6 + "z") + "c";'); }); it('should transform tags inside functions', () => { const diagnostics = new Diagnostics(); const input = 'function foo() { $localize`a${1}b${2}c`; }'; - const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('function foo() {\n "a" + 1 + "b" + 2 + "c";\n}'); }); it('should ignore tags with the wrong name', () => { const diagnostics = new Diagnostics(); const input = 'other`a${1}b${2}c`;'; - const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('other`a${1}b${2}c`;'); }); @@ -66,16 +60,14 @@ describe('makeEs2015Plugin', () => { const diagnostics = new Diagnostics(); const input = 'other`a${1}b${2}c`;'; const output = transformSync( - input, - {plugins: [makeEs2015TranslatePlugin(diagnostics, {}, {localizeName: 'other'})]}) !; + input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {}, {localizeName: 'other'})]})!; expect(output.code).toEqual('"a" + 1 + "b" + 2 + "c";'); }); it('should ignore tags if the identifier is not global', () => { const diagnostics = new Diagnostics(); const input = 'function foo($localize) { $localize`a${1}b${2}c`; }'; - const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('function foo($localize) {\n $localize`a${1}b${2}c`;\n}'); }); @@ -85,12 +77,12 @@ describe('makeEs2015Plugin', () => { const input = 'const b = 10;\n$localize `try\\n${40 + b}\\n me`;'; transformSync(input, { plugins: [makeEs2015TranslatePlugin(diagnostics, {}, {missingTranslation: 'error'})] - }) !; + })!; expect(diagnostics.hasErrors).toBe(true); expect(diagnostics.messages[0]).toEqual({ type: 'error', - message: - `No translation found for "${ɵcomputeMsgId('try\n{$PH}\n me')}" ("try\n{$PH}\n me").` + message: `No translation found for "${ + ɵcomputeMsgId('try\n{$PH}\n me')}" ("try\n{$PH}\n me").` }); }); @@ -99,14 +91,13 @@ describe('makeEs2015Plugin', () => { const diagnostics = new Diagnostics(); const input = 'const b = 10;\n$localize `try\\n${40 + b}\\n me`;'; transformSync(input, { - plugins: - [makeEs2015TranslatePlugin(diagnostics, {}, {missingTranslation: 'warning'})] - }) !; + plugins: [makeEs2015TranslatePlugin(diagnostics, {}, {missingTranslation: 'warning'})] + })!; expect(diagnostics.hasErrors).toBe(false); expect(diagnostics.messages[0]).toEqual({ type: 'warning', - message: - `No translation found for "${ɵcomputeMsgId('try\n{$PH}\n me')}" ("try\n{$PH}\n me").` + message: `No translation found for "${ + ɵcomputeMsgId('try\n{$PH}\n me')}" ("try\n{$PH}\n me").` }); }); @@ -115,9 +106,8 @@ describe('makeEs2015Plugin', () => { const diagnostics = new Diagnostics(); const input = 'const b = 10;\n$localize `try\\n${40 + b}\\n me`;'; transformSync(input, { - plugins: - [makeEs2015TranslatePlugin(diagnostics, {}, {missingTranslation: 'ignore'})] - }) !; + plugins: [makeEs2015TranslatePlugin(diagnostics, {}, {missingTranslation: 'ignore'})] + })!; expect(diagnostics.hasErrors).toBe(false); expect(diagnostics.messages).toEqual([]); }); @@ -139,7 +129,7 @@ describe('makeEs2015Plugin', () => { '$localize `abc${1 + 2 + 3}def${4 + 5 + 6}`;\n' + '$localize `Hello, ${getName()}!`;'; const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, translations)]}) !; + transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, translations)]})!; expect(output.code) .toEqual( '"abc";\n' + @@ -164,7 +154,7 @@ describe('makeEs2015Plugin', () => { '$localize `abc${1 + 2 + 3}def${4 + 5 + 6}`;\n' + '$localize `Hello, ${getName()}!`;'; const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, translations)]}) !; + transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, translations)]})!; expect(output.code) .toEqual( '"ABC";\n' + @@ -182,7 +172,7 @@ describe('makeEs2015Plugin', () => { }; const input = '$localize `abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`;'; const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, translations)]}) !; + transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, translations)]})!; expect(output.code) .toEqual('"abc" + getName() + "def" + (4 + 5 + 6) + " - Hello, " + (1 + 2 + 3) + "!";'); }); @@ -195,7 +185,7 @@ describe('makeEs2015Plugin', () => { }; const input = '$localize `abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`;'; const output = - transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, translations)]}) !; + transformSync(input, {plugins: [makeEs2015TranslatePlugin(diagnostics, translations)]})!; expect(output.code).toEqual('"abc" + (1 + 2 + 3) + " - Hello, " + getName() + "!";'); }); }); diff --git a/packages/localize/src/tools/test/translate/source_files/es5_translate_plugin_spec.ts b/packages/localize/src/tools/test/translate/source_files/es5_translate_plugin_spec.ts index eb3db70de7868..a47dff362ed4d 100644 --- a/packages/localize/src/tools/test/translate/source_files/es5_translate_plugin_spec.ts +++ b/packages/localize/src/tools/test/translate/source_files/es5_translate_plugin_spec.ts @@ -16,7 +16,7 @@ describe('makeEs5Plugin', () => { it('should transform `$localize` calls with binary expression', () => { const diagnostics = new Diagnostics(); const input = 'const b = 10;\n$localize(["try\\n", "\\n me"], 40 + b);'; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('const b = 10;\n"try\\n" + (40 + b) + "\\n me";'); }); @@ -24,7 +24,7 @@ describe('makeEs5Plugin', () => { const diagnostics = new Diagnostics(); const input = 'const b = 10;\n$localize([":description:try\\n", ":placeholder:\\n me"], 40 + b);'; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('const b = 10;\n"try\\n" + (40 + b) + "\\n me";'); }); @@ -32,28 +32,28 @@ describe('makeEs5Plugin', () => { const diagnostics = new Diagnostics(); const input = `$localize(__makeTemplateObject([':desc:try', 'me'], ['\\\\\\:desc:try', 'me']), 40 + 2);`; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('":desc:try" + (40 + 2) + "me";'); }); it('should transform nested `$localize` calls', () => { const diagnostics = new Diagnostics(); const input = '$localize(["a", "b", "c"], 1, $localize(["x", "y", "z"], 5, 6));'; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('"a" + 1 + "b" + ("x" + 5 + "y" + 6 + "z") + "c";'); }); it('should transform calls inside functions', () => { const diagnostics = new Diagnostics(); const input = 'function foo() { $localize(["a", "b", "c"], 1, 2); }'; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('function foo() {\n "a" + 1 + "b" + 2 + "c";\n}'); }); it('should ignore tags with the wrong name', () => { const diagnostics = new Diagnostics(); const input = 'other(["a", "b", "c"], 1, 2);'; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('other(["a", "b", "c"], 1, 2);'); }); @@ -61,14 +61,14 @@ describe('makeEs5Plugin', () => { const diagnostics = new Diagnostics(); const input = 'other(["a", "b", "c"], 1, 2);'; const output = transformSync( - input, {plugins: [makeEs5TranslatePlugin(diagnostics, {}, {localizeName: 'other'})]}) !; + input, {plugins: [makeEs5TranslatePlugin(diagnostics, {}, {localizeName: 'other'})]})!; expect(output.code).toEqual('"a" + 1 + "b" + 2 + "c";'); }); it('should ignore tags if the identifier is not global', () => { const diagnostics = new Diagnostics(); const input = 'function foo($localize) { $localize(["a", "b", "c"], 1, 2); }'; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code) .toEqual('function foo($localize) {\n $localize(["a", "b", "c"], 1, 2);\n}'); }); @@ -76,14 +76,14 @@ describe('makeEs5Plugin', () => { it('should handle template object helper calls', () => { const diagnostics = new Diagnostics(); const input = `$localize(__makeTemplateObject(['try', 'me'], ['try', 'me']), 40 + 2);`; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('"try" + (40 + 2) + "me";'); }); it('should handle template object aliased helper calls', () => { const diagnostics = new Diagnostics(); const input = `$localize(m(['try', 'me'], ['try', 'me']), 40 + 2);`; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('"try" + (40 + 2) + "me";'); }); @@ -91,7 +91,7 @@ describe('makeEs5Plugin', () => { const diagnostics = new Diagnostics(); const input = `$localize((this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})(['try', 'me'], ['try', 'me']), 40 + 2);`; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('"try" + (40 + 2) + "me";'); }); @@ -99,7 +99,7 @@ describe('makeEs5Plugin', () => { const diagnostics = new Diagnostics(); const input = `$localize(cachedObj||(cachedObj=__makeTemplateObject(['try', 'me'],['try', 'me'])),40 + 2)`; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('"try" + (40 + 2) + "me";'); }); @@ -115,7 +115,7 @@ describe('makeEs5Plugin', () => { cookedParts.raw=rawParts, cachedObj=cookedParts ),40 + 2)`; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toEqual('"try" + (40 + 2) + "me";'); }); @@ -139,7 +139,7 @@ describe('makeEs5Plugin', () => { console.log($localize(_templateObject(), '\ufffd0\ufffd')); } `; - const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]}) !; + const output = transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, {})]})!; expect(output.code).toContain('const message = ":escaped-colons:Welcome to the i18n app."'); expect(output.code).toContain('console.log(" Hello " + \'\ufffd0\ufffd\' + "! ");'); expect(output.code).not.toContain('templateObject'); @@ -290,8 +290,8 @@ describe('makeEs5Plugin', () => { expect(diagnostics.hasErrors).toBe(true); expect(diagnostics.messages[0]).toEqual({ type: 'error', - message: - `No translation found for "${ɵcomputeMsgId('try\n{$PH}\n me')}" ("try\n{$PH}\n me").` + message: `No translation found for "${ + ɵcomputeMsgId('try\n{$PH}\n me')}" ("try\n{$PH}\n me").` }); }); @@ -305,8 +305,8 @@ describe('makeEs5Plugin', () => { expect(diagnostics.hasErrors).toBe(false); expect(diagnostics.messages[0]).toEqual({ type: 'warning', - message: - `No translation found for "${ɵcomputeMsgId('try\n{$PH}\n me')}" ("try\n{$PH}\n me").` + message: `No translation found for "${ + ɵcomputeMsgId('try\n{$PH}\n me')}" ("try\n{$PH}\n me").` }); }); @@ -338,7 +338,7 @@ describe('(with translations)', () => { '$localize(["abc", "def", ""], 1 + 2 + 3, 4 + 5 + 6);\n' + '$localize(["Hello, ", "!"], getName());'; const output = - transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, translations)]}) !; + transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, translations)]})!; expect(output.code) .toEqual( '"abc";\n' + @@ -363,7 +363,7 @@ describe('(with translations)', () => { '$localize(["abc", "def", ""], 1 + 2 + 3, 4 + 5 + 6);\n' + '$localize(["Hello, ", "!"], getName());'; const output = - transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, translations)]}) !; + transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, translations)]})!; expect(output.code) .toEqual( '"ABC";\n' + @@ -381,7 +381,7 @@ describe('(with translations)', () => { }; const input = '$localize(["abc", "def", " - Hello, ", "!"], 1 + 2 + 3, 4 + 5 + 6, getName());'; const output = - transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, translations)]}) !; + transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, translations)]})!; expect(output.code) .toEqual('"abc" + getName() + "def" + (4 + 5 + 6) + " - Hello, " + (1 + 2 + 3) + "!";'); }); @@ -394,7 +394,7 @@ describe('(with translations)', () => { }; const input = '$localize(["abc", "def", " - Hello, ", "!"], 1 + 2 + 3, 4 + 5 + 6, getName());'; const output = - transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, translations)]}) !; + transformSync(input, {plugins: [makeEs5TranslatePlugin(diagnostics, translations)]})!; expect(output.code).toEqual('"abc" + (1 + 2 + 3) + " - Hello, " + getName() + "!";'); }); }); diff --git a/packages/localize/src/tools/test/translate/source_files/locale_plugin_spec.ts b/packages/localize/src/tools/test/translate/source_files/locale_plugin_spec.ts index b1033c4191048..637c10a05cf3e 100644 --- a/packages/localize/src/tools/test/translate/source_files/locale_plugin_spec.ts +++ b/packages/localize/src/tools/test/translate/source_files/locale_plugin_spec.ts @@ -11,88 +11,88 @@ import {makeLocalePlugin} from '../../../src/translate/source_files/locale_plugi describe('makeLocalePlugin', () => { it('should replace $localize.locale with the locale string', () => { const input = '$localize.locale;'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('"fr";'); }); it('should replace $localize.locale with the locale string in the context of a variable assignment', () => { const input = 'const a = $localize.locale;'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('const a = "fr";'); }); it('should replace $localize.locale with the locale string in the context of a binary expression', () => { const input = '$localize.locale || "default";'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('"fr" || "default";'); }); it('should remove reference to `$localize` if used to guard the locale', () => { const input = 'typeof $localize !== "undefined" && $localize.locale;'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('"fr";'); }); it('should remove reference to `$localize` if used in a longer logical expression to guard the locale', () => { const input1 = 'x || y && typeof $localize !== "undefined" && $localize.locale;'; - const output1 = transformSync(input1, {plugins: [makeLocalePlugin('fr')]}) !; + const output1 = transformSync(input1, {plugins: [makeLocalePlugin('fr')]})!; expect(output1.code).toEqual('x || y && "fr";'); const input2 = 'x || y && "undefined" !== typeof $localize && $localize.locale;'; - const output2 = transformSync(input2, {plugins: [makeLocalePlugin('fr')]}) !; + const output2 = transformSync(input2, {plugins: [makeLocalePlugin('fr')]})!; expect(output2.code).toEqual('x || y && "fr";'); const input3 = 'x || y && typeof $localize != "undefined" && $localize.locale;'; - const output3 = transformSync(input3, {plugins: [makeLocalePlugin('fr')]}) !; + const output3 = transformSync(input3, {plugins: [makeLocalePlugin('fr')]})!; expect(output3.code).toEqual('x || y && "fr";'); const input4 = 'x || y && "undefined" != typeof $localize && $localize.locale;'; - const output4 = transformSync(input4, {plugins: [makeLocalePlugin('fr')]}) !; + const output4 = transformSync(input4, {plugins: [makeLocalePlugin('fr')]})!; expect(output4.code).toEqual('x || y && "fr";'); }); it('should ignore properties on $localize other than `locale`', () => { const input = '$localize.notLocale;'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('$localize.notLocale;'); }); it('should ignore indexed property on $localize', () => { const input = '$localize["locale"];'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('$localize["locale"];'); }); it('should ignore `locale` on objects other than $localize', () => { const input = '$notLocalize.locale;'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('$notLocalize.locale;'); }); it('should ignore `$localize.locale` if `$localize` is not global', () => { const input = 'const $localize = {};\n$localize.locale;'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('const $localize = {};\n$localize.locale;'); }); it('should ignore `locale` if it is not directly accessed from `$localize`', () => { const input = 'const {locale} = $localize;\nconst a = locale;'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('const {\n locale\n} = $localize;\nconst a = locale;'); }); it('should ignore `$localize.locale` on LHS of an assignment', () => { const input = 'let a;\na = $localize.locale = "de";'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('let a;\na = $localize.locale = "de";'); }); it('should handle `$localize.locale on RHS of an assignment', () => { const input = 'let a;\na = $localize.locale;'; - const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]}) !; + const output = transformSync(input, {plugins: [makeLocalePlugin('fr')]})!; expect(output.code).toEqual('let a;\na = "fr";'); }); }); diff --git a/packages/localize/src/tools/test/translate/source_files/source_file_translation_handler_spec.ts b/packages/localize/src/tools/test/translate/source_files/source_file_translation_handler_spec.ts index 97749abd77653..ef56567330ddd 100644 --- a/packages/localize/src/tools/test/translate/source_files/source_file_translation_handler_spec.ts +++ b/packages/localize/src/tools/test/translate/source_files/source_file_translation_handler_spec.ts @@ -20,7 +20,9 @@ describe('SourceFileTranslationHandler', () => { }); describe('translate()', () => { - beforeEach(() => { spyOn(FileUtils, 'writeFile'); }); + beforeEach(() => { + spyOn(FileUtils, 'writeFile'); + }); it('should copy files for each translation locale if they contain no reference to `$localize`', () => { diff --git a/packages/localize/src/tools/test/translate/source_files/source_file_utils_spec.ts b/packages/localize/src/tools/test/translate/source_files/source_file_utils_spec.ts index 38b8c5f18a110..b6c777a2fcfd8 100644 --- a/packages/localize/src/tools/test/translate/source_files/source_file_utils_spec.ts +++ b/packages/localize/src/tools/test/translate/source_files/source_file_utils_spec.ts @@ -114,7 +114,7 @@ describe('utils', () => { describe('unwrapSubstitutionsFromLocalizeCall', () => { it('should return the substitutions from a direct call to a tag function', () => { - const ast = template.ast `$localize(['a', 'b\t', 'c'], 1, 2)` as ExpressionStatement; + const ast = template.ast`$localize(['a', 'b\t', 'c'], 1, 2)` as ExpressionStatement; const call = ast.expression as CallExpression; const substitutions = unwrapSubstitutionsFromLocalizeCall(call); expect(substitutions.map(s => (s as NumericLiteral).value)).toEqual([1, 2]); @@ -140,13 +140,13 @@ describe('utils', () => { describe('wrapInParensIfNecessary', () => { it('should wrap the expression in parentheses if it is binary', () => { - const ast = template.ast `a + b` as ExpressionStatement; + const ast = template.ast`a + b` as ExpressionStatement; const wrapped = wrapInParensIfNecessary(ast.expression); expect(isParenthesizedExpression(wrapped)).toBe(true); }); it('should return the expression untouched if it is not binary', () => { - const ast = template.ast `a` as ExpressionStatement; + const ast = template.ast`a` as ExpressionStatement; const wrapped = wrapInParensIfNecessary(ast.expression); expect(isParenthesizedExpression(wrapped)).toBe(false); }); @@ -154,12 +154,12 @@ describe('utils', () => { describe('unwrapStringLiteralArray', () => { it('should return an array of string from an array expression', () => { - const ast = template.ast `['a', 'b', 'c']` as ExpressionStatement; + const ast = template.ast`['a', 'b', 'c']` as ExpressionStatement; expect(unwrapStringLiteralArray(ast.expression)).toEqual(['a', 'b', 'c']); }); it('should throw an error if any elements of the array are not literal strings', () => { - const ast = template.ast `['a', 2, 'c']` as ExpressionStatement; + const ast = template.ast`['a', 2, 'c']` as ExpressionStatement; expect(() => unwrapStringLiteralArray(ast.expression)) .toThrowError('Unexpected messageParts for `$localize` (expected an array of strings).'); }); @@ -167,29 +167,29 @@ describe('utils', () => { describe('isStringLiteralArray()', () => { it('should return true if the ast is an array of strings', () => { - const ast = template.ast `['a', 'b', 'c']` as ExpressionStatement; + const ast = template.ast`['a', 'b', 'c']` as ExpressionStatement; expect(isStringLiteralArray(ast.expression)).toBe(true); }); it('should return false if the ast is not an array', () => { - const ast = template.ast `'a'` as ExpressionStatement; + const ast = template.ast`'a'` as ExpressionStatement; expect(isStringLiteralArray(ast.expression)).toBe(false); }); it('should return false if at least on of the array elements is not a string', () => { - const ast = template.ast `['a', 1, 'b']` as ExpressionStatement; + const ast = template.ast`['a', 1, 'b']` as ExpressionStatement; expect(isStringLiteralArray(ast.expression)).toBe(false); }); }); describe('isArrayOfExpressions()', () => { it('should return true if all the nodes are expressions', () => { - const ast = template.ast `function foo(a, b, c) {}` as FunctionDeclaration; + const ast = template.ast`function foo(a, b, c) {}` as FunctionDeclaration; expect(isArrayOfExpressions(ast.params)).toBe(true); }); it('should return false if any of the nodes is not an expression', () => { - const ast = template.ast `function foo(a, b, ...c) {}` as FunctionDeclaration; + const ast = template.ast`function foo(a, b, ...c) {}` as FunctionDeclaration; expect(isArrayOfExpressions(ast.params)).toBe(false); }); }); @@ -203,13 +203,25 @@ function getTaggedTemplate(code: string): NodePath<TaggedTemplateExpression> { function collectExpressionsPlugin() { const expressions: NodePath<Expression>[] = []; - const visitor = {Expression: (path: NodePath<Expression>) => { expressions.push(path); }}; + const visitor = { + Expression: (path: NodePath<Expression>) => { + expressions.push(path); + } + }; return {expressions, plugin: {visitor}}; } function getLocalizeCall(code: string): NodePath<CallExpression> { let callPaths: NodePath<CallExpression>[] = []; - transformSync(code, {plugins: [{visitor: {CallExpression(path) { callPaths.push(path); }}}]}); + transformSync(code, { + plugins: [{ + visitor: { + CallExpression(path) { + callPaths.push(path); + } + } + }] + }); const localizeCall = callPaths.find(p => { const callee = p.get('callee'); return (callee.isIdentifier() && callee.node.name === '$localize'); diff --git a/packages/localize/src/tools/test/translate/translation_files/translation_loader_spec.ts b/packages/localize/src/tools/test/translate/translation_files/translation_loader_spec.ts index 0d9995d55747b..48bd2be07b522 100644 --- a/packages/localize/src/tools/test/translate/translation_files/translation_loader_spec.ts +++ b/packages/localize/src/tools/test/translate/translation_files/translation_loader_spec.ts @@ -84,11 +84,14 @@ describe('TranslationLoader', () => { loader.loadBundles( ['/src/locale/messages.en.xlf', '/src/locale/messages.fr.xlf'], [undefined, 'FR']); expect(diagnostics.messages.length).toEqual(1); - expect(diagnostics.messages).toContain({ - type: 'warning', - message: - `The provided locale "FR" does not match the target locale "pl" found in the translation file "/src/locale/messages.fr.xlf".`, - }, ); + expect(diagnostics.messages) + .toContain( + { + type: 'warning', + message: + `The provided locale "FR" does not match the target locale "pl" found in the translation file "/src/locale/messages.fr.xlf".`, + }, + ); }); it('should throw an error if there is no provided nor parsed target locale', () => { diff --git a/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff2_translation_parser_spec.ts b/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff2_translation_parser_spec.ts index 869149b1c9017..438106dd4c556 100644 --- a/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff2_translation_parser_spec.ts +++ b/packages/localize/src/tools/test/translate/translation_files/translation_parsers/xliff2_translation_parser_spec.ts @@ -421,7 +421,6 @@ describe('Xliff2TranslationParser', () => { .toEqual(ɵmakeParsedTranslation( ['', ' tnemele elbatalsnart ', 'sredlohecalp htiw', ''], ['INTERPOLATION', 'START_BOLD_TEXT', 'CLOSE_BOLD_TEXT'])); - }); describe('[structure errors]', () => { @@ -969,7 +968,6 @@ describe('Xliff2TranslationParser', () => { .toEqual(ɵmakeParsedTranslation( ['', ' tnemele elbatalsnart ', 'sredlohecalp htiw', ''], ['INTERPOLATION', 'START_BOLD_TEXT', 'CLOSE_BOLD_TEXT'])); - }); describe('[structure errors]', () => { diff --git a/packages/localize/src/tools/test/translate/translator_spec.ts b/packages/localize/src/tools/test/translate/translator_spec.ts index 9b1191189c4c2..17acdeba766da 100644 --- a/packages/localize/src/tools/test/translate/translator_spec.ts +++ b/packages/localize/src/tools/test/translate/translator_spec.ts @@ -12,7 +12,6 @@ import {TranslationBundle, TranslationHandler, Translator} from '../../src/trans describe('Translator', () => { describe('translateFiles()', () => { - beforeEach(() => { spyOn(FileUtils, 'readFileBuffer') .and.returnValues(Buffer.from('resource file 1'), Buffer.from('resource file 2')); diff --git a/packages/localize/src/translate.ts b/packages/localize/src/translate.ts index b1f2792063aed..aa045aa378e5f 100644 --- a/packages/localize/src/translate.ts +++ b/packages/localize/src/translate.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {LocalizeFn} from './localize'; -import {MessageId, ParsedTranslation, TargetMessage, parseTranslation, translate as _translate} from './utils'; +import {MessageId, ParsedTranslation, parseTranslation, TargetMessage, translate as _translate} from './utils'; /** * We augment the `$localize` object to also store the translations. diff --git a/packages/localize/src/utils/src/messages.ts b/packages/localize/src/utils/src/messages.ts index a781266e1016d..827c5c242ed98 100644 --- a/packages/localize/src/utils/src/messages.ts +++ b/packages/localize/src/utils/src/messages.ts @@ -131,7 +131,8 @@ export function parseMessage( messageString, meaning: metadata.meaning || '', description: metadata.description || '', - messageParts: cleanedMessageParts, placeholderNames, + messageParts: cleanedMessageParts, + placeholderNames, }; } @@ -176,7 +177,7 @@ export function parseMetadata(cooked: string, raw: string): MessageMetadata { } else { const [meaningDescAndId, ...legacyIds] = block.split(LEGACY_ID_INDICATOR); const [meaningAndDesc, id] = meaningDescAndId.split(ID_SEPARATOR, 2); - let [meaning, description]: (string | undefined)[] = meaningAndDesc.split(MEANING_SEPARATOR, 2); + let [meaning, description]: (string|undefined)[] = meaningAndDesc.split(MEANING_SEPARATOR, 2); if (description === undefined) { description = meaning; meaning = undefined; @@ -236,9 +237,9 @@ function computePlaceholderName(index: number) { */ export function findEndOfBlock(cooked: string, raw: string): number { /************************************************************************************************ - * This function is repeated in `src/localize/src/localize.ts` and the two should be kept in sync. - * (See that file for more explanation of why.) - ************************************************************************************************/ + * This function is repeated in `src/localize/src/localize.ts` and the two should be kept in sync. + * (See that file for more explanation of why.) + ************************************************************************************************/ for (let cookedIndex = 1, rawIndex = 1; cookedIndex < cooked.length; cookedIndex++, rawIndex++) { if (raw[rawIndex] === '\\') { rawIndex++; diff --git a/packages/localize/src/utils/src/translations.ts b/packages/localize/src/utils/src/translations.ts index 4b5c51e3464b4..3376ff609b419 100644 --- a/packages/localize/src/utils/src/translations.ts +++ b/packages/localize/src/utils/src/translations.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {BLOCK_MARKER} from './constants'; -import {MessageId, ParsedMessage, TargetMessage, parseMessage} from './messages'; +import {MessageId, ParsedMessage, parseMessage, TargetMessage} from './messages'; /** @@ -68,8 +68,10 @@ export function translate( return message.substitutions[placeholder]; } else { throw new Error( - `There is a placeholder name mismatch with the translation provided for the message ${describeMessage(message)}.\n` + - `The translation contains a placeholder with name ${placeholder}, which does not exist in the message.`); + `There is a placeholder name mismatch with the translation provided for the message ${ + describeMessage(message)}.\n` + + `The translation contains a placeholder with name ${ + placeholder}, which does not exist in the message.`); } }) ]; @@ -121,5 +123,7 @@ export function makeTemplateObject(cooked: string[], raw: string[]): TemplateStr function describeMessage(message: ParsedMessage): string { const meaningString = message.meaning && ` - "${message.meaning}"`; - return `"${message.messageId}" ("${message.messageString}"${meaningString})`; + const legacy = + message.legacyIds.length > 0 ? ` [${message.legacyIds.map(l => `"${l}"`).join(', ')}]` : ''; + return `"${message.messageId}"${legacy} ("${message.messageString}"${meaningString})`; } \ No newline at end of file diff --git a/packages/localize/src/utils/test/messages_spec.ts b/packages/localize/src/utils/test/messages_spec.ts index 49f6552b07db7..40a55ecc88209 100644 --- a/packages/localize/src/utils/test/messages_spec.ts +++ b/packages/localize/src/utils/test/messages_spec.ts @@ -119,12 +119,12 @@ describe('messages utils', () => { const message = parseMessage(makeTemplateObject(['a', 'b', 'c'], ['a', 'b', 'c']), [1, 2]); expect(message.substitutions).toEqual({PH: 1, PH_1: 2}); }); - }); describe('splitBlock()', () => { - it('should return just the text if there is no block', - () => { expect(splitBlock('abc def', 'abc def')).toEqual({text: 'abc def'}); }); + it('should return just the text if there is no block', () => { + expect(splitBlock('abc def', 'abc def')).toEqual({text: 'abc def'}); + }); it('should return just the text and block if there is one', () => { expect(splitBlock(':block info:abc def', ':block info:abc def')) diff --git a/packages/localize/src/utils/test/translations_spec.ts b/packages/localize/src/utils/test/translations_spec.ts index 267320534d01e..3e4165a4250cf 100644 --- a/packages/localize/src/utils/test/translations_spec.ts +++ b/packages/localize/src/utils/test/translations_spec.ts @@ -5,7 +5,7 @@ * 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 {ParsedTranslation, TargetMessage, computeMsgId, makeTemplateObject, parseTranslation, translate} from '..'; +import {computeMsgId, makeTemplateObject, ParsedTranslation, parseTranslation, TargetMessage, translate} from '..'; describe('utils', () => { describe('makeTemplateObject', () => { @@ -80,9 +80,16 @@ describe('utils', () => { describe('translate', () => { it('should throw an error if there is no matching translation', () => { - expect(() => doTranslate({}, parts `abc`)) + expect(() => doTranslate({}, parts`abc`)) .toThrowError('No translation found for "2674653928643152084" ("abc").'); - expect(() => doTranslate({}, parts `:meaning|:abc`)) + expect(() => doTranslate({}, parts`:@@customId:abc`)) + .toThrowError('No translation found for "customId" ("abc").'); + expect( + () => doTranslate( + {}, parts`:␟d42e3c2d3aa340581d42f53c46eb49ecb3d3beb4␟3896949568605777881:abc`)) + .toThrowError( + 'No translation found for "2674653928643152084" ["d42e3c2d3aa340581d42f53c46eb49ecb3d3beb4", "3896949568605777881"] ("abc").'); + expect(() => doTranslate({}, parts`:meaning|:abc`)) .toThrowError('No translation found for "1071947593002928768" ("abc" - "meaning").'); }); @@ -90,7 +97,7 @@ describe('utils', () => { () => { expect( () => doTranslate( - {'abc{$INTERPOLATION}def': 'a{$PH}bc'}, parts `abc${1 + 2}:INTERPOLATION:def`)) + {'abc{$INTERPOLATION}def': 'a{$PH}bc'}, parts`abc${1 + 2}:INTERPOLATION:def`)) .toThrowError( `There is a placeholder name mismatch with the translation provided for the message "8986527425650846693" ("abc{$INTERPOLATION}def").\n` + `The translation contains a placeholder with name PH, which does not exist in the message.`); @@ -104,15 +111,15 @@ describe('utils', () => { 'abc{$PH}def{$PH_1}': 'abc{$PH}def{$PH_1}', 'Hello, {$PH}!': 'Hello, {$PH}!', }; - expect(doTranslate(translations, parts `abc`)).toEqual(parts `abc`); - expect(doTranslate(translations, parts `abc${1 + 2 + 3}`)).toEqual(parts `abc${1 + 2 + 3}`); - expect(doTranslate(translations, parts `abc${1 + 2 + 3}def`)) - .toEqual(parts `abc${1 + 2 + 3}def`); - expect(doTranslate(translations, parts `abc${1 + 2 + 3}def${4 + 5 + 6}`)) - .toEqual(parts `abc${1 + 2 + 3}def${4 + 5 + 6}`); + expect(doTranslate(translations, parts`abc`)).toEqual(parts`abc`); + expect(doTranslate(translations, parts`abc${1 + 2 + 3}`)).toEqual(parts`abc${1 + 2 + 3}`); + expect(doTranslate(translations, parts`abc${1 + 2 + 3}def`)) + .toEqual(parts`abc${1 + 2 + 3}def`); + expect(doTranslate(translations, parts`abc${1 + 2 + 3}def${4 + 5 + 6}`)) + .toEqual(parts`abc${1 + 2 + 3}def${4 + 5 + 6}`); const getName = () => 'World'; - expect(doTranslate(translations, parts `Hello, ${getName()}!`)) - .toEqual(parts `Hello, ${'World'}!`); + expect(doTranslate(translations, parts`Hello, ${getName()}!`)) + .toEqual(parts`Hello, ${'World'}!`); }); it('(with upper-casing translations) should render template literals with messages upper-cased', @@ -124,16 +131,15 @@ describe('utils', () => { 'abc{$PH}def{$PH_1}': 'ABC{$PH}DEF{$PH_1}', 'Hello, {$PH}!': 'HELLO, {$PH}!', }; - expect(doTranslate(translations, parts `abc`)).toEqual(parts `ABC`); - expect(doTranslate(translations, parts `abc${1 + 2 + 3}`)) - .toEqual(parts `ABC${1 + 2 + 3}`); - expect(doTranslate(translations, parts `abc${1 + 2 + 3}def`)) - .toEqual(parts `ABC${1 + 2 + 3}DEF`); - expect(doTranslate(translations, parts `abc${1 + 2 + 3}def${4 + 5 + 6}`)) - .toEqual(parts `ABC${1 + 2 + 3}DEF${4 + 5 + 6}`); + expect(doTranslate(translations, parts`abc`)).toEqual(parts`ABC`); + expect(doTranslate(translations, parts`abc${1 + 2 + 3}`)).toEqual(parts`ABC${1 + 2 + 3}`); + expect(doTranslate(translations, parts`abc${1 + 2 + 3}def`)) + .toEqual(parts`ABC${1 + 2 + 3}DEF`); + expect(doTranslate(translations, parts`abc${1 + 2 + 3}def${4 + 5 + 6}`)) + .toEqual(parts`ABC${1 + 2 + 3}DEF${4 + 5 + 6}`); const getName = () => 'World'; - expect(doTranslate(translations, parts `Hello, ${getName()}!`)) - .toEqual(parts `HELLO, ${'World'}!`); + expect(doTranslate(translations, parts`Hello, ${getName()}!`)) + .toEqual(parts`HELLO, ${'World'}!`); }); it('(with translations to reverse expressions) should render template literals with expressions reversed', @@ -143,8 +149,8 @@ describe('utils', () => { }; const getName = () => 'World'; expect(doTranslate( - translations, parts `abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`)) - .toEqual(parts `abc${'World'}def${4 + 5 + 6} - Hello, ${1 + 2 + 3}!`); + translations, parts`abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`)) + .toEqual(parts`abc${'World'}def${4 + 5 + 6} - Hello, ${1 + 2 + 3}!`); }); it('(with translations to remove expressions) should render template literals with expressions removed', @@ -154,8 +160,8 @@ describe('utils', () => { }; const getName = () => 'World'; expect(doTranslate( - translations, parts `abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`)) - .toEqual(parts `abc${1 + 2 + 3} - Hello, ${'World'}!`); + translations, parts`abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`)) + .toEqual(parts`abc${1 + 2 + 3} - Hello, ${'World'}!`); }); function parts(messageParts: TemplateStringsArray, ...substitutions: any[]): @@ -167,7 +173,6 @@ describe('utils', () => { Record<string, ParsedTranslation> { const parsedTranslations: Record<string, ParsedTranslation> = {}; Object.keys(translations).forEach(key => { - parsedTranslations[computeMsgId(key, '')] = parseTranslation(translations[key]); }); return parsedTranslations; diff --git a/packages/localize/test/translate_spec.ts b/packages/localize/test/translate_spec.ts index 246978b4dda8b..9e3a98fb1d932 100644 --- a/packages/localize/test/translate_spec.ts +++ b/packages/localize/test/translate_spec.ts @@ -9,7 +9,7 @@ import '@angular/localize/init'; import {clearTranslations, loadTranslations} from '../localize'; -import {MessageId, TargetMessage, computeMsgId} from '../src/utils'; +import {computeMsgId, MessageId, TargetMessage} from '../src/utils'; describe('$localize tag with translations', () => { describe('identities', () => { @@ -22,15 +22,17 @@ describe('$localize tag with translations', () => { 'Hello, {$PH}!': 'Hello, {$PH}!', })); }); - afterEach(() => { clearTranslations(); }); + afterEach(() => { + clearTranslations(); + }); it('should render template literals as-is', () => { - expect($localize `abc`).toEqual('abc'); - expect($localize `abc${1 + 2 + 3}`).toEqual('abc6'); - expect($localize `abc${1 + 2 + 3}def`).toEqual('abc6def'); - expect($localize `abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('abc6def15'); + expect($localize`abc`).toEqual('abc'); + expect($localize`abc${1 + 2 + 3}`).toEqual('abc6'); + expect($localize`abc${1 + 2 + 3}def`).toEqual('abc6def'); + expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('abc6def15'); const getName = () => 'World'; - expect($localize `Hello, ${getName()}!`).toEqual('Hello, World!'); + expect($localize`Hello, ${getName()}!`).toEqual('Hello, World!'); }); }); @@ -44,15 +46,17 @@ describe('$localize tag with translations', () => { 'Hello, {$PH}!': 'HELLO, {$PH}!', })); }); - afterEach(() => { clearTranslations(); }); + afterEach(() => { + clearTranslations(); + }); it('should render template literals with messages upper-cased', () => { - expect($localize `abc`).toEqual('ABC'); - expect($localize `abc${1 + 2 + 3}`).toEqual('ABC6'); - expect($localize `abc${1 + 2 + 3}def`).toEqual('ABC6DEF'); - expect($localize `abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('ABC6DEF15'); + expect($localize`abc`).toEqual('ABC'); + expect($localize`abc${1 + 2 + 3}`).toEqual('ABC6'); + expect($localize`abc${1 + 2 + 3}def`).toEqual('ABC6DEF'); + expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('ABC6DEF15'); const getName = () => 'World'; - expect($localize `Hello, ${getName()}!`).toEqual('HELLO, World!'); + expect($localize`Hello, ${getName()}!`).toEqual('HELLO, World!'); }); }); @@ -62,11 +66,13 @@ describe('$localize tag with translations', () => { 'abc{$PH}def{$PH_1} - Hello, {$PH_2}!': 'abc{$PH_2}def{$PH_1} - Hello, {$PH}!', })); }); - afterEach(() => { clearTranslations(); }); + afterEach(() => { + clearTranslations(); + }); it('should render template literals with expressions reversed', () => { const getName = () => 'World'; - expect($localize `abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`) + expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`) .toEqual('abcWorlddef15 - Hello, 6!'); }); }); @@ -77,11 +83,13 @@ describe('$localize tag with translations', () => { 'abc{$PH}def{$PH_1} - Hello, {$PH_2}!': 'abc{$PH} - Hello, {$PH_2}!', })); }); - afterEach(() => { clearTranslations(); }); + afterEach(() => { + clearTranslations(); + }); it('should render template literals with expressions removed', () => { const getName = () => 'World'; - expect($localize `abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`) + expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`) .toEqual('abc6 - Hello, World!'); }); }); diff --git a/packages/platform-browser-dynamic/src/compiler_factory.ts b/packages/platform-browser-dynamic/src/compiler_factory.ts index ac0a1ed8d5298..e8540bccf4bb7 100644 --- a/packages/platform-browser-dynamic/src/compiler_factory.ts +++ b/packages/platform-browser-dynamic/src/compiler_factory.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileMetadataResolver, CompileReflector, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, ElementSchemaRegistry, HtmlParser, I18NHtmlParser, JitCompiler, JitEvaluator, JitSummaryResolver, Lexer, NgModuleCompiler, NgModuleResolver, Parser, PipeResolver, ProviderMeta, ResourceLoader, StaticSymbolCache, StyleCompiler, SummaryResolver, TemplateParser, UrlResolver, ViewCompiler} from '@angular/compiler'; -import {Compiler, CompilerFactory, CompilerOptions, ComponentFactory, Inject, InjectionToken, Injector, MissingTranslationStrategy, ModuleWithComponentFactories, NgModuleFactory, Optional, PACKAGE_ROOT_URL, StaticProvider, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, isDevMode, ɵConsole as Console} from '@angular/core'; +import {CompileMetadataResolver, CompilerConfig, CompileReflector, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, ElementSchemaRegistry, HtmlParser, I18NHtmlParser, JitCompiler, JitEvaluator, JitSummaryResolver, Lexer, NgModuleCompiler, NgModuleResolver, Parser, PipeResolver, ProviderMeta, ResourceLoader, StaticSymbolCache, StyleCompiler, SummaryResolver, TemplateParser, UrlResolver, ViewCompiler} from '@angular/compiler'; +import {Compiler, CompilerFactory, CompilerOptions, ComponentFactory, Inject, InjectionToken, Injector, isDevMode, MissingTranslationStrategy, ModuleWithComponentFactories, NgModuleFactory, Optional, PACKAGE_ROOT_URL, StaticProvider, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, ɵConsole as Console} from '@angular/core'; import {JitReflector} from './compiler_reflector'; @@ -22,9 +22,10 @@ export const DEFAULT_PACKAGE_URL_PROVIDER = { }; const _NO_RESOURCE_LOADER: ResourceLoader = { - get(url: string): Promise<string>{ - throw new Error( - `No ResourceLoader implementation has been provided. Can't read the url "${url}"`);} + get(url: string): Promise<string> { + throw new Error( + `No ResourceLoader implementation has been provided. Can't read the url "${url}"`); + } }; const baseHtmlParser = new InjectionToken('HtmlParser'); @@ -71,13 +72,21 @@ export class CompilerImpl implements Compiler { componentFactories: result.componentFactories as ComponentFactory<any>[], })); } - loadAotSummaries(summaries: () => any[]) { this._delegate.loadAotSummaries(summaries); } - hasAotSummary(ref: Type<any>): boolean { return this._delegate.hasAotSummary(ref); } + loadAotSummaries(summaries: () => any[]) { + this._delegate.loadAotSummaries(summaries); + } + hasAotSummary(ref: Type<any>): boolean { + return this._delegate.hasAotSummary(ref); + } getComponentFactory<T>(component: Type<T>): ComponentFactory<T> { return this._delegate.getComponentFactory(component) as ComponentFactory<T>; } - clearCache(): void { this._delegate.clearCache(); } - clearCacheFor(type: Type<any>) { this._delegate.clearCacheFor(type); } + clearCache(): void { + this._delegate.clearCache(); + } + clearCacheFor(type: Type<any>) { + this._delegate.clearCacheFor(type); + } getModuleId(moduleType: Type<any>): string|undefined { const meta = this._metadataResolver.getNgModuleMetadata(moduleType); return meta && meta.id || undefined; @@ -102,13 +111,14 @@ const COMPILER_PROVIDERS__PRE_R3__ = <StaticProvider[]>[ }, { provide: I18NHtmlParser, - useFactory: (parser: HtmlParser, translations: string | null, format: string, - config: CompilerConfig, console: Console) => { - translations = translations || ''; - const missingTranslation = - translations ? config.missingTranslation ! : MissingTranslationStrategy.Ignore; - return new I18NHtmlParser(parser, translations, format, missingTranslation, console); - }, + useFactory: + (parser: HtmlParser, translations: string|null, format: string, config: CompilerConfig, + console: Console) => { + translations = translations || ''; + const missingTranslation = + translations ? config.missingTranslation! : MissingTranslationStrategy.Ignore; + return new I18NHtmlParser(parser, translations, format, missingTranslation, console); + }, deps: [ baseHtmlParser, [new Optional(), new Inject(TRANSLATIONS)], @@ -122,36 +132,38 @@ const COMPILER_PROVIDERS__PRE_R3__ = <StaticProvider[]>[ useExisting: I18NHtmlParser, }, { - provide: TemplateParser, deps: [CompilerConfig, CompileReflector, - Parser, ElementSchemaRegistry, - I18NHtmlParser, Console] + provide: TemplateParser, + deps: [CompilerConfig, CompileReflector, Parser, ElementSchemaRegistry, I18NHtmlParser, Console] + }, + {provide: JitEvaluator, useClass: JitEvaluator, deps: []}, + {provide: DirectiveNormalizer, deps: [ResourceLoader, UrlResolver, HtmlParser, CompilerConfig]}, + { + provide: CompileMetadataResolver, + deps: [ + CompilerConfig, HtmlParser, NgModuleResolver, DirectiveResolver, PipeResolver, + SummaryResolver, ElementSchemaRegistry, DirectiveNormalizer, Console, + [Optional, StaticSymbolCache], CompileReflector, [Optional, ERROR_COLLECTOR_TOKEN] + ] }, - { provide: JitEvaluator, useClass: JitEvaluator, deps: [] }, - { provide: DirectiveNormalizer, deps: [ResourceLoader, UrlResolver, HtmlParser, CompilerConfig]}, - { provide: CompileMetadataResolver, deps: [CompilerConfig, HtmlParser, NgModuleResolver, - DirectiveResolver, PipeResolver, - SummaryResolver, - ElementSchemaRegistry, - DirectiveNormalizer, Console, - [Optional, StaticSymbolCache], - CompileReflector, - [Optional, ERROR_COLLECTOR_TOKEN]]}, DEFAULT_PACKAGE_URL_PROVIDER, - { provide: StyleCompiler, deps: [UrlResolver]}, - { provide: ViewCompiler, deps: [CompileReflector]}, - { provide: NgModuleCompiler, deps: [CompileReflector] }, - { provide: CompilerConfig, useValue: new CompilerConfig()}, - { provide: Compiler, useClass: CompilerImpl, deps: [Injector, CompileMetadataResolver, - TemplateParser, StyleCompiler, - ViewCompiler, NgModuleCompiler, - SummaryResolver, CompileReflector, JitEvaluator, CompilerConfig, - Console]}, - { provide: DomElementSchemaRegistry, deps: []}, - { provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry}, - { provide: UrlResolver, deps: [PACKAGE_ROOT_URL]}, - { provide: DirectiveResolver, deps: [CompileReflector]}, - { provide: PipeResolver, deps: [CompileReflector]}, - { provide: NgModuleResolver, deps: [CompileReflector]}, + {provide: StyleCompiler, deps: [UrlResolver]}, + {provide: ViewCompiler, deps: [CompileReflector]}, + {provide: NgModuleCompiler, deps: [CompileReflector]}, + {provide: CompilerConfig, useValue: new CompilerConfig()}, + { + provide: Compiler, + useClass: CompilerImpl, + deps: [ + Injector, CompileMetadataResolver, TemplateParser, StyleCompiler, ViewCompiler, + NgModuleCompiler, SummaryResolver, CompileReflector, JitEvaluator, CompilerConfig, Console + ] + }, + {provide: DomElementSchemaRegistry, deps: []}, + {provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry}, + {provide: UrlResolver, deps: [PACKAGE_ROOT_URL]}, + {provide: DirectiveResolver, deps: [CompileReflector]}, + {provide: PipeResolver, deps: [CompileReflector]}, + {provide: NgModuleResolver, deps: [CompileReflector]}, ]; export const COMPILER_PROVIDERS__POST_R3__ = @@ -193,7 +205,7 @@ export class JitCompilerFactory implements CompilerFactory { }, deps: [] }, - opts.providers ! + opts.providers! ]); return injector.get(Compiler); } @@ -203,7 +215,7 @@ function _mergeOptions(optionsArr: CompilerOptions[]): CompilerOptions { return { useJit: _lastDefined(optionsArr.map(options => options.useJit)), defaultEncapsulation: _lastDefined(optionsArr.map(options => options.defaultEncapsulation)), - providers: _mergeArrays(optionsArr.map(options => options.providers !)), + providers: _mergeArrays(optionsArr.map(options => options.providers!)), missingTranslation: _lastDefined(optionsArr.map(options => options.missingTranslation)), preserveWhitespaces: _lastDefined(optionsArr.map(options => options.preserveWhitespaces)), }; diff --git a/packages/platform-browser-dynamic/src/compiler_reflector.ts b/packages/platform-browser-dynamic/src/compiler_reflector.ts index 25904ae730219..3ac0515f2f0ec 100644 --- a/packages/platform-browser-dynamic/src/compiler_reflector.ts +++ b/packages/platform-browser-dynamic/src/compiler_reflector.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileReflector, ExternalReference, Identifiers, getUrlScheme, syntaxError} from '@angular/compiler'; -import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, NgModuleRef, QueryList, Renderer2, SecurityContext, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵCodegenComponentFactoryResolver, ɵEMPTY_ARRAY, ɵEMPTY_MAP, ɵReflectionCapabilities as ReflectionCapabilities, ɵand, ɵccf, ɵcmf, ɵcrt, ɵdid, ɵeld, ɵinlineInterpolate, ɵinterpolate, ɵmod, ɵmpd, ɵncd, ɵnov, ɵpad, ɵpid, ɵpod, ɵppd, ɵprd, ɵqud, ɵregisterModuleFactory, ɵstringify as stringify, ɵted, ɵunv, ɵvid} from '@angular/core'; +import {CompileReflector, ExternalReference, getUrlScheme, Identifiers, syntaxError} from '@angular/compiler'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, NgModuleRef, QueryList, Renderer2, SecurityContext, TemplateRef, TRANSLATIONS_FORMAT, ViewContainerRef, ViewEncapsulation, ɵand, ɵccf, ɵcmf, ɵCodegenComponentFactoryResolver, ɵcrt, ɵdid, ɵeld, ɵEMPTY_ARRAY, ɵEMPTY_MAP, ɵinlineInterpolate, ɵinterpolate, ɵmod, ɵmpd, ɵncd, ɵnov, ɵpad, ɵpid, ɵpod, ɵppd, ɵprd, ɵqud, ɵReflectionCapabilities as ReflectionCapabilities, ɵregisterModuleFactory, ɵstringify as stringify, ɵted, ɵunv, ɵvid} from '@angular/core'; export const MODULE_SUFFIX = ''; const builtinExternalReferences = createBuiltinExternalReferencesMap(); @@ -23,7 +23,8 @@ export class JitReflector implements CompileReflector { return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`; } else if (moduleId !== null && moduleId !== void 0) { throw syntaxError( - `moduleId should be a string in "${stringify(type)}". See https://goo.gl/wIDDiL for more information.\n` + + `moduleId should be a string in "${ + stringify(type)}". See https://goo.gl/wIDDiL for more information.\n` + `If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`); } @@ -32,7 +33,9 @@ export class JitReflector implements CompileReflector { parameters(typeOrFunc: /*Type*/ any): any[][] { return this.reflectionCapabilities.parameters(typeOrFunc); } - tryAnnotations(typeOrFunc: /*Type*/ any): any[] { return this.annotations(typeOrFunc); } + tryAnnotations(typeOrFunc: /*Type*/ any): any[] { + return this.annotations(typeOrFunc); + } annotations(typeOrFunc: /*Type*/ any): any[] { return this.reflectionCapabilities.annotations(typeOrFunc); } @@ -45,7 +48,9 @@ export class JitReflector implements CompileReflector { hasLifecycleHook(type: any, lcProperty: string): boolean { return this.reflectionCapabilities.hasLifecycleHook(type, lcProperty); } - guards(type: any): {[key: string]: any} { return this.reflectionCapabilities.guards(type); } + guards(type: any): {[key: string]: any} { + return this.reflectionCapabilities.guards(type); + } resolveExternalReference(ref: ExternalReference): any { return builtinExternalReferences.get(ref) || ref.runtime; } diff --git a/packages/platform-browser-dynamic/src/platform-browser-dynamic.ts b/packages/platform-browser-dynamic/src/platform-browser-dynamic.ts index f34d09d475733..322f51ad7c54a 100644 --- a/packages/platform-browser-dynamic/src/platform-browser-dynamic.ts +++ b/packages/platform-browser-dynamic/src/platform-browser-dynamic.ts @@ -7,7 +7,7 @@ */ import {ResourceLoader} from '@angular/compiler'; -import {CompilerFactory, PlatformRef, Provider, StaticProvider, createPlatformFactory, platformCore} from '@angular/core'; +import {CompilerFactory, createPlatformFactory, platformCore, PlatformRef, Provider, StaticProvider} from '@angular/core'; import {platformCoreDynamic} from './platform_core_dynamic'; import {INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS} from './platform_providers'; diff --git a/packages/platform-browser-dynamic/src/platform_core_dynamic.ts b/packages/platform-browser-dynamic/src/platform_core_dynamic.ts index b035b7b0245ee..3a0a63f457ec2 100644 --- a/packages/platform-browser-dynamic/src/platform_core_dynamic.ts +++ b/packages/platform-browser-dynamic/src/platform_core_dynamic.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMPILER_OPTIONS, CompilerFactory, PlatformRef, StaticProvider, createPlatformFactory, platformCore} from '@angular/core'; +import {COMPILER_OPTIONS, CompilerFactory, createPlatformFactory, platformCore, PlatformRef, StaticProvider} from '@angular/core'; + import {JitCompilerFactory} from './compiler_factory'; /** diff --git a/packages/platform-browser-dynamic/src/resource_loader/resource_loader_impl.ts b/packages/platform-browser-dynamic/src/resource_loader/resource_loader_impl.ts index 4e5dfd6e5e3b9..812914a7dd9dc 100644 --- a/packages/platform-browser-dynamic/src/resource_loader/resource_loader_impl.ts +++ b/packages/platform-browser-dynamic/src/resource_loader/resource_loader_impl.ts @@ -45,7 +45,9 @@ export class ResourceLoaderImpl extends ResourceLoader { } }; - xhr.onerror = function() { reject(`Failed to load ${url}`); }; + xhr.onerror = function() { + reject(`Failed to load ${url}`); + }; xhr.send(); return promise; diff --git a/packages/platform-browser-dynamic/test/metadata_overrider_spec.ts b/packages/platform-browser-dynamic/test/metadata_overrider_spec.ts index 42933442a7f2d..3bf4c38979e5a 100644 --- a/packages/platform-browser-dynamic/test/metadata_overrider_spec.ts +++ b/packages/platform-browser-dynamic/test/metadata_overrider_spec.ts @@ -25,9 +25,9 @@ class SomeMetadata implements SomeMetadataType { arrayProp: any[]; constructor(options: SomeMetadataType) { - this.plainProp = options.plainProp !; - this._getterProp = options.getterProp !; - this.arrayProp = options.arrayProp !; + this.plainProp = options.plainProp!; + this._getterProp = options.getterProp!; + this.arrayProp = options.arrayProp!; Object.defineProperty(this, 'getterProp', { enumerable: true, // getters are non-enumerable by default in es2015 get: () => this._getterProp, @@ -45,7 +45,7 @@ class OtherMetadata extends SomeMetadata implements OtherMetadataType { arrayProp: options.arrayProp }); - this.otherPlainProp = options.otherPlainProp !; + this.otherPlainProp = options.otherPlainProp!; } } @@ -53,7 +53,9 @@ class OtherMetadata extends SomeMetadata implements OtherMetadataType { describe('metadata overrider', () => { let overrider: MetadataOverrider; - beforeEach(() => { overrider = new MetadataOverrider(); }); + beforeEach(() => { + overrider = new MetadataOverrider(); + }); it('should return a new instance with the same values', () => { const oldInstance = new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someInput'}); @@ -130,7 +132,6 @@ class OtherMetadata extends SomeMetadata implements OtherMetadataType { const instance3 = overrider.overrideMetadata(SomeMetadata, instance2, {remove: {arrayProp: [Class3]}}); expect(instance3).toEqual(new SomeMetadata({arrayProp: [Class2]})); - }); }); @@ -149,7 +150,6 @@ class OtherMetadata extends SomeMetadata implements OtherMetadataType { otherPlainProp: 'newOtherProp' })); }); - }); }); } diff --git a/packages/platform-browser-dynamic/test/resource_loader/resource_loader_cache_spec.ts b/packages/platform-browser-dynamic/test/resource_loader/resource_loader_cache_spec.ts index 316152f7b9115..4caf9e57f24b3 100644 --- a/packages/platform-browser-dynamic/test/resource_loader/resource_loader_cache_spec.ts +++ b/packages/platform-browser-dynamic/test/resource_loader/resource_loader_cache_spec.ts @@ -8,7 +8,7 @@ import {ResourceLoader, UrlResolver} from '@angular/compiler'; import {Component} from '@angular/core'; -import {TestBed, async, fakeAsync, tick} from '@angular/core/testing'; +import {async, fakeAsync, TestBed, tick} from '@angular/core/testing'; import {CachedResourceLoader} from '@angular/platform-browser-dynamic/src/resource_loader/resource_loader_cache'; import {setTemplateCache} from '@angular/platform-browser-dynamic/test/resource_loader/resource_loader_cache_setter'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -31,7 +31,9 @@ if (isBrowser) { it('should resolve the Promise with the cached file content on success', async(() => { resourceLoader = createCachedResourceLoader(); - resourceLoader.get('test.html').then((text) => { expect(text).toBe('<div>Hello</div>'); }); + resourceLoader.get('test.html').then((text) => { + expect(text).toBe('<div>Hello</div>'); + }); })); it('should reject the Promise on failure', async(() => { diff --git a/packages/platform-browser-dynamic/test/resource_loader/resource_loader_impl_spec.ts b/packages/platform-browser-dynamic/test/resource_loader/resource_loader_impl_spec.ts index 480cb97de176c..feef70f34969f 100644 --- a/packages/platform-browser-dynamic/test/resource_loader/resource_loader_impl_spec.ts +++ b/packages/platform-browser-dynamic/test/resource_loader/resource_loader_impl_spec.ts @@ -22,7 +22,9 @@ if (isBrowser) { const url200 = '/base/angular/packages/platform-browser/test/browser/static_assets/200.html'; const url404 = '/bad/path/404.html'; - beforeEach(() => { resourceLoader = new ResourceLoaderImpl(); }); + beforeEach(() => { + resourceLoader = new ResourceLoaderImpl(); + }); it('should resolve the Promise with the file content on success', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { diff --git a/packages/platform-browser-dynamic/test/testing_public_browser_spec.ts b/packages/platform-browser-dynamic/test/testing_public_browser_spec.ts index 7bac854e87361..6adde8f4a4771 100644 --- a/packages/platform-browser-dynamic/test/testing_public_browser_spec.ts +++ b/packages/platform-browser-dynamic/test/testing_public_browser_spec.ts @@ -8,8 +8,7 @@ import {ResourceLoader} from '@angular/compiler'; import {Compiler, Component, NgModule} from '@angular/core'; -import {TestBed, async, fakeAsync, inject, tick} from '@angular/core/testing'; - +import {async, fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; import {ResourceLoaderImpl} from '@angular/platform-browser-dynamic/src/resource_loader/resource_loader_impl'; @@ -17,10 +16,15 @@ import {ResourceLoaderImpl} from '@angular/platform-browser-dynamic/src/resource // Components for the tests. class FancyService { value: string = 'real value'; - getAsyncValue() { return Promise.resolve('async value'); } + getAsyncValue() { + return Promise.resolve('async value'); + } getTimeoutValue() { - return new Promise( - (resolve, reject) => { setTimeout(() => { resolve('timeout value'); }, 10); }); + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve('timeout value'); + }, 10); + }); } } @@ -42,15 +46,21 @@ if (isBrowser) { describe('using the async helper', () => { let actuallyDone: boolean; - beforeEach(() => { actuallyDone = false; }); + beforeEach(() => { + actuallyDone = false; + }); - afterEach(() => { expect(actuallyDone).toEqual(true); }); + afterEach(() => { + expect(actuallyDone).toEqual(true); + }); it('should run async tests with ResourceLoaders', async(() => { const resourceLoader = new ResourceLoaderImpl(); resourceLoader .get('/base/angular/packages/platform-browser/test/static_assets/test.html') - .then(() => { actuallyDone = true; }); + .then(() => { + actuallyDone = true; + }); }), 10000); // Long timeout here because this test makes an actual ResourceLoader. }); @@ -70,7 +80,9 @@ if (isBrowser) { it('should allow the use of fakeAsync', fakeAsync(inject([FancyService], (service: any /** TODO #9100 */) => { let value: any /** TODO #9100 */; - service.getAsyncValue().then(function(val: any /** TODO #9100 */) { value = val; }); + service.getAsyncValue().then(function(val: any /** TODO #9100 */) { + value = val; + }); tick(); expect(value).toEqual('async value'); }))); @@ -113,7 +125,9 @@ if (isBrowser) { return promise; }; - const restoreJasmineIt = () => { jasmine.getEnv().it = originalJasmineIt; }; + const restoreJasmineIt = () => { + jasmine.getEnv().it = originalJasmineIt; + }; it('should fail when an ResourceLoader fails', done => { const itPromise = patchJasmineIt(); @@ -124,7 +138,9 @@ if (isBrowser) { })); itPromise.then( - () => { done.fail('Expected test to fail, but it did not'); }, + () => { + done.fail('Expected test to fail, but it did not'); + }, (err: any) => { expect(err.message) .toEqual('Uncaught (in promise): Failed to load non-existent.html'); diff --git a/packages/platform-browser-dynamic/testing/src/compiler_factory.ts b/packages/platform-browser-dynamic/testing/src/compiler_factory.ts index 28fcfde2b2f32..1f5ff43e85eda 100644 --- a/packages/platform-browser-dynamic/testing/src/compiler_factory.ts +++ b/packages/platform-browser-dynamic/testing/src/compiler_factory.ts @@ -39,7 +39,9 @@ export class TestingCompilerImpl implements TestingCompiler { constructor( private _compiler: CompilerImpl, private _directiveResolver: MockDirectiveResolver, private _pipeResolver: MockPipeResolver, private _moduleResolver: MockNgModuleResolver) {} - get injector(): Injector { return this._compiler.injector; } + get injector(): Injector { + return this._compiler.injector; + } compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> { return this._compiler.compileModuleSync(moduleType); @@ -78,14 +80,14 @@ export class TestingCompilerImpl implements TestingCompiler { this.checkOverrideAllowed(directive); const oldMetadata = this._directiveResolver.resolve(directive, false); this._directiveResolver.setDirective( - directive, this._overrider.overrideMetadata(Directive, oldMetadata !, override)); + directive, this._overrider.overrideMetadata(Directive, oldMetadata!, override)); this.clearCacheFor(directive); } overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void { this.checkOverrideAllowed(component); const oldMetadata = this._directiveResolver.resolve(component, false); this._directiveResolver.setDirective( - component, this._overrider.overrideMetadata(Component, oldMetadata !, override)); + component, this._overrider.overrideMetadata(Component, oldMetadata!, override)); this.clearCacheFor(component); } overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void { @@ -94,11 +96,19 @@ export class TestingCompilerImpl implements TestingCompiler { this._pipeResolver.setPipe(pipe, this._overrider.overrideMetadata(Pipe, oldMetadata, override)); this.clearCacheFor(pipe); } - loadAotSummaries(summaries: () => any[]) { this._compiler.loadAotSummaries(summaries); } - clearCache(): void { this._compiler.clearCache(); } - clearCacheFor(type: Type<any>) { this._compiler.clearCacheFor(type); } + loadAotSummaries(summaries: () => any[]) { + this._compiler.loadAotSummaries(summaries); + } + clearCache(): void { + this._compiler.clearCache(); + } + clearCacheFor(type: Type<any>) { + this._compiler.clearCacheFor(type); + } - getComponentFromError(error: Error) { return (error as any)[ERROR_COMPONENT_TYPE] || null; } + getComponentFromError(error: Error) { + return (error as any)[ERROR_COMPONENT_TYPE] || null; + } getModuleId(moduleType: Type<any>): string|undefined { return this._moduleResolver.resolve(moduleType, true).id; diff --git a/packages/platform-browser-dynamic/testing/src/dom_test_component_renderer.ts b/packages/platform-browser-dynamic/testing/src/dom_test_component_renderer.ts index 46e3ea96abf2f..33e55e07bae37 100644 --- a/packages/platform-browser-dynamic/testing/src/dom_test_component_renderer.ts +++ b/packages/platform-browser-dynamic/testing/src/dom_test_component_renderer.ts @@ -15,7 +15,9 @@ import {TestComponentRenderer} from '@angular/core/testing'; */ @Injectable() export class DOMTestComponentRenderer extends TestComponentRenderer { - constructor(@Inject(DOCUMENT) private _doc: any) { super(); } + constructor(@Inject(DOCUMENT) private _doc: any) { + super(); + } insertRootElement(rootElId: string) { const template = getDOM().getDefaultDocument().createElement('template'); diff --git a/packages/platform-browser-dynamic/testing/src/platform_core_dynamic_testing.ts b/packages/platform-browser-dynamic/testing/src/platform_core_dynamic_testing.ts index 7e7f24266efa2..1c540f5fad1ec 100644 --- a/packages/platform-browser-dynamic/testing/src/platform_core_dynamic_testing.ts +++ b/packages/platform-browser-dynamic/testing/src/platform_core_dynamic_testing.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMPILER_OPTIONS, CompilerFactory, Injector, PlatformRef, createPlatformFactory} from '@angular/core'; +import {COMPILER_OPTIONS, CompilerFactory, createPlatformFactory, Injector, PlatformRef} from '@angular/core'; import {ɵTestingCompilerFactory as TestingCompilerFactory} from '@angular/core/testing'; import {ɵplatformCoreDynamic as platformCoreDynamic} from '@angular/platform-browser-dynamic'; diff --git a/packages/platform-browser-dynamic/testing/src/testing.ts b/packages/platform-browser-dynamic/testing/src/testing.ts index 9454590a91e54..8109454b4b1c2 100644 --- a/packages/platform-browser-dynamic/testing/src/testing.ts +++ b/packages/platform-browser-dynamic/testing/src/testing.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {NgModule, PlatformRef, StaticProvider, createPlatformFactory} from '@angular/core'; +import {createPlatformFactory, NgModule, PlatformRef, StaticProvider} from '@angular/core'; import {TestComponentRenderer} from '@angular/core/testing'; import {ɵINTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS as INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS} from '@angular/platform-browser-dynamic'; import {BrowserTestingModule} from '@angular/platform-browser/testing'; diff --git a/packages/platform-browser/animations/src/animation_builder.ts b/packages/platform-browser/animations/src/animation_builder.ts index 2028af8bbb22b..b705d54623394 100644 --- a/packages/platform-browser/animations/src/animation_builder.ts +++ b/packages/platform-browser/animations/src/animation_builder.ts @@ -18,12 +18,9 @@ export class BrowserAnimationBuilder extends AnimationBuilder { constructor(rootRenderer: RendererFactory2, @Inject(DOCUMENT) doc: any) { super(); - const typeData = { - id: '0', - encapsulation: ViewEncapsulation.None, - styles: [], - data: {animation: []} - } as RendererType2; + const typeData = + {id: '0', encapsulation: ViewEncapsulation.None, styles: [], data: {animation: []}} as + RendererType2; this._renderer = rootRenderer.createRenderer(doc.body, typeData) as AnimationRenderer; } @@ -37,7 +34,9 @@ export class BrowserAnimationBuilder extends AnimationBuilder { } export class BrowserAnimationFactory extends AnimationFactory { - constructor(private _id: string, private _renderer: AnimationRenderer) { super(); } + constructor(private _id: string, private _renderer: AnimationRenderer) { + super(); + } create(element: any, options?: AnimationOptions): AnimationPlayer { return new RendererAnimationPlayer(this._id, element, options || {}, this._renderer); @@ -62,34 +61,58 @@ export class RendererAnimationPlayer implements AnimationPlayer { return issueAnimationCommand(this._renderer, this.element, this.id, command, args); } - onDone(fn: () => void): void { this._listen('done', fn); } + onDone(fn: () => void): void { + this._listen('done', fn); + } - onStart(fn: () => void): void { this._listen('start', fn); } + onStart(fn: () => void): void { + this._listen('start', fn); + } - onDestroy(fn: () => void): void { this._listen('destroy', fn); } + onDestroy(fn: () => void): void { + this._listen('destroy', fn); + } - init(): void { this._command('init'); } + init(): void { + this._command('init'); + } - hasStarted(): boolean { return this._started; } + hasStarted(): boolean { + return this._started; + } play(): void { this._command('play'); this._started = true; } - pause(): void { this._command('pause'); } + pause(): void { + this._command('pause'); + } - restart(): void { this._command('restart'); } + restart(): void { + this._command('restart'); + } - finish(): void { this._command('finish'); } + finish(): void { + this._command('finish'); + } - destroy(): void { this._command('destroy'); } + destroy(): void { + this._command('destroy'); + } - reset(): void { this._command('reset'); } + reset(): void { + this._command('reset'); + } - setPosition(p: number): void { this._command('setPosition', p); } + setPosition(p: number): void { + this._command('setPosition', p); + } - getPosition(): number { return 0; } + getPosition(): number { + return 0; + } public totalTime = 0; } diff --git a/packages/platform-browser/animations/src/animation_renderer.ts b/packages/platform-browser/animations/src/animation_renderer.ts index 02ae8cf58d853..af2f739b111cc 100644 --- a/packages/platform-browser/animations/src/animation_renderer.ts +++ b/packages/platform-browser/animations/src/animation_renderer.ts @@ -15,7 +15,7 @@ const DISABLE_ANIMATIONS_FLAG = '@.disabled'; // Define a recursive type to allow for nested arrays of `AnimationTriggerMetadata`. Note that an // interface declaration is used as TypeScript prior to 3.7 does not support recursive type // references, see https://github.com/microsoft/TypeScript/pull/33050 for details. -type NestedAnimationTriggerMetadata = AnimationTriggerMetadata | RecursiveAnimationTriggerMetadata; +type NestedAnimationTriggerMetadata = AnimationTriggerMetadata|RecursiveAnimationTriggerMetadata; interface RecursiveAnimationTriggerMetadata extends Array<NestedAnimationTriggerMetadata> {} @Injectable() @@ -84,7 +84,9 @@ export class AnimationRendererFactory implements RendererFactory2 { private _scheduleCountTask() { // always use promise to schedule microtask instead of use Zone - this.promise.then(() => { this._microtaskId++; }); + this.promise.then(() => { + this._microtaskId++; + }); } /** @internal */ @@ -125,16 +127,20 @@ export class AnimationRendererFactory implements RendererFactory2 { } } - whenRenderingDone(): Promise<any> { return this.engine.whenRenderingDone(); } + whenRenderingDone(): Promise<any> { + return this.engine.whenRenderingDone(); + } } export class BaseAnimationRenderer implements Renderer2 { constructor( protected namespaceId: string, public delegate: Renderer2, public engine: AnimationEngine) { - this.destroyNode = this.delegate.destroyNode ? (n) => delegate.destroyNode !(n) : null; + this.destroyNode = this.delegate.destroyNode ? (n) => delegate.destroyNode!(n) : null; } - get data() { return this.delegate.data; } + get data() { + return this.delegate.data; + } destroyNode: ((n: any) => void)|null; @@ -147,9 +153,13 @@ export class BaseAnimationRenderer implements Renderer2 { return this.delegate.createElement(name, namespace); } - createComment(value: string) { return this.delegate.createComment(value); } + createComment(value: string) { + return this.delegate.createComment(value); + } - createText(value: string) { return this.delegate.createText(value); } + createText(value: string) { + return this.delegate.createText(value); + } appendChild(parent: any, newChild: any): void { this.delegate.appendChild(parent, newChild); @@ -169,9 +179,13 @@ export class BaseAnimationRenderer implements Renderer2 { return this.delegate.selectRootElement(selectorOrNode, preserveContent); } - parentNode(node: any) { return this.delegate.parentNode(node); } + parentNode(node: any) { + return this.delegate.parentNode(node); + } - nextSibling(node: any) { return this.delegate.nextSibling(node); } + nextSibling(node: any) { + return this.delegate.nextSibling(node); + } setAttribute(el: any, name: string, value: string, namespace?: string|null|undefined): void { this.delegate.setAttribute(el, name, value, namespace); @@ -181,9 +195,13 @@ export class BaseAnimationRenderer implements Renderer2 { this.delegate.removeAttribute(el, name, namespace); } - addClass(el: any, name: string): void { this.delegate.addClass(el, name); } + addClass(el: any, name: string): void { + this.delegate.addClass(el, name); + } - removeClass(el: any, name: string): void { this.delegate.removeClass(el, name); } + removeClass(el: any, name: string): void { + this.delegate.removeClass(el, name); + } setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2|undefined): void { this.delegate.setStyle(el, style, value, flags); @@ -201,7 +219,9 @@ export class BaseAnimationRenderer implements Renderer2 { } } - setValue(node: any, value: string): void { this.delegate.setValue(node, value); } + setValue(node: any, value: string): void { + this.delegate.setValue(node, value); + } listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void { return this.delegate.listen(target, eventName, callback); @@ -253,7 +273,7 @@ export class AnimationRenderer extends BaseAnimationRenderer implements Renderer } } -function resolveElementFromTarget(target: 'window' | 'document' | 'body' | any): any { +function resolveElementFromTarget(target: 'window'|'document'|'body'|any): any { switch (target) { case 'body': return document.body; diff --git a/packages/platform-browser/animations/src/providers.ts b/packages/platform-browser/animations/src/providers.ts index 1df032529a878..105f7ced1311a 100644 --- a/packages/platform-browser/animations/src/providers.ts +++ b/packages/platform-browser/animations/src/providers.ts @@ -7,7 +7,7 @@ */ import {AnimationBuilder} from '@angular/animations'; -import {AnimationDriver, ɵAnimationEngine as AnimationEngine, ɵAnimationStyleNormalizer as AnimationStyleNormalizer, ɵCssKeyframesDriver as CssKeyframesDriver, ɵNoopAnimationDriver as NoopAnimationDriver, ɵWebAnimationsDriver as WebAnimationsDriver, ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer, ɵsupportsWebAnimations as supportsWebAnimations} from '@angular/animations/browser'; +import {AnimationDriver, ɵAnimationEngine as AnimationEngine, ɵAnimationStyleNormalizer as AnimationStyleNormalizer, ɵCssKeyframesDriver as CssKeyframesDriver, ɵNoopAnimationDriver as NoopAnimationDriver, ɵsupportsWebAnimations as supportsWebAnimations, ɵWebAnimationsDriver as WebAnimationsDriver, ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer} from '@angular/animations/browser'; import {DOCUMENT} from '@angular/common'; import {Inject, Injectable, InjectionToken, NgZone, Provider, RendererFactory2} from '@angular/core'; import {ɵDomRendererFactory2 as DomRendererFactory2} from '@angular/platform-browser'; diff --git a/packages/platform-browser/animations/test/animation_renderer_spec.ts b/packages/platform-browser/animations/test/animation_renderer_spec.ts index 6b5b49fa09021..44da1541af4b3 100644 --- a/packages/platform-browser/animations/test/animation_renderer_spec.ts +++ b/packages/platform-browser/animations/test/animation_renderer_spec.ts @@ -5,7 +5,7 @@ * 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 {AnimationPlayer, AnimationTriggerMetadata, animate, state, style, transition, trigger} from '@angular/animations'; +import {animate, AnimationPlayer, AnimationTriggerMetadata, state, style, transition, trigger} from '@angular/animations'; import {ɵAnimationEngine as AnimationEngine} from '@angular/animations/browser'; import {Component, Injectable, NgZone, RendererFactory2, RendererType2, ViewChild} from '@angular/core'; import {TestBed} from '@angular/core/testing'; @@ -15,309 +15,314 @@ import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_rendere import {el} from '../../testing/src/browser_util'; (function() { - if (isNode) return; - describe('AnimationRenderer', () => { - let element: any; - beforeEach(() => { - element = el('<div></div>'); - - TestBed.configureTestingModule({ - providers: [{provide: AnimationEngine, useClass: MockAnimationEngine}], - imports: [BrowserAnimationsModule] - }); +if (isNode) return; +describe('AnimationRenderer', () => { + let element: any; + beforeEach(() => { + element = el('<div></div>'); + + TestBed.configureTestingModule({ + providers: [{provide: AnimationEngine, useClass: MockAnimationEngine}], + imports: [BrowserAnimationsModule] }); + }); - function makeRenderer(animationTriggers: any[] = []) { - const type = <RendererType2>{ - id: 'id', - encapsulation: null !, - styles: [], - data: {'animation': animationTriggers} - }; - return (TestBed.inject(RendererFactory2) as AnimationRendererFactory) - .createRenderer(element, type); - } + function makeRenderer(animationTriggers: any[] = []) { + const type = <RendererType2>{ + id: 'id', + encapsulation: null!, + styles: [], + data: {'animation': animationTriggers} + }; + return (TestBed.inject(RendererFactory2) as AnimationRendererFactory) + .createRenderer(element, type); + } - it('should hook into the engine\'s insert operations when appending children', () => { - const renderer = makeRenderer(); - const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; - const container = el('<div></div>'); + it('should hook into the engine\'s insert operations when appending children', () => { + const renderer = makeRenderer(); + const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; + const container = el('<div></div>'); - renderer.appendChild(container, element); - expect(engine.captures['onInsert'].pop()).toEqual([element]); - }); + renderer.appendChild(container, element); + expect(engine.captures['onInsert'].pop()).toEqual([element]); + }); - it('should hook into the engine\'s insert operations when inserting a child before another', - () => { - const renderer = makeRenderer(); - const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; - const container = el('<div></div>'); - const element2 = el('<div></div>'); - container.appendChild(element2); + it('should hook into the engine\'s insert operations when inserting a child before another', + () => { + const renderer = makeRenderer(); + const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; + const container = el('<div></div>'); + const element2 = el('<div></div>'); + container.appendChild(element2); + + renderer.insertBefore(container, element, element2); + expect(engine.captures['onInsert'].pop()).toEqual([element]); + }); + + it('should hook into the engine\'s insert operations when removing children', () => { + const renderer = makeRenderer(); + const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; + const container = el('<div></div>'); + + renderer.removeChild(container, element); + expect(engine.captures['onRemove'].pop()).toEqual([element]); + }); - renderer.insertBefore(container, element, element2); - expect(engine.captures['onInsert'].pop()).toEqual([element]); - }); + it('should hook into the engine\'s setProperty call if the property begins with `@`', () => { + const renderer = makeRenderer(); + const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; - it('should hook into the engine\'s insert operations when removing children', () => { - const renderer = makeRenderer(); - const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; - const container = el('<div></div>'); + renderer.setProperty(element, 'prop', 'value'); + expect(engine.captures['setProperty']).toBeFalsy(); - renderer.removeChild(container, element); - expect(engine.captures['onRemove'].pop()).toEqual([element]); - }); + renderer.setProperty(element, '@prop', 'value'); + expect(engine.captures['setProperty'].pop()).toEqual([element, 'prop', 'value']); + }); - it('should hook into the engine\'s setProperty call if the property begins with `@`', () => { - const renderer = makeRenderer(); - const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; + // https://github.com/angular/angular/issues/32794 + it('should support nested animation triggers', () => { + makeRenderer([[trigger('myAnimation', [])]]); - renderer.setProperty(element, 'prop', 'value'); - expect(engine.captures['setProperty']).toBeFalsy(); + const {triggers} = TestBed.inject(AnimationEngine) as MockAnimationEngine; - renderer.setProperty(element, '@prop', 'value'); - expect(engine.captures['setProperty'].pop()).toEqual([element, 'prop', 'value']); - }); + expect(triggers.length).toEqual(1); + expect(triggers[0].name).toEqual('myAnimation'); + }); + + describe('listen', () => { + it('should hook into the engine\'s listen call if the property begins with `@`', () => { + const renderer = makeRenderer(); + const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; - // https://github.com/angular/angular/issues/32794 - it('should support nested animation triggers', () => { - makeRenderer([[trigger('myAnimation', [])]]); + const cb = (event: any): boolean => { + return true; + }; - const {triggers} = TestBed.inject(AnimationEngine) as MockAnimationEngine; + renderer.listen(element, 'event', cb); + expect(engine.captures['listen']).toBeFalsy(); - expect(triggers.length).toEqual(1); - expect(triggers[0].name).toEqual('myAnimation'); + renderer.listen(element, '@event.phase', cb); + expect(engine.captures['listen'].pop()).toEqual([element, 'event', 'phase']); }); - describe('listen', () => { - it('should hook into the engine\'s listen call if the property begins with `@`', () => { - const renderer = makeRenderer(); - const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; + it('should resolve the body|document|window nodes given their values as strings as input', + () => { + const renderer = makeRenderer(); + const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; - const cb = (event: any): boolean => { return true; }; + const cb = (event: any): boolean => { + return true; + }; - renderer.listen(element, 'event', cb); - expect(engine.captures['listen']).toBeFalsy(); + renderer.listen('body', '@event', cb); + expect(engine.captures['listen'].pop()[0]).toBe(document.body); - renderer.listen(element, '@event.phase', cb); - expect(engine.captures['listen'].pop()).toEqual([element, 'event', 'phase']); - }); + renderer.listen('document', '@event', cb); + expect(engine.captures['listen'].pop()[0]).toBe(document); - it('should resolve the body|document|window nodes given their values as strings as input', - () => { - const renderer = makeRenderer(); - const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine; + renderer.listen('window', '@event', cb); + expect(engine.captures['listen'].pop()[0]).toBe(window); + }); + }); - const cb = (event: any): boolean => { return true; }; + describe('registering animations', () => { + it('should only create a trigger definition once even if the registered multiple times'); + }); - renderer.listen('body', '@event', cb); - expect(engine.captures['listen'].pop()[0]).toBe(document.body); + describe('flushing animations', () => { + // these tests are only mean't to be run within the DOM + if (isNode) return; - renderer.listen('document', '@event', cb); - expect(engine.captures['listen'].pop()[0]).toBe(document); + it('should flush and fire callbacks when the zone becomes stable', (async) => { + @Component({ + selector: 'my-cmp', + template: '<div [@myAnimation]="exp" (@myAnimation.start)="onStart($event)"></div>', + animations: [trigger( + 'myAnimation', + [transition( + '* => state', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], + }) + class Cmp { + exp: any; + event: any; + onStart(event: any) { + this.event = event; + } + } - renderer.listen('window', '@event', cb); - expect(engine.captures['listen'].pop()[0]).toBe(window); - }); - }); + TestBed.configureTestingModule({ + providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], + declarations: [Cmp] + }); - describe('registering animations', () => { - it('should only create a trigger definition once even if the registered multiple times'); + const engine = TestBed.inject(AnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = 'state'; + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(cmp.event.triggerName).toEqual('myAnimation'); + expect(cmp.event.phaseName).toEqual('start'); + cmp.event = null; + + engine.flush(); + expect(cmp.event).toBeFalsy(); + async(); + }); }); - describe('flushing animations', () => { - // these tests are only mean't to be run within the DOM - if (isNode) return; - - it('should flush and fire callbacks when the zone becomes stable', (async) => { - @Component({ - selector: 'my-cmp', - template: '<div [@myAnimation]="exp" (@myAnimation.start)="onStart($event)"></div>', - animations: [trigger( - 'myAnimation', - [transition( - '* => state', - [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], - }) - class Cmp { - exp: any; - event: any; - onStart(event: any) { this.event = event; } - } + it('should properly insert/remove nodes through the animation renderer that do not contain animations', + (async) => { + @Component({ + selector: 'my-cmp', + template: '<div #elm *ngIf="exp"></div>', + animations: [trigger( + 'someAnimation', + [transition( + '* => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], + }) + class Cmp { + exp: any; + @ViewChild('elm') public element: any; + } + + TestBed.configureTestingModule({ + providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], + declarations: [Cmp] + }); - TestBed.configureTestingModule({ - providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], - declarations: [Cmp] - }); - - const engine = TestBed.inject(AnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = 'state'; - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(cmp.event.triggerName).toEqual('myAnimation'); - expect(cmp.event.phaseName).toEqual('start'); - cmp.event = null; - - engine.flush(); - expect(cmp.event).toBeFalsy(); - async(); - }); - }); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + cmp.exp = true; + fixture.detectChanges(); - it('should properly insert/remove nodes through the animation renderer that do not contain animations', - (async) => { - @Component({ - selector: 'my-cmp', - template: '<div #elm *ngIf="exp"></div>', - animations: [trigger( - 'someAnimation', - [transition( - '* => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])], - }) - class Cmp { - exp: any; - @ViewChild('elm') public element: any; - } - - TestBed.configureTestingModule({ - providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], - declarations: [Cmp] - }); + fixture.whenStable().then(() => { + cmp.exp = false; + const element = cmp.element; + expect(element.nativeElement.parentNode).toBeTruthy(); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - cmp.exp = true; fixture.detectChanges(); - fixture.whenStable().then(() => { - cmp.exp = false; - const element = cmp.element; - expect(element.nativeElement.parentNode).toBeTruthy(); - - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(element.nativeElement.parentNode).toBeFalsy(); - async(); - }); + expect(element.nativeElement.parentNode).toBeFalsy(); + async(); }); }); + }); - it('should only queue up dom removals if the element itself contains a valid leave animation', - () => { - @Component({ - selector: 'my-cmp', - template: ` + it('should only queue up dom removals if the element itself contains a valid leave animation', + () => { + @Component({ + selector: 'my-cmp', + template: ` <div #elm1 *ngIf="exp1"></div> <div #elm2 @animation1 *ngIf="exp2"></div> <div #elm3 @animation2 *ngIf="exp3"></div> `, - animations: [ - trigger('animation1', [transition('a => b', [])]), - trigger('animation2', [transition(':leave', [])]), - ] - }) - class Cmp { - exp1: any = true; - exp2: any = true; - exp3: any = true; - - @ViewChild('elm1') public elm1: any; - - @ViewChild('elm2') public elm2: any; - - @ViewChild('elm3') public elm3: any; - } - - TestBed.configureTestingModule({ - providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], - declarations: [Cmp] - }); - - const engine = TestBed.inject(AnimationEngine); - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; - - fixture.detectChanges(); - const elm1 = cmp.elm1; - const elm2 = cmp.elm2; - const elm3 = cmp.elm3; - assertHasParent(elm1); - assertHasParent(elm2); - assertHasParent(elm3); - engine.flush(); - finishPlayers(engine.players); - - cmp.exp1 = false; - fixture.detectChanges(); - assertHasParent(elm1, false); - assertHasParent(elm2); - assertHasParent(elm3); - engine.flush(); - expect(engine.players.length).toEqual(0); - - cmp.exp2 = false; - fixture.detectChanges(); - assertHasParent(elm1, false); - assertHasParent(elm2, false); - assertHasParent(elm3); - engine.flush(); - expect(engine.players.length).toEqual(0); - - cmp.exp3 = false; - fixture.detectChanges(); - assertHasParent(elm1, false); - assertHasParent(elm2, false); - assertHasParent(elm3); - engine.flush(); - expect(engine.players.length).toEqual(1); + animations: [ + trigger('animation1', [transition('a => b', [])]), + trigger('animation2', [transition(':leave', [])]), + ] + }) + class Cmp { + exp1: any = true; + exp2: any = true; + exp3: any = true; + + @ViewChild('elm1') public elm1: any; + + @ViewChild('elm2') public elm2: any; + + @ViewChild('elm3') public elm3: any; + } + + TestBed.configureTestingModule({ + providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], + declarations: [Cmp] }); - }); - }); - describe('AnimationRendererFactory', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [{ - provide: RendererFactory2, - useClass: ExtendedAnimationRendererFactory, - deps: [DomRendererFactory2, AnimationEngine, NgZone] - }], - imports: [BrowserAnimationsModule] - }); + const engine = TestBed.inject(AnimationEngine); + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; + + fixture.detectChanges(); + const elm1 = cmp.elm1; + const elm2 = cmp.elm2; + const elm3 = cmp.elm3; + assertHasParent(elm1); + assertHasParent(elm2); + assertHasParent(elm3); + engine.flush(); + finishPlayers(engine.players); + + cmp.exp1 = false; + fixture.detectChanges(); + assertHasParent(elm1, false); + assertHasParent(elm2); + assertHasParent(elm3); + engine.flush(); + expect(engine.players.length).toEqual(0); + + cmp.exp2 = false; + fixture.detectChanges(); + assertHasParent(elm1, false); + assertHasParent(elm2, false); + assertHasParent(elm3); + engine.flush(); + expect(engine.players.length).toEqual(0); + + cmp.exp3 = false; + fixture.detectChanges(); + assertHasParent(elm1, false); + assertHasParent(elm2, false); + assertHasParent(elm3); + engine.flush(); + expect(engine.players.length).toEqual(1); + }); + }); +}); + +describe('AnimationRendererFactory', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ + provide: RendererFactory2, + useClass: ExtendedAnimationRendererFactory, + deps: [DomRendererFactory2, AnimationEngine, NgZone] + }], + imports: [BrowserAnimationsModule] }); + }); - it('should provide hooks at the start and end of change detection', () => { - @Component({ - selector: 'my-cmp', - template: ` + it('should provide hooks at the start and end of change detection', () => { + @Component({ + selector: 'my-cmp', + template: ` <div [@myAnimation]="exp"></div> `, - animations: [trigger('myAnimation', [])] - }) - class Cmp { - public exp: any; - } + animations: [trigger('myAnimation', [])] + }) + class Cmp { + public exp: any; + } - TestBed.configureTestingModule({ - providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], - declarations: [Cmp] - }); + TestBed.configureTestingModule({ + providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}], + declarations: [Cmp] + }); - const renderer = TestBed.inject(RendererFactory2) as ExtendedAnimationRendererFactory; - const fixture = TestBed.createComponent(Cmp); - const cmp = fixture.componentInstance; + const renderer = TestBed.inject(RendererFactory2) as ExtendedAnimationRendererFactory; + const fixture = TestBed.createComponent(Cmp); + const cmp = fixture.componentInstance; - renderer.log = []; - fixture.detectChanges(); - expect(renderer.log).toEqual(['begin', 'end']); + renderer.log = []; + fixture.detectChanges(); + expect(renderer.log).toEqual(['begin', 'end']); - renderer.log = []; - fixture.detectChanges(); - expect(renderer.log).toEqual(['begin', 'end']); - }); + renderer.log = []; + fixture.detectChanges(); + expect(renderer.log).toEqual(['begin', 'end']); }); +}); })(); @Injectable() @@ -336,7 +341,9 @@ class MockAnimationEngine extends InjectableAnimationEngine { this.triggers.push(metadata); } - onInsert(namespaceId: string, element: any): void { this._capture('onInsert', [element]); } + onInsert(namespaceId: string, element: any): void { + this._capture('onInsert', [element]); + } onRemove(namespaceId: string, element: any, domFn: () => any): void { this._capture('onRemove', [element]); diff --git a/packages/platform-browser/animations/test/browser_animation_builder_spec.ts b/packages/platform-browser/animations/test/browser_animation_builder_spec.ts index 38535fcda584e..a254629119fa6 100644 --- a/packages/platform-browser/animations/test/browser_animation_builder_spec.ts +++ b/packages/platform-browser/animations/test/browser_animation_builder_spec.ts @@ -5,11 +5,11 @@ * 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 {AnimationBuilder, animate, style} from '@angular/animations'; +import {animate, AnimationBuilder, style} from '@angular/animations'; import {AnimationDriver} from '@angular/animations/browser'; import {MockAnimationDriver} from '@angular/animations/browser/testing'; import {Component, ViewChild} from '@angular/core'; -import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing'; +import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing'; import {NoopAnimationsModule, ɵBrowserAnimationBuilder as BrowserAnimationBuilder} from '@angular/platform-browser/animations'; import {el} from '../../testing/src/browser_util'; diff --git a/packages/platform-browser/animations/test/noop_animations_module_spec.ts b/packages/platform-browser/animations/test/noop_animations_module_spec.ts index b71d58320e52d..3a8d24470669b 100644 --- a/packages/platform-browser/animations/test/noop_animations_module_spec.ts +++ b/packages/platform-browser/animations/test/noop_animations_module_spec.ts @@ -13,7 +13,9 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations'; { describe('NoopAnimationsModule', () => { - beforeEach(() => { TestBed.configureTestingModule({imports: [NoopAnimationsModule]}); }); + beforeEach(() => { + TestBed.configureTestingModule({imports: [NoopAnimationsModule]}); + }); it('should flush and fire callbacks when the zone becomes stable', (async) => { @Component({ @@ -29,8 +31,12 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations'; exp: any; startEvent: any; doneEvent: any; - onStart(event: any) { this.startEvent = event; } - onDone(event: any) { this.doneEvent = event; } + onStart(event: any) { + this.startEvent = event; + } + onDone(event: any) { + this.doneEvent = event; + } } TestBed.configureTestingModule({declarations: [Cmp]}); @@ -63,8 +69,12 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations'; exp: any; startEvent: any; doneEvent: any; - onStart(event: any) { this.startEvent = event; } - onDone(event: any) { this.doneEvent = event; } + onStart(event: any) { + this.startEvent = event; + } + onDone(event: any) { + this.doneEvent = event; + } } TestBed.configureTestingModule({declarations: [Cmp]}); diff --git a/packages/platform-browser/src/browser.ts b/packages/platform-browser/src/browser.ts index 44fad7407a3a7..52d601ac8a894 100644 --- a/packages/platform-browser/src/browser.ts +++ b/packages/platform-browser/src/browser.ts @@ -7,7 +7,8 @@ */ import {CommonModule, DOCUMENT, ɵPLATFORM_BROWSER_ID as PLATFORM_BROWSER_ID} from '@angular/common'; -import {APP_ID, ApplicationModule, ErrorHandler, Inject, ModuleWithProviders, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, RendererFactory2, Sanitizer, SkipSelf, StaticProvider, Testability, createPlatformFactory, platformCore, ɵConsole as Console, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵsetDocument} from '@angular/core'; +import {APP_ID, ApplicationModule, createPlatformFactory, ErrorHandler, Inject, ModuleWithProviders, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, platformCore, PlatformRef, RendererFactory2, Sanitizer, SkipSelf, StaticProvider, Testability, ɵConsole as Console, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵsetDocument} from '@angular/core'; + import {BrowserDomAdapter} from './browser/browser_adapter'; import {SERVER_TRANSITION_PROVIDERS, TRANSITION_ID} from './browser/server-transition'; import {BrowserGetTestability} from './browser/testability'; diff --git a/packages/platform-browser/src/browser/browser_adapter.ts b/packages/platform-browser/src/browser/browser_adapter.ts index ced19976b57fd..cb56e52db29bd 100644 --- a/packages/platform-browser/src/browser/browser_adapter.ts +++ b/packages/platform-browser/src/browser/browser_adapter.ts @@ -29,8 +29,12 @@ const nodeContains: (this: Node, other: Node) => boolean = (() => { */ /* tslint:disable:requireParameterType no-console */ export class BrowserDomAdapter extends GenericBrowserDomAdapter { - static makeCurrent() { setRootDomAdapter(new BrowserDomAdapter()); } - getProperty(el: Node, name: string): any { return (<any>el)[name]; } + static makeCurrent() { + setRootDomAdapter(new BrowserDomAdapter()); + } + getProperty(el: Node, name: string): any { + return (<any>el)[name]; + } log(error: string): void { if (window.console) { @@ -54,16 +58,22 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter { el.addEventListener(evt, listener, false); // Needed to follow Dart's subscription semantic, until fix of // https://code.google.com/p/dart/issues/detail?id=17406 - return () => { el.removeEventListener(evt, listener, false); }; + return () => { + el.removeEventListener(evt, listener, false); + }; + } + dispatchEvent(el: Node, evt: any) { + el.dispatchEvent(evt); } - dispatchEvent(el: Node, evt: any) { el.dispatchEvent(evt); } remove(node: Node): Node { if (node.parentNode) { node.parentNode.removeChild(node); } return node; } - getValue(el: any): string { return el.value; } + getValue(el: any): string { + return el.value; + } createElement(tagName: string, doc?: Document): HTMLElement { doc = doc || this.getDefaultDocument(); return doc.createElement(tagName); @@ -71,11 +81,17 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter { createHtmlDocument(): HTMLDocument { return document.implementation.createHTMLDocument('fakeTitle'); } - getDefaultDocument(): Document { return document; } + getDefaultDocument(): Document { + return document; + } - isElementNode(node: Node): boolean { return node.nodeType === Node.ELEMENT_NODE; } + isElementNode(node: Node): boolean { + return node.nodeType === Node.ELEMENT_NODE; + } - isShadowRoot(node: any): boolean { return node instanceof DocumentFragment; } + isShadowRoot(node: any): boolean { + return node instanceof DocumentFragment; + } getGlobalEventTarget(doc: Document, target: string): EventTarget|null { if (target === 'window') { @@ -89,14 +105,22 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter { } return null; } - getHistory(): History { return window.history; } - getLocation(): Location { return window.location; } + getHistory(): History { + return window.history; + } + getLocation(): Location { + return window.location; + } getBaseHref(doc: Document): string|null { const href = getBaseElementHref(); return href == null ? null : relativePath(href); } - resetBaseElement(): void { baseElement = null; } - getUserAgent(): string { return window.navigator.userAgent; } + resetBaseElement(): void { + baseElement = null; + } + getUserAgent(): string { + return window.navigator.userAgent; + } performanceNow(): number { // performance.now() is not available in all browsers, see // http://caniuse.com/#search=performance.now @@ -104,15 +128,19 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter { new Date().getTime(); } - supportsCookies(): boolean { return true; } + supportsCookies(): boolean { + return true; + } - getCookie(name: string): string|null { return parseCookieValue(document.cookie, name); } + getCookie(name: string): string|null { + return parseCookieValue(document.cookie, name); + } } let baseElement: HTMLElement|null = null; function getBaseElementHref(): string|null { if (!baseElement) { - baseElement = document.querySelector('base') !; + baseElement = document.querySelector('base')!; if (!baseElement) { return null; } diff --git a/packages/platform-browser/src/browser/generic_browser_adapter.ts b/packages/platform-browser/src/browser/generic_browser_adapter.ts index 174567359c354..cd4c956a4c036 100644 --- a/packages/platform-browser/src/browser/generic_browser_adapter.ts +++ b/packages/platform-browser/src/browser/generic_browser_adapter.ts @@ -17,7 +17,11 @@ import {ɵDomAdapter as DomAdapter} from '@angular/common'; * can introduce XSS risks. */ export abstract class GenericBrowserDomAdapter extends DomAdapter { - constructor() { super(); } + constructor() { + super(); + } - supportsDOMEvents(): boolean { return true; } + supportsDOMEvents(): boolean { + return true; + } } diff --git a/packages/platform-browser/src/browser/meta.ts b/packages/platform-browser/src/browser/meta.ts index 0962e4656ce35..a595f2cb92710 100644 --- a/packages/platform-browser/src/browser/meta.ts +++ b/packages/platform-browser/src/browser/meta.ts @@ -15,13 +15,16 @@ import {Inject, Injectable, ɵɵinject} from '@angular/core'; * @publicApi */ export type MetaDefinition = { - charset?: string; content?: string; httpEquiv?: string; id?: string; itemprop?: string; + charset?: string; + content?: string; + httpEquiv?: string; + id?: string; + itemprop?: string; name?: string; property?: string; scheme?: string; url?: string; -} & -{ +}&{ // TODO(IgorMinar): this type looks wrong [prop: string]: string; }; @@ -41,7 +44,9 @@ export function createMeta() { @Injectable({providedIn: 'root', useFactory: createMeta, deps: []}) export class Meta { private _dom: DomAdapter; - constructor(@Inject(DOCUMENT) private _doc: any) { this._dom = getDOM(); } + constructor(@Inject(DOCUMENT) private _doc: any) { + this._dom = getDOM(); + } addTag(tag: MetaDefinition, forceCreation: boolean = false): HTMLMetaElement|null { if (!tag) return null; @@ -72,14 +77,16 @@ export class Meta { updateTag(tag: MetaDefinition, selector?: string): HTMLMetaElement|null { if (!tag) return null; selector = selector || this._parseSelector(tag); - const meta: HTMLMetaElement = this.getTag(selector) !; + const meta: HTMLMetaElement = this.getTag(selector)!; if (meta) { return this._setMetaElementAttributes(tag, meta); } return this._getOrCreateElement(tag, true); } - removeTag(attrSelector: string): void { this.removeTagElement(this.getTag(attrSelector) !); } + removeTag(attrSelector: string): void { + this.removeTagElement(this.getTag(attrSelector)!); + } removeTagElement(meta: HTMLMetaElement): void { if (meta) { @@ -91,7 +98,7 @@ export class Meta { HTMLMetaElement { if (!forceCreation) { const selector: string = this._parseSelector(meta); - const elem: HTMLMetaElement = this.getTag(selector) !; + const elem: HTMLMetaElement = this.getTag(selector)!; // It's allowed to have multiple elements with the same name so it's not enough to // just check that element with the same name already present on the page. We also need to // check if element has tag attributes diff --git a/packages/platform-browser/src/browser/testability.ts b/packages/platform-browser/src/browser/testability.ts index bddcc9700d781..2592657566aaf 100644 --- a/packages/platform-browser/src/browser/testability.ts +++ b/packages/platform-browser/src/browser/testability.ts @@ -7,10 +7,12 @@ */ import {ɵgetDOM as getDOM} from '@angular/common'; -import {GetTestability, Testability, TestabilityRegistry, setTestabilityGetter, ɵglobal as global} from '@angular/core'; +import {GetTestability, setTestabilityGetter, Testability, TestabilityRegistry, ɵglobal as global} from '@angular/core'; export class BrowserGetTestability implements GetTestability { - static init() { setTestabilityGetter(new BrowserGetTestability()); } + static init() { + setTestabilityGetter(new BrowserGetTestability()); + } addToWindow(registry: TestabilityRegistry): void { global['getAngularTestability'] = (elem: any, findInAncestors: boolean = true) => { diff --git a/packages/platform-browser/src/browser/title.ts b/packages/platform-browser/src/browser/title.ts index 81ec5f29742c3..b262c08042c76 100644 --- a/packages/platform-browser/src/browser/title.ts +++ b/packages/platform-browser/src/browser/title.ts @@ -33,11 +33,15 @@ export class Title { /** * Get the title of the current HTML document. */ - getTitle(): string { return this._doc.title; } + getTitle(): string { + return this._doc.title; + } /** * Set the title of the current HTML document. * @param newTitle */ - setTitle(newTitle: string) { this._doc.title = newTitle || ''; } + setTitle(newTitle: string) { + this._doc.title = newTitle || ''; + } } diff --git a/packages/platform-browser/src/browser/tools/common_tools.ts b/packages/platform-browser/src/browser/tools/common_tools.ts index 37e12036f7a38..a4d0a62f9c178 100644 --- a/packages/platform-browser/src/browser/tools/common_tools.ts +++ b/packages/platform-browser/src/browser/tools/common_tools.ts @@ -21,7 +21,9 @@ export class ChangeDetectionPerfRecord { export class AngularProfiler { appRef: ApplicationRef; - constructor(ref: ComponentRef<any>) { this.appRef = ref.injector.get(ApplicationRef); } + constructor(ref: ComponentRef<any>) { + this.appRef = ref.injector.get(ApplicationRef); + } // tslint:disable:no-console /** diff --git a/packages/platform-browser/src/browser/transfer_state.ts b/packages/platform-browser/src/browser/transfer_state.ts index a9bf935df500a..f0bbe053e62bb 100644 --- a/packages/platform-browser/src/browser/transfer_state.ts +++ b/packages/platform-browser/src/browser/transfer_state.ts @@ -45,7 +45,7 @@ export function unescapeHtml(text: string): string { * * @publicApi */ -export type StateKey<T> = string & {__not_a_string: never}; +export type StateKey<T> = string&{__not_a_string: never}; /** * Create a `StateKey<T>` that can be used to store value of type T with `TransferState`. @@ -80,7 +80,7 @@ export function makeStateKey<T = void>(key: string): StateKey<T> { */ @Injectable() export class TransferState { - private store: {[k: string]: {} | undefined} = {}; + private store: {[k: string]: {}|undefined} = {}; private onSerializeCallbacks: {[k: string]: () => {} | undefined} = {}; /** @internal */ @@ -100,17 +100,23 @@ export class TransferState { /** * Set the value corresponding to a key. */ - set<T>(key: StateKey<T>, value: T): void { this.store[key] = value; } + set<T>(key: StateKey<T>, value: T): void { + this.store[key] = value; + } /** * Remove a key from the store. */ - remove<T>(key: StateKey<T>): void { delete this.store[key]; } + remove<T>(key: StateKey<T>): void { + delete this.store[key]; + } /** * Test whether a key exists in the store. */ - hasKey<T>(key: StateKey<T>) { return this.store.hasOwnProperty(key); } + hasKey<T>(key: StateKey<T>) { + return this.store.hasOwnProperty(key); + } /** * Register a callback to provide the value for a key when `toJson` is called. diff --git a/packages/platform-browser/src/dom/debug/by.ts b/packages/platform-browser/src/dom/debug/by.ts index d35e4055f64a3..f0799cd4f448c 100644 --- a/packages/platform-browser/src/dom/debug/by.ts +++ b/packages/platform-browser/src/dom/debug/by.ts @@ -25,7 +25,9 @@ export class By { * * {@example platform-browser/dom/debug/ts/by/by.ts region='by_all'} */ - static all(): Predicate<DebugNode> { return () => true; } + static all(): Predicate<DebugNode> { + return () => true; + } /** * Match elements by the given CSS selector. @@ -52,7 +54,7 @@ export class By { * {@example platform-browser/dom/debug/ts/by/by.ts region='by_directive'} */ static directive(type: Type<any>): Predicate<DebugNode> { - return (debugNode) => debugNode.providerTokens !.indexOf(type) !== -1; + return (debugNode) => debugNode.providerTokens!.indexOf(type) !== -1; } } diff --git a/packages/platform-browser/src/dom/dom_renderer.ts b/packages/platform-browser/src/dom/dom_renderer.ts index 80fc39d85a5de..9362a0881de60 100644 --- a/packages/platform-browser/src/dom/dom_renderer.ts +++ b/packages/platform-browser/src/dom/dom_renderer.ts @@ -137,11 +137,17 @@ class DefaultDomRenderer2 implements Renderer2 { return document.createElement(name); } - createComment(value: string): any { return document.createComment(value); } + createComment(value: string): any { + return document.createComment(value); + } - createText(value: string): any { return document.createTextNode(value); } + createText(value: string): any { + return document.createTextNode(value); + } - appendChild(parent: any, newChild: any): void { parent.appendChild(newChild); } + appendChild(parent: any, newChild: any): void { + parent.appendChild(newChild); + } insertBefore(parent: any, newChild: any, refChild: any): void { if (parent) { @@ -167,9 +173,13 @@ class DefaultDomRenderer2 implements Renderer2 { return el; } - parentNode(node: any): any { return node.parentNode; } + parentNode(node: any): any { + return node.parentNode; + } - nextSibling(node: any): any { return node.nextSibling; } + nextSibling(node: any): any { + return node.nextSibling; + } setAttribute(el: any, name: string, value: string, namespace?: string): void { if (namespace) { @@ -205,9 +215,13 @@ class DefaultDomRenderer2 implements Renderer2 { } } - addClass(el: any, name: string): void { el.classList.add(name); } + addClass(el: any, name: string): void { + el.classList.add(name); + } - removeClass(el: any, name: string): void { el.classList.remove(name); } + removeClass(el: any, name: string): void { + el.classList.remove(name); + } setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void { if (flags & RendererStyleFlags2.DashCase) { @@ -233,7 +247,9 @@ class DefaultDomRenderer2 implements Renderer2 { el[name] = value; } - setValue(node: any, value: string): void { node.nodeValue = value; } + setValue(node: any, value: string): void { + node.nodeValue = value; + } listen(target: 'window'|'document'|'body'|any, event: string, callback: (event: any) => boolean): () => void { @@ -243,15 +259,15 @@ class DefaultDomRenderer2 implements Renderer2 { target, event, decoratePreventDefault(callback)); } return <() => void>this.eventManager.addEventListener( - target, event, decoratePreventDefault(callback)) as() => void; + target, event, decoratePreventDefault(callback)) as () => void; } } const AT_CHARCODE = (() => '@'.charCodeAt(0))(); function checkNoSyntheticProp(name: string, nameKind: string) { if (name.charCodeAt(0) === AT_CHARCODE) { - throw new Error( - `Found the synthetic ${nameKind} ${name}. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.`); + throw new Error(`Found the synthetic ${nameKind} ${ + name}. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.`); } } @@ -270,7 +286,9 @@ class EmulatedEncapsulationDomRenderer2 extends DefaultDomRenderer2 { this.hostAttr = shimHostAttribute(appId + '-' + component.id); } - applyToHost(element: any) { super.setAttribute(element, this.hostAttr, ''); } + applyToHost(element: any) { + super.setAttribute(element, this.hostAttr, ''); + } createElement(parent: any, name: string): Element { const el = super.createElement(parent, name); @@ -300,9 +318,13 @@ class ShadowDomRenderer extends DefaultDomRenderer2 { } } - private nodeOrShadowRoot(node: any): any { return node === this.hostEl ? this.shadowRoot : node; } + private nodeOrShadowRoot(node: any): any { + return node === this.hostEl ? this.shadowRoot : node; + } - destroy() { this.sharedStylesHost.removeHost(this.shadowRoot); } + destroy() { + this.sharedStylesHost.removeHost(this.shadowRoot); + } appendChild(parent: any, newChild: any): void { return super.appendChild(this.nodeOrShadowRoot(parent), newChild); diff --git a/packages/platform-browser/src/dom/events/dom_events.ts b/packages/platform-browser/src/dom/events/dom_events.ts index d080fba80ea19..77152c4e7d800 100644 --- a/packages/platform-browser/src/dom/events/dom_events.ts +++ b/packages/platform-browser/src/dom/events/dom_events.ts @@ -13,11 +13,15 @@ import {EventManagerPlugin} from './event_manager'; @Injectable() export class DomEventsPlugin extends EventManagerPlugin { - constructor(@Inject(DOCUMENT) doc: any) { super(doc); } + constructor(@Inject(DOCUMENT) doc: any) { + super(doc); + } // This plugin should come last in the list of plugins, because it accepts all // events. - supports(eventName: string): boolean { return true; } + supports(eventName: string): boolean { + return true; + } addEventListener(element: HTMLElement, eventName: string, handler: Function): Function { element.addEventListener(eventName, handler as EventListener, false); diff --git a/packages/platform-browser/src/dom/events/event_manager.ts b/packages/platform-browser/src/dom/events/event_manager.ts index 460f19110cab6..0a69b867cb05f 100644 --- a/packages/platform-browser/src/dom/events/event_manager.ts +++ b/packages/platform-browser/src/dom/events/event_manager.ts @@ -67,7 +67,9 @@ export class EventManager { /** * Retrieves the compilation zone in which event listeners are registered. */ - getZone(): NgZone { return this._zone; } + getZone(): NgZone { + return this._zone; + } /** @internal */ _findPluginFor(eventName: string): EventManagerPlugin { @@ -92,7 +94,7 @@ export abstract class EventManagerPlugin { constructor(private _doc: any) {} // TODO(issue/24571): remove '!'. - manager !: EventManager; + manager!: EventManager; abstract supports(eventName: string): boolean; diff --git a/packages/platform-browser/src/dom/events/hammer_gestures.ts b/packages/platform-browser/src/dom/events/hammer_gestures.ts index 27f7e961fb4a1..dcfb894003ddd 100644 --- a/packages/platform-browser/src/dom/events/hammer_gestures.ts +++ b/packages/platform-browser/src/dom/events/hammer_gestures.ts @@ -99,21 +99,21 @@ export class HammerGestureConfig { events: string[] = []; /** - * Maps gesture event names to a set of configuration options - * that specify overrides to the default values for specific properties. - * - * The key is a supported event name to be configured, - * and the options object contains a set of properties, with override values - * to be applied to the named recognizer event. - * For example, to disable recognition of the rotate event, specify - * `{"rotate": {"enable": false}}`. - * - * Properties that are not present take the HammerJS default values. - * For information about which properties are supported for which events, - * and their allowed and default values, see - * [HammerJS documentation](http://hammerjs.github.io/). - * - */ + * Maps gesture event names to a set of configuration options + * that specify overrides to the default values for specific properties. + * + * The key is a supported event name to be configured, + * and the options object contains a set of properties, with override values + * to be applied to the named recognizer event. + * For example, to disable recognition of the rotate event, specify + * `{"rotate": {"enable": false}}`. + * + * Properties that are not present take the HammerJS default values. + * For information about which properties are supported for which events, + * and their allowed and default values, see + * [HammerJS documentation](http://hammerjs.github.io/). + * + */ overrides: {[key: string]: Object} = {}; /** @@ -124,7 +124,9 @@ export class HammerGestureConfig { * [HammerJS documentation](http://hammerjs.github.io/). */ options?: { - cssProps?: any; domEvents?: boolean; enable?: boolean | ((manager: any) => boolean); + cssProps?: any; + domEvents?: boolean; + enable?: boolean | ((manager: any) => boolean); preset?: any[]; touchAction?: string; recognizers?: any[]; @@ -139,7 +141,7 @@ export class HammerGestureConfig { * @returns A HammerJS event-manager object. */ buildHammer(element: HTMLElement): HammerInstance { - const mc = new Hammer !(element, this.options); + const mc = new Hammer!(element, this.options); mc.get('pinch').set({enable: true}); mc.get('rotate').set({enable: true}); @@ -192,7 +194,9 @@ export class HammerGesturesPlugin extends EventManagerPlugin { // Until Hammer is loaded, the returned function needs to *cancel* the registration rather // than remove anything. let cancelRegistration = false; - let deregister: Function = () => { cancelRegistration = true; }; + let deregister: Function = () => { + cancelRegistration = true; + }; this.loader() .then(() => { @@ -220,14 +224,18 @@ export class HammerGesturesPlugin extends EventManagerPlugin { // Return a function that *executes* `deregister` (and not `deregister` itself) so that we // can change the behavior of `deregister` once the listener is added. Using a closure in // this way allows us to avoid any additional data structures to track listener removal. - return () => { deregister(); }; + return () => { + deregister(); + }; } return zone.runOutsideAngular(() => { // Creating the manager bind events, must be done outside of angular const mc = this._config.buildHammer(element); const callback = function(eventObj: HammerInput) { - zone.runGuarded(function() { handler(eventObj); }); + zone.runGuarded(function() { + handler(eventObj); + }); }; mc.on(eventName, callback); return () => { @@ -240,7 +248,9 @@ export class HammerGesturesPlugin extends EventManagerPlugin { }); } - isCustomEvent(eventName: string): boolean { return this._config.events.indexOf(eventName) > -1; } + isCustomEvent(eventName: string): boolean { + return this._config.events.indexOf(eventName) > -1; + } } /** diff --git a/packages/platform-browser/src/dom/events/key_events.ts b/packages/platform-browser/src/dom/events/key_events.ts index e7516ae591a3c..151b6e289ace2 100644 --- a/packages/platform-browser/src/dom/events/key_events.ts +++ b/packages/platform-browser/src/dom/events/key_events.ts @@ -79,14 +79,18 @@ export class KeyEventsPlugin extends EventManagerPlugin { * Initializes an instance of the browser plug-in. * @param doc The document in which key events will be detected. */ - constructor(@Inject(DOCUMENT) doc: any) { super(doc); } + constructor(@Inject(DOCUMENT) doc: any) { + super(doc); + } /** - * Reports whether a named key event is supported. - * @param eventName The event name to query. - * @return True if the named key event is supported. + * Reports whether a named key event is supported. + * @param eventName The event name to query. + * @return True if the named key event is supported. */ - supports(eventName: string): boolean { return KeyEventsPlugin.parseEventName(eventName) != null; } + supports(eventName: string): boolean { + return KeyEventsPlugin.parseEventName(eventName) != null; + } /** * Registers a handler for a specific element and key event. @@ -95,9 +99,9 @@ export class KeyEventsPlugin extends EventManagerPlugin { * @param handler A function to call when the notification occurs. Receives the * event object as an argument. * @returns The key event that was registered. - */ + */ addEventListener(element: HTMLElement, eventName: string, handler: Function): Function { - const parsedEvent = KeyEventsPlugin.parseEventName(eventName) !; + const parsedEvent = KeyEventsPlugin.parseEventName(eventName)!; const outsideHandler = KeyEventsPlugin.eventCallback(parsedEvent['fullKey'], handler, this.manager.getZone()); @@ -115,7 +119,7 @@ export class KeyEventsPlugin extends EventManagerPlugin { return null; } - const key = KeyEventsPlugin._normalizeKey(parts.pop() !); + const key = KeyEventsPlugin._normalizeKey(parts.pop()!); let fullKey = ''; MODIFIER_KEYS.forEach(modifierName => { diff --git a/packages/platform-browser/src/dom/shared_styles_host.ts b/packages/platform-browser/src/dom/shared_styles_host.ts index 54745b63b6ba3..e53bc046469e2 100644 --- a/packages/platform-browser/src/dom/shared_styles_host.ts +++ b/packages/platform-browser/src/dom/shared_styles_host.ts @@ -27,7 +27,9 @@ export class SharedStylesHost { onStylesAdded(additions: Set<string>): void {} - getAllStyles(): string[] { return Array.from(this._stylesSet); } + getAllStyles(): string[] { + return Array.from(this._stylesSet); + } } @Injectable() @@ -52,11 +54,15 @@ export class DomSharedStylesHost extends SharedStylesHost implements OnDestroy { this._hostNodes.add(hostNode); } - removeHost(hostNode: Node): void { this._hostNodes.delete(hostNode); } + removeHost(hostNode: Node): void { + this._hostNodes.delete(hostNode); + } onStylesAdded(additions: Set<string>): void { this._hostNodes.forEach(hostNode => this._addStylesToHost(additions, hostNode)); } - ngOnDestroy(): void { this._styleNodes.forEach(styleNode => getDOM().remove(styleNode)); } + ngOnDestroy(): void { + this._styleNodes.forEach(styleNode => getDOM().remove(styleNode)); + } } diff --git a/packages/platform-browser/src/dom/util.ts b/packages/platform-browser/src/dom/util.ts index 7f5b18cb6a7e0..2cc75227befac 100644 --- a/packages/platform-browser/src/dom/util.ts +++ b/packages/platform-browser/src/dom/util.ts @@ -33,7 +33,7 @@ export function exportNgVar(name: string, value: any): void { // - closure declares globals itself for minified names, which sometimes clobber our `ng` global // - we can't declare a closure extern as the namespace `ng` is already used within Google // for typings for angularJS (via `goog.provide('ng....')`). - const ng = global['ng'] = (global['ng'] as{[key: string]: any} | undefined) || {}; + const ng = global['ng'] = (global['ng'] as {[key: string]: any} | undefined) || {}; ng[name] = value; } } diff --git a/packages/platform-browser/src/platform-browser.ts b/packages/platform-browser/src/platform-browser.ts index 5c3d5ac11fe4d..3f072b3fb1d88 100644 --- a/packages/platform-browser/src/platform-browser.ts +++ b/packages/platform-browser/src/platform-browser.ts @@ -10,7 +10,7 @@ export {BrowserModule, platformBrowser} from './browser'; export {Meta, MetaDefinition} from './browser/meta'; export {Title} from './browser/title'; export {disableDebugTools, enableDebugTools} from './browser/tools/tools'; -export {BrowserTransferStateModule, StateKey, TransferState, makeStateKey} from './browser/transfer_state'; +export {BrowserTransferStateModule, makeStateKey, StateKey, TransferState} from './browser/transfer_state'; export {By} from './dom/debug/by'; export {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager'; export {HAMMER_GESTURE_CONFIG, HAMMER_LOADER, HAMMER_PROVIDERS__POST_R3__ as ɵHAMMER_PROVIDERS__POST_R3__, HammerGestureConfig, HammerLoader, HammerModule} from './dom/events/hammer_gestures'; diff --git a/packages/platform-browser/src/private_export.ts b/packages/platform-browser/src/private_export.ts index f6159130c3e23..c37af45559035 100644 --- a/packages/platform-browser/src/private_export.ts +++ b/packages/platform-browser/src/private_export.ts @@ -7,13 +7,13 @@ */ export {ɵgetDOM} from '@angular/common'; -export {BROWSER_SANITIZATION_PROVIDERS as ɵBROWSER_SANITIZATION_PROVIDERS, BROWSER_SANITIZATION_PROVIDERS__POST_R3__ as ɵBROWSER_SANITIZATION_PROVIDERS__POST_R3__, INTERNAL_BROWSER_PLATFORM_PROVIDERS as ɵINTERNAL_BROWSER_PLATFORM_PROVIDERS, initDomAdapter as ɵinitDomAdapter} from './browser'; +export {BROWSER_SANITIZATION_PROVIDERS as ɵBROWSER_SANITIZATION_PROVIDERS, BROWSER_SANITIZATION_PROVIDERS__POST_R3__ as ɵBROWSER_SANITIZATION_PROVIDERS__POST_R3__, initDomAdapter as ɵinitDomAdapter, INTERNAL_BROWSER_PLATFORM_PROVIDERS as ɵINTERNAL_BROWSER_PLATFORM_PROVIDERS} from './browser'; export {BrowserDomAdapter as ɵBrowserDomAdapter} from './browser/browser_adapter'; export {TRANSITION_ID as ɵTRANSITION_ID} from './browser/server-transition'; export {BrowserGetTestability as ɵBrowserGetTestability} from './browser/testability'; export {escapeHtml as ɵescapeHtml} from './browser/transfer_state'; export {ELEMENT_PROBE_PROVIDERS as ɵELEMENT_PROBE_PROVIDERS} from './dom/debug/ng_probe'; -export {DomRendererFactory2 as ɵDomRendererFactory2, NAMESPACE_URIS as ɵNAMESPACE_URIS, flattenStyles as ɵflattenStyles, shimContentAttribute as ɵshimContentAttribute, shimHostAttribute as ɵshimHostAttribute} from './dom/dom_renderer'; +export {DomRendererFactory2 as ɵDomRendererFactory2, flattenStyles as ɵflattenStyles, NAMESPACE_URIS as ɵNAMESPACE_URIS, shimContentAttribute as ɵshimContentAttribute, shimHostAttribute as ɵshimHostAttribute} from './dom/dom_renderer'; export {DomEventsPlugin as ɵDomEventsPlugin} from './dom/events/dom_events'; export {HammerGesturesPlugin as ɵHammerGesturesPlugin} from './dom/events/hammer_gestures'; export {KeyEventsPlugin as ɵKeyEventsPlugin} from './dom/events/key_events'; diff --git a/packages/platform-browser/src/security/dom_sanitization_service.ts b/packages/platform-browser/src/security/dom_sanitization_service.ts index c448588b340dd..4bb39ff56efb8 100644 --- a/packages/platform-browser/src/security/dom_sanitization_service.ts +++ b/packages/platform-browser/src/security/dom_sanitization_service.ts @@ -7,7 +7,7 @@ */ import {DOCUMENT} from '@angular/common'; -import {Inject, Injectable, Injector, Sanitizer, SecurityContext, forwardRef, ɵBypassType as BypassType, ɵ_sanitizeHtml as _sanitizeHtml, ɵ_sanitizeStyle as _sanitizeStyle, ɵ_sanitizeUrl as _sanitizeUrl, ɵallowSanitizationBypassAndThrow as allowSanitizationBypassOrThrow, ɵbypassSanitizationTrustHtml as bypassSanitizationTrustHtml, ɵbypassSanitizationTrustResourceUrl as bypassSanitizationTrustResourceUrl, ɵbypassSanitizationTrustScript as bypassSanitizationTrustScript, ɵbypassSanitizationTrustStyle as bypassSanitizationTrustStyle, ɵbypassSanitizationTrustUrl as bypassSanitizationTrustUrl, ɵgetSanitizationBypassType as getSanitizationBypassType, ɵunwrapSafeValue as unwrapSafeValue} from '@angular/core'; +import {forwardRef, Inject, Injectable, Injector, Sanitizer, SecurityContext, ɵ_sanitizeHtml as _sanitizeHtml, ɵ_sanitizeStyle as _sanitizeStyle, ɵ_sanitizeUrl as _sanitizeUrl, ɵallowSanitizationBypassAndThrow as allowSanitizationBypassOrThrow, ɵbypassSanitizationTrustHtml as bypassSanitizationTrustHtml, ɵbypassSanitizationTrustResourceUrl as bypassSanitizationTrustResourceUrl, ɵbypassSanitizationTrustScript as bypassSanitizationTrustScript, ɵbypassSanitizationTrustStyle as bypassSanitizationTrustStyle, ɵbypassSanitizationTrustUrl as bypassSanitizationTrustUrl, ɵBypassType as BypassType, ɵgetSanitizationBypassType as getSanitizationBypassType, ɵunwrapSafeValue as unwrapSafeValue} from '@angular/core'; export {SecurityContext}; @@ -149,7 +149,9 @@ export function domSanitizerImplFactory(injector: Injector) { @Injectable({providedIn: 'root', useFactory: domSanitizerImplFactory, deps: [Injector]}) export class DomSanitizerImpl extends DomSanitizer { - constructor(@Inject(DOCUMENT) private _doc: any) { super(); } + constructor(@Inject(DOCUMENT) private _doc: any) { + super(); + } sanitize(ctx: SecurityContext, value: SafeValue|string|null): string|null { if (value == null) return null; @@ -188,12 +190,18 @@ export class DomSanitizerImpl extends DomSanitizer { } } - bypassSecurityTrustHtml(value: string): SafeHtml { return bypassSanitizationTrustHtml(value); } - bypassSecurityTrustStyle(value: string): SafeStyle { return bypassSanitizationTrustStyle(value); } + bypassSecurityTrustHtml(value: string): SafeHtml { + return bypassSanitizationTrustHtml(value); + } + bypassSecurityTrustStyle(value: string): SafeStyle { + return bypassSanitizationTrustStyle(value); + } bypassSecurityTrustScript(value: string): SafeScript { return bypassSanitizationTrustScript(value); } - bypassSecurityTrustUrl(value: string): SafeUrl { return bypassSanitizationTrustUrl(value); } + bypassSecurityTrustUrl(value: string): SafeUrl { + return bypassSanitizationTrustUrl(value); + } bypassSecurityTrustResourceUrl(value: string): SafeResourceUrl { return bypassSanitizationTrustResourceUrl(value); } diff --git a/packages/platform-browser/test/BUILD.bazel b/packages/platform-browser/test/BUILD.bazel index 901e3f6c94f4e..0f518dcf68b36 100644 --- a/packages/platform-browser/test/BUILD.bazel +++ b/packages/platform-browser/test/BUILD.bazel @@ -53,6 +53,17 @@ karma_web_test_suite( static_files = [ ":static_assets/test.html", ], + tags = [ + # disabled on 2020-04-14 due to failure on saucelabs monitor job + # https://app.circleci.com/pipelines/github/angular/angular/13320/workflows/9ca3527a-d448-4a64-880a-fb4de9d1fece/jobs/680645 + # ``` + # Chrome 73.0.3683 (Windows 7.0.0) public testing API using the test injector with modules components with template url should allow to createSync components with templateUrl after explicit async compilation FAILED + # Error: Component 'CompWithUrlTemplate' is not resolved: + # IE 10.0.0 (Windows 8.0.0) ERROR: 'Unhandled Promise rejection:', 'Failed to load ./sometemplate.html', '; Zone:', 'ProxyZone', '; Task:', 'Promise.then', '; Value:', 'Failed to load ./sometemplate.html', undefined + # Chrome Mobile 74.0.3729 (Android 0.0.0) ERROR: 'Unhandled Promise rejection:', 'Failed to load ./sometemplate.html', '; Zone:', 'ProxyZone', '; Task:', 'Promise.then', '; Value:', 'Failed to load ./sometemplate.html', undefined + # ``` + "fixme-saucelabs-ivy", + ], deps = [ ":test_lib", ], diff --git a/packages/platform-browser/test/browser/bootstrap_spec.ts b/packages/platform-browser/test/browser/bootstrap_spec.ts index 0fbf42f4c6852..f7ecc854139ae 100644 --- a/packages/platform-browser/test/browser/bootstrap_spec.ts +++ b/packages/platform-browser/test/browser/bootstrap_spec.ts @@ -7,12 +7,12 @@ */ import {DOCUMENT, isPlatformBrowser, ɵgetDOM as getDOM} from '@angular/common'; -import {APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, Directive, ErrorHandler, Inject, Injector, Input, LOCALE_ID, NgModule, OnDestroy, PLATFORM_ID, PLATFORM_INITIALIZER, Pipe, Provider, Sanitizer, StaticProvider, Type, VERSION, createPlatformFactory} from '@angular/core'; +import {APP_INITIALIZER, Compiler, Component, createPlatformFactory, CUSTOM_ELEMENTS_SCHEMA, Directive, ErrorHandler, Inject, Injector, Input, LOCALE_ID, NgModule, OnDestroy, Pipe, PLATFORM_ID, PLATFORM_INITIALIZER, Provider, Sanitizer, StaticProvider, Type, VERSION} from '@angular/core'; import {ApplicationRef, destroyPlatform} from '@angular/core/src/application_ref'; import {Console} from '@angular/core/src/console'; import {ComponentRef} from '@angular/core/src/linker/component_factory'; import {Testability, TestabilityRegistry} from '@angular/core/src/testability/testability'; -import {AsyncTestCompleter, Log, afterEach, beforeEach, beforeEachProviders, describe, inject, it} from '@angular/core/testing/src/testing_internal'; +import {afterEach, AsyncTestCompleter, beforeEach, beforeEachProviders, describe, inject, it, Log} from '@angular/core/testing/src/testing_internal'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -25,7 +25,9 @@ class NonExistentComp { @Component({selector: 'hello-app', template: '{{greeting}} world!'}) class HelloRootCmp { greeting: string; - constructor() { this.greeting = 'hello'; } + constructor() { + this.greeting = 'hello'; + } } @Component({selector: 'hello-app', template: 'before: <ng-content></ng-content> after: done'}) @@ -36,7 +38,9 @@ class HelloRootCmpContent { @Component({selector: 'hello-app-2', template: '{{greeting}} world, again!'}) class HelloRootCmp2 { greeting: string; - constructor() { this.greeting = 'hello'; } + constructor() { + this.greeting = 'hello'; + } } @Component({selector: 'hello-app', template: ''}) @@ -52,7 +56,9 @@ class HelloRootCmp3 { class HelloRootCmp4 { appRef: any /** TODO #9100 */; - constructor(@Inject(ApplicationRef) appRef: ApplicationRef) { this.appRef = appRef; } + constructor(@Inject(ApplicationRef) appRef: ApplicationRef) { + this.appRef = appRef; + } } @Component({selector: 'hello-app'}) @@ -66,9 +72,13 @@ class HelloRootDirectiveIsNotCmp { @Component({selector: 'hello-app', template: ''}) class HelloOnDestroyTickCmp implements OnDestroy { appRef: ApplicationRef; - constructor(@Inject(ApplicationRef) appRef: ApplicationRef) { this.appRef = appRef; } + constructor(@Inject(ApplicationRef) appRef: ApplicationRef) { + this.appRef = appRef; + } - ngOnDestroy(): void { this.appRef.tick(); } + ngOnDestroy(): void { + this.appRef.tick(); + } } @Component({selector: 'hello-app', templateUrl: './sometemplate.html'}) @@ -79,13 +89,14 @@ class HelloUrlCmp { @Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) class SomeDirective { // TODO(issue/24571): remove '!'. - @Input() - someDir !: string; + @Input() someDir!: string; } @Pipe({name: 'somePipe'}) class SomePipe { - transform(value: string): any { return `transformed ${value}`; } + transform(value: string): any { + return `transformed ${value}`; + } } @Component({selector: 'hello-app', template: `<div [someDir]="'someValue' | somePipe"></div>`}) @@ -99,7 +110,9 @@ class HelloCmpUsingCustomElement { class MockConsole { res: any[][] = []; - error(...s: any[]): void { this.res.push(s); } + error(...s: any[]): void { + this.res.push(s); + } } @@ -107,7 +120,9 @@ class DummyConsole implements Console { public warnings: string[] = []; log(message: string) {} - warn(message: string) { this.warnings.push(message); } + warn(message: string) { + this.warnings.push(message); + } } @@ -135,7 +150,9 @@ function bootstrap( if (isNode) return; let compilerConsole: DummyConsole; - beforeEachProviders(() => { return [Log]; }); + beforeEachProviders(() => { + return [Log]; + }); beforeEach(inject([DOCUMENT], (doc: any) => { destroyPlatform(); @@ -419,7 +436,6 @@ function bootstrap( it('should remove styles when transitioning from a server render', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - @Component({ selector: 'root', template: 'root', @@ -449,8 +465,9 @@ function bootstrap( platform.bootstrapModule(TestModule).then(() => { const styles: HTMLElement[] = Array.prototype.slice.apply(document.getElementsByTagName('style') || []); - styles.forEach( - style => { expect(style.getAttribute('ng-transition')).not.toBe('my-app'); }); + styles.forEach(style => { + expect(style.getAttribute('ng-transition')).not.toBe('my-app'); + }); async.done(); }); })); @@ -487,7 +504,9 @@ function bootstrap( }) class CompA { title: string = ''; - ngDoCheck() { log.push('CompA:ngDoCheck'); } + ngDoCheck() { + log.push('CompA:ngDoCheck'); + } onClick() { this.title = 'CompA'; log.push('CompA:onClick'); @@ -500,7 +519,9 @@ function bootstrap( }) class CompB { title: string = ''; - ngDoCheck() { log.push('CompB:ngDoCheck'); } + ngDoCheck() { + log.push('CompB:ngDoCheck'); + } onClick() { this.title = 'CompB'; log.push('CompB:onClick'); diff --git a/packages/platform-browser/test/browser/meta_spec.ts b/packages/platform-browser/test/browser/meta_spec.ts index 41d59fcd3df99..a959a4134c567 100644 --- a/packages/platform-browser/test/browser/meta_spec.ts +++ b/packages/platform-browser/test/browser/meta_spec.ts @@ -30,14 +30,14 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; afterEach(() => getDOM().remove(defaultMeta)); it('should return meta tag matching selector', () => { - const actual: HTMLMetaElement = metaService.getTag('property="fb:app_id"') !; + const actual: HTMLMetaElement = metaService.getTag('property="fb:app_id"')!; expect(actual).not.toBeNull(); expect(actual.getAttribute('content')).toEqual('123456789'); }); it('should return all meta tags matching selector', () => { - const tag1 = metaService.addTag({name: 'author', content: 'page author'}) !; - const tag2 = metaService.addTag({name: 'author', content: 'another page author'}) !; + const tag1 = metaService.addTag({name: 'author', content: 'page author'})!; + const tag2 = metaService.addTag({name: 'author', content: 'another page author'})!; const actual: HTMLMetaElement[] = metaService.getTags('name=author'); expect(actual.length).toEqual(2); @@ -50,7 +50,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); it('should return null if meta tag does not exist', () => { - const actual: HTMLMetaElement = metaService.getTag('fake=fake') !; + const actual: HTMLMetaElement = metaService.getTag('fake=fake')!; expect(actual).toBeNull(); }); @@ -73,7 +73,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; metaService.addTags([{name: 'keywords', content: 'meta test'}]); - const meta = metaService.getTag(selector) !; + const meta = metaService.getTag(selector)!; expect(meta).not.toBeNull(); metaService.removeTagElement(meta); @@ -87,7 +87,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; const actual = metaService.getTag(selector); expect(actual).not.toBeNull(); - expect(actual !.getAttribute('content')).toEqual('4321'); + expect(actual!.getAttribute('content')).toEqual('4321'); }); it('should extract selector from the tag definition', () => { @@ -96,7 +96,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; const actual = metaService.getTag(selector); expect(actual).not.toBeNull(); - expect(actual !.getAttribute('content')).toEqual('666'); + expect(actual!.getAttribute('content')).toEqual('666'); }); it('should create meta tag if it does not exist', () => { @@ -104,7 +104,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; metaService.updateTag({name: 'twitter:title', content: 'Content Title'}, selector); - const actual = metaService.getTag(selector) !; + const actual = metaService.getTag(selector)!; expect(actual).not.toBeNull(); expect(actual.getAttribute('content')).toEqual('Content Title'); @@ -118,7 +118,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; metaService.addTag({name: 'og:title', content: 'Content Title'}); - const actual = metaService.getTag(selector) !; + const actual = metaService.getTag(selector)!; expect(actual).not.toBeNull(); expect(actual.getAttribute('content')).toEqual('Content Title'); @@ -136,8 +136,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; {name: 'twitter:title', content: 'Content Title'}, {property: 'og:title', content: 'Content Title'} ]); - const twitterMeta = metaService.getTag(nameSelector) !; - const fbMeta = metaService.getTag(propertySelector) !; + const twitterMeta = metaService.getTag(nameSelector)!; + const fbMeta = metaService.getTag(propertySelector)!; expect(twitterMeta).not.toBeNull(); expect(fbMeta).not.toBeNull(); @@ -160,7 +160,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; const selector = 'property="fb:app_id"'; expect(metaService.getTags(selector).length).toEqual(1); - const meta = metaService.addTag({property: 'fb:app_id', content: '666'}) !; + const meta = metaService.addTag({property: 'fb:app_id', content: '666'})!; expect(metaService.getTags(selector).length).toEqual(2); @@ -172,18 +172,16 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; const selector = 'property="fb:app_id"'; expect(metaService.getTags(selector).length).toEqual(1); - const meta = metaService.addTag({property: 'fb:app_id', content: '123456789'}, true) !; + const meta = metaService.addTag({property: 'fb:app_id', content: '123456789'}, true)!; expect(metaService.getTags(selector).length).toEqual(2); // clean up metaService.removeTagElement(meta); }); - }); describe('integration test', () => { - @Injectable() class DependsOnMeta { constructor(public meta: Meta) {} diff --git a/packages/platform-browser/test/browser/title_spec.ts b/packages/platform-browser/test/browser/title_spec.ts index e99244ab7304d..4f885fc27873e 100644 --- a/packages/platform-browser/test/browser/title_spec.ts +++ b/packages/platform-browser/test/browser/title_spec.ts @@ -24,10 +24,13 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; titleService = new Title(doc); }); - afterEach(() => { doc.title = initialTitle; }); + afterEach(() => { + doc.title = initialTitle; + }); - it('should allow reading initial title', - () => { expect(titleService.getTitle()).toEqual(initialTitle); }); + it('should allow reading initial title', () => { + expect(titleService.getTitle()).toEqual(initialTitle); + }); it('should set a title on the injected document', () => { titleService.setTitle('test title'); @@ -36,13 +39,12 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); it('should reset title to empty string if title not provided', () => { - titleService.setTitle(null !); + titleService.setTitle(null!); expect(doc.title).toEqual(''); }); }); describe('integration test', () => { - @Injectable() class DependsOnTitle { constructor(public title: Title) {} @@ -55,7 +57,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; }); }); - it('should inject Title service when using BrowserModule', - () => { expect(TestBed.inject(DependsOnTitle).title).toBeAnInstanceOf(Title); }); + it('should inject Title service when using BrowserModule', () => { + expect(TestBed.inject(DependsOnTitle).title).toBeAnInstanceOf(Title); + }); }); } diff --git a/packages/platform-browser/test/browser/tools/spies.ts b/packages/platform-browser/test/browser/tools/spies.ts index 40f3bdce9e981..84bd9a8e29634 100644 --- a/packages/platform-browser/test/browser/tools/spies.ts +++ b/packages/platform-browser/test/browser/tools/spies.ts @@ -11,7 +11,9 @@ import {ApplicationRef} from '@angular/core/src/application_ref'; import {SpyObject} from '@angular/core/testing/src/testing_internal'; export class SpyApplicationRef extends SpyObject { - constructor() { super(ApplicationRef); } + constructor() { + super(ApplicationRef); + } } export class SpyComponentRef extends SpyObject { diff --git a/packages/platform-browser/test/browser/tools/tools_spec.ts b/packages/platform-browser/test/browser/tools/tools_spec.ts index e66f217d4585d..f747f987c6379 100644 --- a/packages/platform-browser/test/browser/tools/tools_spec.ts +++ b/packages/platform-browser/test/browser/tools/tools_spec.ts @@ -8,18 +8,25 @@ import {disableDebugTools, enableDebugTools} from '@angular/platform-browser'; -import {SpyComponentRef, callNgProfilerTimeChangeDetection} from './spies'; +import {callNgProfilerTimeChangeDetection, SpyComponentRef} from './spies'; { describe('profiler', () => { if (isNode) return; - beforeEach(() => { enableDebugTools((<any>new SpyComponentRef())); }); + beforeEach(() => { + enableDebugTools((<any>new SpyComponentRef())); + }); - afterEach(() => { disableDebugTools(); }); + afterEach(() => { + disableDebugTools(); + }); - it('should time change detection', () => { callNgProfilerTimeChangeDetection(); }); + it('should time change detection', () => { + callNgProfilerTimeChangeDetection(); + }); - it('should time change detection with recording', - () => { callNgProfilerTimeChangeDetection({'record': true}); }); + it('should time change detection with recording', () => { + callNgProfilerTimeChangeDetection({'record': true}); + }); }); } diff --git a/packages/platform-browser/test/browser/transfer_state_spec.ts b/packages/platform-browser/test/browser/transfer_state_spec.ts index f2a3fa0ed4e00..56cb3e9dddd19 100644 --- a/packages/platform-browser/test/browser/transfer_state_spec.ts +++ b/packages/platform-browser/test/browser/transfer_state_spec.ts @@ -9,124 +9,126 @@ import {DOCUMENT} from '@angular/common'; import {TestBed} from '@angular/core/testing'; import {BrowserModule, BrowserTransferStateModule, TransferState} from '@angular/platform-browser'; -import {StateKey, escapeHtml, makeStateKey, unescapeHtml} from '@angular/platform-browser/src/browser/transfer_state'; +import {escapeHtml, makeStateKey, StateKey, unescapeHtml} from '@angular/platform-browser/src/browser/transfer_state'; (function() { - function removeScriptTag(doc: Document, id: string) { - const existing = doc.getElementById(id); - if (existing) { - doc.body.removeChild(existing); - } +function removeScriptTag(doc: Document, id: string) { + const existing = doc.getElementById(id); + if (existing) { + doc.body.removeChild(existing); } - - function addScriptTag(doc: Document, appId: string, data: {}) { - const script = doc.createElement('script'); - const id = appId + '-state'; - script.id = id; - script.setAttribute('type', 'application/json'); - script.textContent = escapeHtml(JSON.stringify(data)); - - // Remove any stale script tags. - removeScriptTag(doc, id); - - doc.body.appendChild(script); - } - - describe('TransferState', () => { - const APP_ID = 'test-app'; - let doc: Document; - - const TEST_KEY = makeStateKey<number>('test'); - const DELAYED_KEY = makeStateKey<string>('delayed'); - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - BrowserModule.withServerTransition({appId: APP_ID}), - BrowserTransferStateModule, - ] - }); - doc = TestBed.inject(DOCUMENT); +} + +function addScriptTag(doc: Document, appId: string, data: {}) { + const script = doc.createElement('script'); + const id = appId + '-state'; + script.id = id; + script.setAttribute('type', 'application/json'); + script.textContent = escapeHtml(JSON.stringify(data)); + + // Remove any stale script tags. + removeScriptTag(doc, id); + + doc.body.appendChild(script); +} + +describe('TransferState', () => { + const APP_ID = 'test-app'; + let doc: Document; + + const TEST_KEY = makeStateKey<number>('test'); + const DELAYED_KEY = makeStateKey<string>('delayed'); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserModule.withServerTransition({appId: APP_ID}), + BrowserTransferStateModule, + ] }); + doc = TestBed.inject(DOCUMENT); + }); - afterEach(() => { removeScriptTag(doc, APP_ID + '-state'); }); + afterEach(() => { + removeScriptTag(doc, APP_ID + '-state'); + }); - it('is initialized from script tag', () => { - addScriptTag(doc, APP_ID, {test: 10}); - const transferState: TransferState = TestBed.inject(TransferState); - expect(transferState.get(TEST_KEY, 0)).toBe(10); - }); + it('is initialized from script tag', () => { + addScriptTag(doc, APP_ID, {test: 10}); + const transferState: TransferState = TestBed.inject(TransferState); + expect(transferState.get(TEST_KEY, 0)).toBe(10); + }); - it('is initialized to empty state if script tag not found', () => { - const transferState: TransferState = TestBed.inject(TransferState); - expect(transferState.get(TEST_KEY, 0)).toBe(0); - }); + it('is initialized to empty state if script tag not found', () => { + const transferState: TransferState = TestBed.inject(TransferState); + expect(transferState.get(TEST_KEY, 0)).toBe(0); + }); - it('supports adding new keys using set', () => { - const transferState: TransferState = TestBed.inject(TransferState); - transferState.set(TEST_KEY, 20); - expect(transferState.get(TEST_KEY, 0)).toBe(20); - expect(transferState.hasKey(TEST_KEY)).toBe(true); - }); + it('supports adding new keys using set', () => { + const transferState: TransferState = TestBed.inject(TransferState); + transferState.set(TEST_KEY, 20); + expect(transferState.get(TEST_KEY, 0)).toBe(20); + expect(transferState.hasKey(TEST_KEY)).toBe(true); + }); - it('supports setting and accessing value \'0\' via get', () => { - const transferState: TransferState = TestBed.inject(TransferState); - transferState.set(TEST_KEY, 0); - expect(transferState.get(TEST_KEY, 20)).toBe(0); - expect(transferState.hasKey(TEST_KEY)).toBe(true); - }); + it('supports setting and accessing value \'0\' via get', () => { + const transferState: TransferState = TestBed.inject(TransferState); + transferState.set(TEST_KEY, 0); + expect(transferState.get(TEST_KEY, 20)).toBe(0); + expect(transferState.hasKey(TEST_KEY)).toBe(true); + }); - it('supports setting and accessing value \'false\' via get', () => { - const transferState: TransferState = TestBed.inject(TransferState); - transferState.set(TEST_KEY, false); - expect(transferState.get(TEST_KEY, true)).toBe(false); - expect(transferState.hasKey(TEST_KEY)).toBe(true); - }); + it('supports setting and accessing value \'false\' via get', () => { + const transferState: TransferState = TestBed.inject(TransferState); + transferState.set(TEST_KEY, false); + expect(transferState.get(TEST_KEY, true)).toBe(false); + expect(transferState.hasKey(TEST_KEY)).toBe(true); + }); - it('supports setting and accessing value \'null\' via get', () => { - const transferState: TransferState = TestBed.inject(TransferState); - transferState.set(TEST_KEY, null); - expect(transferState.get(TEST_KEY, 20 as any)).toBe(null); - expect(transferState.hasKey(TEST_KEY)).toBe(true); - }); + it('supports setting and accessing value \'null\' via get', () => { + const transferState: TransferState = TestBed.inject(TransferState); + transferState.set(TEST_KEY, null); + expect(transferState.get(TEST_KEY, 20 as any)).toBe(null); + expect(transferState.hasKey(TEST_KEY)).toBe(true); + }); - it('supports removing keys', () => { - const transferState: TransferState = TestBed.inject(TransferState); - transferState.set(TEST_KEY, 20); - transferState.remove(TEST_KEY); - expect(transferState.get(TEST_KEY, 0)).toBe(0); - expect(transferState.hasKey(TEST_KEY)).toBe(false); - }); + it('supports removing keys', () => { + const transferState: TransferState = TestBed.inject(TransferState); + transferState.set(TEST_KEY, 20); + transferState.remove(TEST_KEY); + expect(transferState.get(TEST_KEY, 0)).toBe(0); + expect(transferState.hasKey(TEST_KEY)).toBe(false); + }); - it('supports serialization using toJson()', () => { - const transferState: TransferState = TestBed.inject(TransferState); - transferState.set(TEST_KEY, 20); - expect(transferState.toJson()).toBe('{"test":20}'); - }); + it('supports serialization using toJson()', () => { + const transferState: TransferState = TestBed.inject(TransferState); + transferState.set(TEST_KEY, 20); + expect(transferState.toJson()).toBe('{"test":20}'); + }); - it('calls onSerialize callbacks when calling toJson()', () => { - const transferState: TransferState = TestBed.inject(TransferState); - transferState.set(TEST_KEY, 20); + it('calls onSerialize callbacks when calling toJson()', () => { + const transferState: TransferState = TestBed.inject(TransferState); + transferState.set(TEST_KEY, 20); - let value = 'initial'; - transferState.onSerialize(DELAYED_KEY, () => value); - value = 'changed'; + let value = 'initial'; + transferState.onSerialize(DELAYED_KEY, () => value); + value = 'changed'; - expect(transferState.toJson()).toBe('{"test":20,"delayed":"changed"}'); - }); + expect(transferState.toJson()).toBe('{"test":20,"delayed":"changed"}'); }); - - describe('escape/unescape', () => { - it('works with all escaped characters', () => { - const testString = '</script><script>alert(\'Hello&\' + "World");'; - const testObj = {testString}; - const escaped = escapeHtml(JSON.stringify(testObj)); - expect(escaped).toBe( - '{&q;testString&q;:&q;&l;/script&g;&l;script&g;' + - 'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}'); - - const unescapedObj = JSON.parse(unescapeHtml(escaped)); - expect(unescapedObj['testString']).toBe(testString); - }); +}); + +describe('escape/unescape', () => { + it('works with all escaped characters', () => { + const testString = '</script><script>alert(\'Hello&\' + "World");'; + const testObj = {testString}; + const escaped = escapeHtml(JSON.stringify(testObj)); + expect(escaped).toBe( + '{&q;testString&q;:&q;&l;/script&g;&l;script&g;' + + 'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}'); + + const unescapedObj = JSON.parse(unescapeHtml(escaped)); + expect(unescapedObj['testString']).toBe(testString); }); +}); })(); diff --git a/packages/platform-browser/test/browser_util_spec.ts b/packages/platform-browser/test/browser_util_spec.ts index 664f2cc3667f6..d67a429724633 100644 --- a/packages/platform-browser/test/browser_util_spec.ts +++ b/packages/platform-browser/test/browser_util_spec.ts @@ -11,7 +11,6 @@ import {BrowserDetection} from '../testing/src/browser_util'; { describe('BrowserDetection', () => { - const browsers = [ { name: 'Chrome', @@ -224,7 +223,7 @@ import {BrowserDetection} from '../testing/src/browser_util'; ]; browsers.forEach((browser: {[key: string]: any}) => { - it(`should detect ${browser[ 'name']}`, () => { + it(`should detect ${browser['name']}`, () => { const bd = new BrowserDetection(<string>browser['ua']); expect(bd.isFirefox).toBe(browser['isFirefox']); expect(bd.isAndroid).toBe(browser['isAndroid']); diff --git a/packages/platform-browser/test/dom/events/event_manager_spec.ts b/packages/platform-browser/test/dom/events/event_manager_spec.ts index 8c433500fd1ba..6dc8fa356e4b2 100644 --- a/packages/platform-browser/test/dom/events/event_manager_spec.ts +++ b/packages/platform-browser/test/dom/events/event_manager_spec.ts @@ -14,361 +14,393 @@ import {EventManager, EventManagerPlugin} from '@angular/platform-browser/src/do import {createMouseEvent, el} from '../../../testing/src/browser_util'; (function() { - if (isNode) return; - let domEventPlugin: DomEventsPlugin; - let doc: any; - let zone: NgZone; - - describe('EventManager', () => { - beforeEach(() => { - doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument(); - zone = new NgZone({}); - domEventPlugin = new DomEventsPlugin(doc); - }); +if (isNode) return; +let domEventPlugin: DomEventsPlugin; +let doc: any; +let zone: NgZone; + +describe('EventManager', () => { + beforeEach(() => { + doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument(); + zone = new NgZone({}); + domEventPlugin = new DomEventsPlugin(doc); + }); - it('should delegate event bindings to plugins that are passed in from the most generic one to the most specific one', - () => { - const element = el('<div></div>'); - const handler = (e: any /** TODO #9100 */) => e; - const plugin = new FakeEventManagerPlugin(doc, ['click']); - const manager = new EventManager([domEventPlugin, plugin], new FakeNgZone()); - manager.addEventListener(element, 'click', handler); - expect(plugin.eventHandler['click']).toBe(handler); - }); - - it('should delegate event bindings to the first plugin supporting the event', () => { - const element = el('<div></div>'); - const clickHandler = (e: any /** TODO #9100 */) => e; - const dblClickHandler = (e: any /** TODO #9100 */) => e; - const plugin1 = new FakeEventManagerPlugin(doc, ['dblclick']); - const plugin2 = new FakeEventManagerPlugin(doc, ['click', 'dblclick']); - const manager = new EventManager([plugin2, plugin1], new FakeNgZone()); - manager.addEventListener(element, 'click', clickHandler); - manager.addEventListener(element, 'dblclick', dblClickHandler); - expect(plugin2.eventHandler['click']).toBe(clickHandler); - expect(plugin1.eventHandler['dblclick']).toBe(dblClickHandler); - }); + it('should delegate event bindings to plugins that are passed in from the most generic one to the most specific one', + () => { + const element = el('<div></div>'); + const handler = (e: any /** TODO #9100 */) => e; + const plugin = new FakeEventManagerPlugin(doc, ['click']); + const manager = new EventManager([domEventPlugin, plugin], new FakeNgZone()); + manager.addEventListener(element, 'click', handler); + expect(plugin.eventHandler['click']).toBe(handler); + }); + + it('should delegate event bindings to the first plugin supporting the event', () => { + const element = el('<div></div>'); + const clickHandler = (e: any /** TODO #9100 */) => e; + const dblClickHandler = (e: any /** TODO #9100 */) => e; + const plugin1 = new FakeEventManagerPlugin(doc, ['dblclick']); + const plugin2 = new FakeEventManagerPlugin(doc, ['click', 'dblclick']); + const manager = new EventManager([plugin2, plugin1], new FakeNgZone()); + manager.addEventListener(element, 'click', clickHandler); + manager.addEventListener(element, 'dblclick', dblClickHandler); + expect(plugin2.eventHandler['click']).toBe(clickHandler); + expect(plugin1.eventHandler['dblclick']).toBe(dblClickHandler); + }); - it('should throw when no plugin can handle the event', () => { - const element = el('<div></div>'); - const plugin = new FakeEventManagerPlugin(doc, ['dblclick']); - const manager = new EventManager([plugin], new FakeNgZone()); - expect(() => manager.addEventListener(element, 'click', null !)) - .toThrowError('No event manager plugin found for event click'); - }); + it('should throw when no plugin can handle the event', () => { + const element = el('<div></div>'); + const plugin = new FakeEventManagerPlugin(doc, ['dblclick']); + const manager = new EventManager([plugin], new FakeNgZone()); + expect(() => manager.addEventListener(element, 'click', null!)) + .toThrowError('No event manager plugin found for event click'); + }); - it('events are caught when fired from a child', () => { - const element = el('<div><div></div></div>'); - // Workaround for https://bugs.webkit.org/show_bug.cgi?id=122755 - doc.body.appendChild(element); + it('events are caught when fired from a child', () => { + const element = el('<div><div></div></div>'); + // Workaround for https://bugs.webkit.org/show_bug.cgi?id=122755 + doc.body.appendChild(element); + + const child = element.firstChild as Element; + const dispatchedEvent = createMouseEvent('click'); + let receivedEvent: any /** TODO #9100 */ = null; + const handler = (e: any /** TODO #9100 */) => { + receivedEvent = e; + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + manager.addEventListener(element, 'click', handler); + getDOM().dispatchEvent(child, dispatchedEvent); + + expect(receivedEvent).toBe(dispatchedEvent); + }); - const child = element.firstChild as Element; - const dispatchedEvent = createMouseEvent('click'); - let receivedEvent: any /** TODO #9100 */ = null; - const handler = (e: any /** TODO #9100 */) => { receivedEvent = e; }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - manager.addEventListener(element, 'click', handler); - getDOM().dispatchEvent(child, dispatchedEvent); + it('should add and remove global event listeners', () => { + const element = el('<div><div></div></div>'); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('click'); + let receivedEvent: any /** TODO #9100 */ = null; + const handler = (e: any /** TODO #9100 */) => { + receivedEvent = e; + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + + const remover = manager.addGlobalEventListener('document', 'click', handler); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvent).toBe(dispatchedEvent); + + receivedEvent = null; + remover(); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvent).toBe(null); + }); - expect(receivedEvent).toBe(dispatchedEvent); + it('should keep zone when addEventListener', () => { + const Zone = (window as any)['Zone']; + + const element = el('<div><div></div></div>'); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('click'); + let receivedEvent: any /** TODO #9100 */ = null; + let receivedZone: any = null; + const handler = (e: any /** TODO #9100 */) => { + receivedEvent = e; + receivedZone = Zone.current; + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + + let remover: any = null; + Zone.root.run(() => { + remover = manager.addEventListener(element, 'click', handler); }); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvent).toBe(dispatchedEvent); + expect(receivedZone.name).toBe(Zone.root.name); + + receivedEvent = null; + remover && remover(); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvent).toBe(null); + }); - it('should add and remove global event listeners', () => { - const element = el('<div><div></div></div>'); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('click'); - let receivedEvent: any /** TODO #9100 */ = null; - const handler = (e: any /** TODO #9100 */) => { receivedEvent = e; }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - - const remover = manager.addGlobalEventListener('document', 'click', handler); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvent).toBe(dispatchedEvent); - - receivedEvent = null; - remover(); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvent).toBe(null); + it('should keep zone when addEventListener multiple times', () => { + const Zone = (window as any)['Zone']; + + const element = el('<div><div></div></div>'); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('click'); + let receivedEvents: any[] /** TODO #9100 */ = []; + let receivedZones: any[] = []; + const handler1 = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); + }; + const handler2 = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + + let remover1: any = null; + let remover2: any = null; + Zone.root.run(() => { + remover1 = manager.addEventListener(element, 'click', handler1); }); - - it('should keep zone when addEventListener', () => { - const Zone = (window as any)['Zone']; - - const element = el('<div><div></div></div>'); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('click'); - let receivedEvent: any /** TODO #9100 */ = null; - let receivedZone: any = null; - const handler = (e: any /** TODO #9100 */) => { - receivedEvent = e; - receivedZone = Zone.current; - }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - - let remover: any = null; - Zone.root.run(() => { remover = manager.addEventListener(element, 'click', handler); }); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvent).toBe(dispatchedEvent); - expect(receivedZone.name).toBe(Zone.root.name); - - receivedEvent = null; - remover && remover(); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvent).toBe(null); + Zone.root.fork({name: 'test'}).run(() => { + remover2 = manager.addEventListener(element, 'click', handler2); }); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]); + expect(receivedZones).toEqual([Zone.root.name, 'test']); + + receivedEvents = []; + remover1 && remover1(); + remover2 && remover2(); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([]); + }); - it('should keep zone when addEventListener multiple times', () => { - const Zone = (window as any)['Zone']; - - const element = el('<div><div></div></div>'); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('click'); - let receivedEvents: any[] /** TODO #9100 */ = []; - let receivedZones: any[] = []; - const handler1 = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - }; - const handler2 = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - - let remover1: any = null; - let remover2: any = null; - Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); }); - Zone.root.fork({name: 'test'}).run(() => { - remover2 = manager.addEventListener(element, 'click', handler2); - }); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]); - expect(receivedZones).toEqual([Zone.root.name, 'test']); - - receivedEvents = []; - remover1 && remover1(); - remover2 && remover2(); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([]); + it('should support event.stopImmediatePropagation', () => { + const Zone = (window as any)['Zone']; + + const element = el('<div><div></div></div>'); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('click'); + let receivedEvents: any[] /** TODO #9100 */ = []; + let receivedZones: any[] = []; + const handler1 = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); + e.stopImmediatePropagation(); + }; + const handler2 = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + + let remover1: any = null; + let remover2: any = null; + Zone.root.run(() => { + remover1 = manager.addEventListener(element, 'click', handler1); }); - - it('should support event.stopImmediatePropagation', () => { - const Zone = (window as any)['Zone']; - - const element = el('<div><div></div></div>'); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('click'); - let receivedEvents: any[] /** TODO #9100 */ = []; - let receivedZones: any[] = []; - const handler1 = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - e.stopImmediatePropagation(); - }; - const handler2 = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - - let remover1: any = null; - let remover2: any = null; - Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); }); - Zone.root.fork({name: 'test'}).run(() => { - remover2 = manager.addEventListener(element, 'click', handler2); - }); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([dispatchedEvent]); - expect(receivedZones).toEqual([Zone.root.name]); - - receivedEvents = []; - remover1 && remover1(); - remover2 && remover2(); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([]); + Zone.root.fork({name: 'test'}).run(() => { + remover2 = manager.addEventListener(element, 'click', handler2); }); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([dispatchedEvent]); + expect(receivedZones).toEqual([Zone.root.name]); + + receivedEvents = []; + remover1 && remover1(); + remover2 && remover2(); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([]); + }); - it('should handle event correctly when one handler remove itself ', () => { - const Zone = (window as any)['Zone']; - - const element = el('<div><div></div></div>'); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('click'); - let receivedEvents: any[] /** TODO #9100 */ = []; - let receivedZones: any[] = []; - let remover1: any = null; - let remover2: any = null; - const handler1 = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - remover1 && remover1(); - }; - const handler2 = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - - Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); }); - Zone.root.fork({name: 'test'}).run(() => { - remover2 = manager.addEventListener(element, 'click', handler2); - }); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]); - expect(receivedZones).toEqual([Zone.root.name, 'test']); - - receivedEvents = []; + it('should handle event correctly when one handler remove itself ', () => { + const Zone = (window as any)['Zone']; + + const element = el('<div><div></div></div>'); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('click'); + let receivedEvents: any[] /** TODO #9100 */ = []; + let receivedZones: any[] = []; + let remover1: any = null; + let remover2: any = null; + const handler1 = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); remover1 && remover1(); - remover2 && remover2(); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([]); + }; + const handler2 = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + + Zone.root.run(() => { + remover1 = manager.addEventListener(element, 'click', handler1); }); - - it('should only add same callback once when addEventListener', () => { - const Zone = (window as any)['Zone']; - - const element = el('<div><div></div></div>'); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('click'); - let receivedEvents: any[] /** TODO #9100 */ = []; - let receivedZones: any[] = []; - const handler = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - - let remover1: any = null; - let remover2: any = null; - Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler); }); - Zone.root.fork({name: 'test'}).run(() => { - remover2 = manager.addEventListener(element, 'click', handler); - }); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([dispatchedEvent]); - expect(receivedZones).toEqual([Zone.root.name]); - - receivedEvents = []; - remover1 && remover1(); - remover2 && remover2(); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([]); + Zone.root.fork({name: 'test'}).run(() => { + remover2 = manager.addEventListener(element, 'click', handler2); }); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]); + expect(receivedZones).toEqual([Zone.root.name, 'test']); + + receivedEvents = []; + remover1 && remover1(); + remover2 && remover2(); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([]); + }); - it('should be able to remove event listener which was added inside of ngZone', () => { - const Zone = (window as any)['Zone']; - - const element = el('<div><div></div></div>'); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('click'); - let receivedEvents: any[] /** TODO #9100 */ = []; - let receivedZones: any[] = []; - const handler1 = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - }; - const handler2 = (e: any /** TODO #9100 */) => { - receivedEvents.push(e); - receivedZones.push(Zone.current.name); - }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - - let remover1: any = null; - let remover2: any = null; - // handler1 is added in root zone - Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); }); - // handler2 is added in 'angular' zone - Zone.root.fork({name: 'fakeAngularZone', properties: {isAngularZone: true}}).run(() => { - remover2 = manager.addEventListener(element, 'click', handler2); - }); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]); - expect(receivedZones).toEqual([Zone.root.name, 'fakeAngularZone']); - - receivedEvents = []; - remover1 && remover1(); - remover2 && remover2(); - getDOM().dispatchEvent(element, dispatchedEvent); - // handler1 and handler2 are added in different zone - // one is angular zone, the other is not - // should still be able to remove them correctly - expect(receivedEvents).toEqual([]); + it('should only add same callback once when addEventListener', () => { + const Zone = (window as any)['Zone']; + + const element = el('<div><div></div></div>'); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('click'); + let receivedEvents: any[] /** TODO #9100 */ = []; + let receivedZones: any[] = []; + const handler = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + + let remover1: any = null; + let remover2: any = null; + Zone.root.run(() => { + remover1 = manager.addEventListener(element, 'click', handler); + }); + Zone.root.fork({name: 'test'}).run(() => { + remover2 = manager.addEventListener(element, 'click', handler); }); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([dispatchedEvent]); + expect(receivedZones).toEqual([Zone.root.name]); + + receivedEvents = []; + remover1 && remover1(); + remover2 && remover2(); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([]); + }); - it('should run blackListedEvents handler outside of ngZone', () => { - const Zone = (window as any)['Zone']; - const element = el('<div><div></div></div>'); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('scroll'); - let receivedEvent: any /** TODO #9100 */ = null; - let receivedZone: any = null; - const handler = (e: any /** TODO #9100 */) => { - receivedEvent = e; - receivedZone = Zone.current; - }; - const manager = new EventManager([domEventPlugin], new FakeNgZone()); - - let remover = manager.addEventListener(element, 'scroll', handler); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvent).toBe(dispatchedEvent); - expect(receivedZone.name).not.toEqual('angular'); - - receivedEvent = null; - remover && remover(); - getDOM().dispatchEvent(element, dispatchedEvent); - expect(receivedEvent).toBe(null); + it('should be able to remove event listener which was added inside of ngZone', () => { + const Zone = (window as any)['Zone']; + + const element = el('<div><div></div></div>'); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('click'); + let receivedEvents: any[] /** TODO #9100 */ = []; + let receivedZones: any[] = []; + const handler1 = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); + }; + const handler2 = (e: any /** TODO #9100 */) => { + receivedEvents.push(e); + receivedZones.push(Zone.current.name); + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + + let remover1: any = null; + let remover2: any = null; + // handler1 is added in root zone + Zone.root.run(() => { + remover1 = manager.addEventListener(element, 'click', handler1); + }); + // handler2 is added in 'angular' zone + Zone.root.fork({name: 'fakeAngularZone', properties: {isAngularZone: true}}).run(() => { + remover2 = manager.addEventListener(element, 'click', handler2); }); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]); + expect(receivedZones).toEqual([Zone.root.name, 'fakeAngularZone']); + + receivedEvents = []; + remover1 && remover1(); + remover2 && remover2(); + getDOM().dispatchEvent(element, dispatchedEvent); + // handler1 and handler2 are added in different zone + // one is angular zone, the other is not + // should still be able to remove them correctly + expect(receivedEvents).toEqual([]); + }); + + it('should run blackListedEvents handler outside of ngZone', () => { + const Zone = (window as any)['Zone']; + const element = el('<div><div></div></div>'); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('scroll'); + let receivedEvent: any /** TODO #9100 */ = null; + let receivedZone: any = null; + const handler = (e: any /** TODO #9100 */) => { + receivedEvent = e; + receivedZone = Zone.current; + }; + const manager = new EventManager([domEventPlugin], new FakeNgZone()); + + let remover = manager.addEventListener(element, 'scroll', handler); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvent).toBe(dispatchedEvent); + expect(receivedZone.name).not.toEqual('angular'); + + receivedEvent = null; + remover && remover(); + getDOM().dispatchEvent(element, dispatchedEvent); + expect(receivedEvent).toBe(null); + }); - it('should only trigger one Change detection when bubbling', (done: DoneFn) => { - doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument(); - zone = new NgZone({shouldCoalesceEventChangeDetection: true}); - domEventPlugin = new DomEventsPlugin(doc); - const element = el('<div></div>'); - const child = el('<div></div>'); - element.appendChild(child); - doc.body.appendChild(element); - const dispatchedEvent = createMouseEvent('click'); - let receivedEvents: any = []; - let stables: any = []; - const handler = (e: any) => { receivedEvents.push(e); }; - const manager = new EventManager([domEventPlugin], zone); - let removerChild: any; - let removerParent: any; - - zone.run(() => { - removerChild = manager.addEventListener(child, 'click', handler); - removerParent = manager.addEventListener(element, 'click', handler); - }); - zone.onStable.subscribe((isStable: any) => { stables.push(isStable); }); - getDOM().dispatchEvent(child, dispatchedEvent); - requestAnimationFrame(() => { - expect(receivedEvents.length).toBe(2); - expect(stables.length).toBe(1); - - removerChild && removerChild(); - removerParent && removerParent(); - done(); - }); + it('should only trigger one Change detection when bubbling', (done: DoneFn) => { + doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument(); + zone = new NgZone({shouldCoalesceEventChangeDetection: true}); + domEventPlugin = new DomEventsPlugin(doc); + const element = el('<div></div>'); + const child = el('<div></div>'); + element.appendChild(child); + doc.body.appendChild(element); + const dispatchedEvent = createMouseEvent('click'); + let receivedEvents: any = []; + let stables: any = []; + const handler = (e: any) => { + receivedEvents.push(e); + }; + const manager = new EventManager([domEventPlugin], zone); + let removerChild: any; + let removerParent: any; + + zone.run(() => { + removerChild = manager.addEventListener(child, 'click', handler); + removerParent = manager.addEventListener(element, 'click', handler); + }); + zone.onStable.subscribe((isStable: any) => { + stables.push(isStable); + }); + getDOM().dispatchEvent(child, dispatchedEvent); + requestAnimationFrame(() => { + expect(receivedEvents.length).toBe(2); + expect(stables.length).toBe(1); + + removerChild && removerChild(); + removerParent && removerParent(); + done(); }); }); +}); })(); /** @internal */ class FakeEventManagerPlugin extends EventManagerPlugin { eventHandler: {[event: string]: Function} = {}; - constructor(doc: any, public supportedEvents: string[]) { super(doc); } + constructor(doc: any, public supportedEvents: string[]) { + super(doc); + } - supports(eventName: string): boolean { return this.supportedEvents.indexOf(eventName) > -1; } + supports(eventName: string): boolean { + return this.supportedEvents.indexOf(eventName) > -1; + } addEventListener(element: any, eventName: string, handler: Function) { this.eventHandler[eventName] = handler; - return () => { delete this.eventHandler[eventName]; }; + return () => { + delete this.eventHandler[eventName]; + }; } } class FakeNgZone extends NgZone { - constructor() { super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: true}); } - run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T { return fn(); } - runOutsideAngular(fn: Function) { return fn(); } + constructor() { + super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: true}); + } + run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T { + return fn(); + } + runOutsideAngular(fn: Function) { + return fn(); + } } diff --git a/packages/platform-browser/test/dom/events/hammer_gestures_spec.ts b/packages/platform-browser/test/dom/events/hammer_gestures_spec.ts index cf82361a9bc0c..fd0c99c35e3ce 100644 --- a/packages/platform-browser/test/dom/events/hammer_gestures_spec.ts +++ b/packages/platform-browser/test/dom/events/hammer_gestures_spec.ts @@ -17,7 +17,9 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow let fakeConsole: any; if (isNode) return; - beforeEach(() => { fakeConsole = {warn: jasmine.createSpy('console.warn')}; }); + beforeEach(() => { + fakeConsole = {warn: jasmine.createSpy('console.warn')}; + }); describe('with no custom loader', () => { beforeEach(() => { @@ -25,7 +27,7 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow }); it('should implement addGlobalEventListener', () => { - spyOn(plugin, 'addEventListener').and.callFake(() => {}); + spyOn(plugin, 'addEventListener').and.callFake(() => () => {}); expect(() => { plugin.addGlobalEventListener('document', 'swipe', () => {}); @@ -61,7 +63,9 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow // Inject the NgZone so that we can make it available to the plugin through a fake // EventManager. let ngZone: NgZone; - beforeEach(inject([NgZone], (z: NgZone) => { ngZone = z; })); + beforeEach(inject([NgZone], (z: NgZone) => { + ngZone = z; + })); beforeEach(() => { originalHammerGlobal = (window as any).Hammer; @@ -84,13 +88,15 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow plugin = new HammerGesturesPlugin(document, hammerConfig, fakeConsole, loader); // Use a fake EventManager that has access to the NgZone. - plugin.manager = { getZone: () => ngZone } as EventManager; + plugin.manager = {getZone: () => ngZone} as EventManager; someElement = document.createElement('div'); someListener = () => {}; }); - afterEach(() => { (window as any).Hammer = originalHammerGlobal; }); + afterEach(() => { + (window as any).Hammer = originalHammerGlobal; + }); it('should not log a warning when HammerJS is not loaded', () => { plugin.addEventListener(someElement, 'swipe', () => {}); diff --git a/packages/platform-browser/test/dom/events/key_events_spec.ts b/packages/platform-browser/test/dom/events/key_events_spec.ts index 648852de64e97..ba97308756686 100644 --- a/packages/platform-browser/test/dom/events/key_events_spec.ts +++ b/packages/platform-browser/test/dom/events/key_events_spec.ts @@ -51,7 +51,6 @@ import {KeyEventsPlugin} from '@angular/platform-browser/src/dom/events/key_even .toEqual({'domEventName': 'keydown', 'fullKey': 'control.shift'}); expect(KeyEventsPlugin.parseEventName('keyup.control.shift')) .toEqual({'domEventName': 'keyup', 'fullKey': 'control.shift'}); - }); it('should alias esc to escape', () => { @@ -62,11 +61,10 @@ import {KeyEventsPlugin} from '@angular/platform-browser/src/dom/events/key_even it('should implement addGlobalEventListener', () => { const plugin = new KeyEventsPlugin(document); - spyOn(plugin, 'addEventListener').and.callFake(() => {}); + spyOn(plugin, 'addEventListener').and.callFake(() => () => {}); expect(() => plugin.addGlobalEventListener('window', 'keyup.control.esc', () => {})) .not.toThrowError(); }); - }); } diff --git a/packages/platform-browser/test/dom/shadow_dom_spec.ts b/packages/platform-browser/test/dom/shadow_dom_spec.ts index 0570dd9ee235e..54a0c472f1ada 100644 --- a/packages/platform-browser/test/dom/shadow_dom_spec.ts +++ b/packages/platform-browser/test/dom/shadow_dom_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, EventEmitter, Injector, Input, NgModule, Output, Renderer2, ViewEncapsulation, destroyPlatform} from '@angular/core'; +import {Component, destroyPlatform, EventEmitter, Injector, Input, NgModule, Output, Renderer2, ViewEncapsulation} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {BrowserModule} from '@angular/platform-browser'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; @@ -15,14 +15,15 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; if (browserDetection.supportsShadowDom) { describe('ShadowDOM Support', () => { - let testContainer: HTMLDivElement; - beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); }); + beforeEach(() => { + TestBed.configureTestingModule({imports: [TestModule]}); + }); it('should attach and use a shadowRoot when ViewEncapsulation.Native is set', () => { const compEl = TestBed.createComponent(ShadowComponent).nativeElement; - expect(compEl.shadowRoot !.textContent).toEqual('Hello World'); + expect(compEl.shadowRoot!.textContent).toEqual('Hello World'); }); it('should use the shadow root to encapsulate styles', () => { @@ -40,10 +41,10 @@ if (browserDetection.supportsShadowDom) { const el = TestBed.createComponent(ShadowSlotComponent).nativeElement; const projectedContent = document.createTextNode('Hello Slot!'); el.appendChild(projectedContent); - const slot = el.shadowRoot !.querySelector('slot'); + const slot = el.shadowRoot!.querySelector('slot'); - expect(slot !.assignedNodes().length).toBe(1); - expect(slot !.assignedNodes()[0].textContent).toBe('Hello Slot!'); + expect(slot!.assignedNodes().length).toBe(1); + expect(slot!.assignedNodes()[0].textContent).toBe('Hello Slot!'); }); it('should allow the usage of named <slot> elements', () => { @@ -65,16 +66,16 @@ if (browserDetection.supportsShadowDom) { el.appendChild(articleContent); el.appendChild(articleSubcontent); - const headerSlot = el.shadowRoot !.querySelector('slot[name=header]') as HTMLSlotElement; - const articleSlot = el.shadowRoot !.querySelector('slot[name=article]') as HTMLSlotElement; + const headerSlot = el.shadowRoot!.querySelector('slot[name=header]') as HTMLSlotElement; + const articleSlot = el.shadowRoot!.querySelector('slot[name=article]') as HTMLSlotElement; - expect(headerSlot !.assignedNodes().length).toBe(1); - expect(headerSlot !.assignedNodes()[0].textContent).toBe('Header Text!'); + expect(headerSlot!.assignedNodes().length).toBe(1); + expect(headerSlot!.assignedNodes()[0].textContent).toBe('Header Text!'); expect(headerContent.assignedSlot).toBe(headerSlot); - expect(articleSlot !.assignedNodes().length).toBe(2); - expect(articleSlot !.assignedNodes()[0].textContent).toBe('Article Text!'); - expect(articleSlot !.assignedNodes()[1].textContent).toBe('Article Subtext!'); + expect(articleSlot!.assignedNodes().length).toBe(2); + expect(articleSlot!.assignedNodes()[0].textContent).toBe('Article Text!'); + expect(articleSlot!.assignedNodes()[1].textContent).toBe('Article Subtext!'); expect(articleContent.assignedSlot).toBe(articleSlot); expect(articleSubcontent.assignedSlot).toBe(articleSlot); }); diff --git a/packages/platform-browser/test/testing_public_spec.ts b/packages/platform-browser/test/testing_public_spec.ts index 24608a88bdef6..600e1415f1334 100644 --- a/packages/platform-browser/test/testing_public_spec.ts +++ b/packages/platform-browser/test/testing_public_spec.ts @@ -7,8 +7,8 @@ */ import {CompilerConfig, ResourceLoader} from '@angular/compiler'; -import {CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, Inject, Injectable, InjectionToken, Injector, Input, NgModule, Optional, Pipe, SkipSelf, ɵstringify as stringify} from '@angular/core'; -import {TestBed, async, fakeAsync, getTestBed, inject, tick, withModule} from '@angular/core/testing'; +import {Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, Inject, Injectable, InjectionToken, Injector, Input, NgModule, Optional, Pipe, SkipSelf, ɵstringify as stringify} from '@angular/core'; +import {async, fakeAsync, getTestBed, inject, TestBed, tick, withModule} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing'; @@ -18,7 +18,9 @@ import {ivyEnabled, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/priv @Injectable() class ChildComp { childBinding: string; - constructor() { this.childBinding = 'Child'; } + constructor() { + this.childBinding = 'Child'; + } } @Component({selector: 'child-comp', template: `<span>Mock</span>`}) @@ -52,12 +54,16 @@ class ChildChildComp { @Injectable() class ChildWithChildComp { childBinding: string; - constructor() { this.childBinding = 'Child'; } + constructor() { + this.childBinding = 'Child'; + } } class FancyService { value: string = 'real value'; - getAsyncValue() { return Promise.resolve('async value'); } + getAsyncValue() { + return Promise.resolve('async value'); + } getTimeoutValue() { return new Promise<string>((resolve, reject) => setTimeout(() => resolve('timeout value'), 10)); } @@ -88,13 +94,14 @@ class TestViewProvidersComp { @Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) class SomeDirective { // TODO(issue/24571): remove '!'. - @Input() - someDir !: string; + @Input() someDir!: string; } @Pipe({name: 'somePipe'}) class SomePipe { - transform(value: string) { return `transformed ${value}`; } + transform(value: string) { + return `transformed ${value}`; + } } @Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`}) @@ -120,11 +127,17 @@ const bTok = new InjectionToken<string>('b'); describe('using the async helper with context passing', () => { type TestContext = {actuallyDone: boolean}; - beforeEach(function(this: TestContext) { this.actuallyDone = false; }); + beforeEach(function(this: TestContext) { + this.actuallyDone = false; + }); - afterEach(function(this: TestContext) { expect(this.actuallyDone).toEqual(true); }); + afterEach(function(this: TestContext) { + expect(this.actuallyDone).toEqual(true); + }); - it('should run normal tests', function(this: TestContext) { this.actuallyDone = true; }); + it('should run normal tests', function(this: TestContext) { + this.actuallyDone = true; + }); it('should run normal async tests', function(this: TestContext, done) { setTimeout(() => { @@ -133,8 +146,9 @@ const bTok = new InjectionToken<string>('b'); }, 0); }); - it('should run async tests with tasks', - async(function(this: TestContext) { setTimeout(() => this.actuallyDone = true, 0); })); + it('should run async tests with tasks', async(function(this: TestContext) { + setTimeout(() => this.actuallyDone = true, 0); + })); it('should run async tests with promises', async(function(this: TestContext) { const p = new Promise((resolve, reject) => setTimeout(resolve, 10)); @@ -149,18 +163,26 @@ const bTok = new InjectionToken<string>('b'); type TestContext = {contextModified: boolean}; - beforeEach(function(this: TestContext) { this.contextModified = false; }); + beforeEach(function(this: TestContext) { + this.contextModified = false; + }); - afterEach(function(this: TestContext) { expect(this.contextModified).toEqual(true); }); + afterEach(function(this: TestContext) { + expect(this.contextModified).toEqual(true); + }); - it('should pass context to inject helper', - inject([], function(this: TestContext) { this.contextModified = true; })); + it('should pass context to inject helper', inject([], function(this: TestContext) { + this.contextModified = true; + })); - it('should pass context to fakeAsync helper', - fakeAsync(function(this: TestContext) { this.contextModified = true; })); + it('should pass context to fakeAsync helper', fakeAsync(function(this: TestContext) { + this.contextModified = true; + })); it('should pass context to withModule helper - simple', - withModule(moduleConfig, function(this: TestContext) { this.contextModified = true; })); + withModule(moduleConfig, function(this: TestContext) { + this.contextModified = true; + })); it('should pass context to withModule helper - advanced', withModule(moduleConfig) @@ -199,7 +221,7 @@ const bTok = new InjectionToken<string>('b'); it('should allow the use of fakeAsync', fakeAsync(inject([FancyService], (service: FancyService) => { - let value: string = undefined !; + let value: string = undefined!; service.getAsyncValue().then((val) => value = val); tick(); expect(value).toEqual('async value'); @@ -329,7 +351,9 @@ const bTok = new InjectionToken<string>('b'); describe('overwriting metadata', () => { @Pipe({name: 'undefined'}) class SomePipe { - transform(value: string): string { return `transformed ${value}`; } + transform(value: string): string { + return `transformed ${value}`; + } } @Directive({selector: '[undefined]'}) @@ -418,7 +442,6 @@ const bTok = new InjectionToken<string>('b'); }); describe('overriding providers', () => { - describe('in core', () => { it('ComponentFactoryResolver', () => { const componentFactoryMock = @@ -429,7 +452,6 @@ const bTok = new InjectionToken<string>('b'); }); describe('in NgModules', () => { - it('should support useValue', () => { TestBed.configureTestingModule({ providers: [ @@ -501,7 +523,9 @@ const bTok = new InjectionToken<string>('b'); @NgModule() class SomeModule { - constructor() { someModule = this; } + constructor() { + someModule = this; + } } TestBed.configureTestingModule({ @@ -731,11 +755,12 @@ const bTok = new InjectionToken<string>('b'); @Directive({selector: '[test]'}) class TestDir { - constructor() { testDir = this; } + constructor() { + testDir = this; + } // TODO(issue/24571): remove '!'. - @Input('test') - test !: string; + @Input('test') test!: string; } TestBed.overrideTemplateUsingTestingModule( @@ -746,7 +771,7 @@ const bTok = new InjectionToken<string>('b'); fixture.detectChanges(); expect(fixture.nativeElement).toHaveText('Hello world!'); expect(testDir).toBeAnInstanceOf(TestDir); - expect(testDir !.test).toBe('some prop'); + expect(testDir!.test).toBe('some prop'); }); it('should throw if the TestBed is already created', () => { @@ -776,9 +801,7 @@ const bTok = new InjectionToken<string>('b'); }); describe('setting up the compiler', () => { - describe('providers', () => { - it('should use set up providers', fakeAsync(() => { // Keeping this component inside the test is needed to make sure it's not resolved // prior to this test, thus having ɵcmp and a reference in resource @@ -869,8 +892,9 @@ const bTok = new InjectionToken<string>('b'); const itPromise = patchJasmineIt(); const barError = new Error('bar'); - it('throws an async error', - async(inject([], () => setTimeout(() => { throw barError; }, 0)))); + it('throws an async error', async(inject([], () => setTimeout(() => { + throw barError; + }, 0)))); itPromise.then(() => done.fail('Expected test to fail, but it did not'), (err) => { expect(err).toEqual(barError); @@ -883,7 +907,7 @@ const bTok = new InjectionToken<string>('b'); const itPromise = patchJasmineIt(); it('should fail with an error from a promise', async(inject([], () => { - let reject: (error: any) => void = undefined !; + let reject: (error: any) => void = undefined!; const promise = new Promise((_, rej) => reject = rej); const p = promise.then(() => expect(1).toEqual(2)); @@ -919,21 +943,23 @@ const bTok = new InjectionToken<string>('b'); } expect( - () => it( - 'should fail', withModule( - {declarations: [InlineCompWithUrlTemplate]}, - () => TestBed.createComponent(InlineCompWithUrlTemplate)))) + () => + it('should fail', + withModule( + {declarations: [InlineCompWithUrlTemplate]}, + () => TestBed.createComponent(InlineCompWithUrlTemplate)))) .toThrowError( ivyEnabled ? `Component 'InlineCompWithUrlTemplate' is not resolved: - templateUrl: /base/angular/packages/platform-browser/test/static_assets/test.html Did you run and wait for 'resolveComponentResources()'?` : - `This test module uses the component ${stringify(InlineCompWithUrlTemplate)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` + + `This test module uses the component ${ + stringify( + InlineCompWithUrlTemplate)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` + `Please call "TestBed.compileComponents" before your test.`); restoreJasmineIt(); }); - }); @@ -972,7 +998,6 @@ Did you run and wait for 'resolveComponentResources()'?` : }); describe('creating components', () => { - beforeEach(() => { TestBed.configureTestingModule({ declarations: [ @@ -1008,7 +1033,6 @@ Did you run and wait for 'resolveComponentResources()'?` : const componentFixture = TestBed.createComponent(ChildComp); componentFixture.detectChanges(); expect(componentFixture.nativeElement).toHaveText('Mock'); - })); it('should override a provider', async(() => { diff --git a/packages/platform-browser/testing/src/browser.ts b/packages/platform-browser/testing/src/browser.ts index 77f9d7675f12d..e9d931e98601a 100644 --- a/packages/platform-browser/testing/src/browser.ts +++ b/packages/platform-browser/testing/src/browser.ts @@ -5,8 +5,9 @@ * 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 {APP_ID, NgModule, NgZone, PLATFORM_INITIALIZER, PlatformRef, StaticProvider, createPlatformFactory, platformCore} from '@angular/core'; +import {APP_ID, createPlatformFactory, NgModule, NgZone, PLATFORM_INITIALIZER, platformCore, PlatformRef, StaticProvider} from '@angular/core'; import {BrowserModule, ɵBrowserDomAdapter as BrowserDomAdapter, ɵELEMENT_PROBE_PROVIDERS as ELEMENT_PROBE_PROVIDERS} from '@angular/platform-browser'; + import {BrowserDetection, createNgZone} from './browser_util'; function initBrowserTests() { diff --git a/packages/platform-browser/testing/src/browser_util.ts b/packages/platform-browser/testing/src/browser_util.ts index 31d1382061732..8ebf893807570 100644 --- a/packages/platform-browser/testing/src/browser_util.ts +++ b/packages/platform-browser/testing/src/browser_util.ts @@ -19,11 +19,17 @@ export class BrowserDetection { return getDOM() ? getDOM().getUserAgent() : ''; } - static setup() { return new BrowserDetection(null); } + static setup() { + return new BrowserDetection(null); + } - constructor(ua: string|null) { this._overrideUa = ua; } + constructor(ua: string|null) { + this._overrideUa = ua; + } - get isFirefox(): boolean { return this._ua.indexOf('Firefox') > -1; } + get isFirefox(): boolean { + return this._ua.indexOf('Firefox') > -1; + } get isAndroid(): boolean { return this._ua.indexOf('Mozilla/5.0') > -1 && this._ua.indexOf('Android') > -1 && @@ -31,9 +37,13 @@ export class BrowserDetection { this._ua.indexOf('IEMobile') == -1; } - get isEdge(): boolean { return this._ua.indexOf('Edge') > -1; } + get isEdge(): boolean { + return this._ua.indexOf('Edge') > -1; + } - get isIE(): boolean { return this._ua.indexOf('Trident') > -1; } + get isIE(): boolean { + return this._ua.indexOf('Trident') > -1; + } get isWebkit(): boolean { return this._ua.indexOf('AppleWebKit') > -1 && this._ua.indexOf('Edge') == -1 && @@ -45,7 +55,9 @@ export class BrowserDetection { this._ua.indexOf('IEMobile') == -1; } - get isSlow(): boolean { return this.isAndroid || this.isIE || this.isIOS7; } + get isSlow(): boolean { + return this.isAndroid || this.isIE || this.isIOS7; + } // The Intl API is only natively supported in Chrome, Firefox, IE11 and Edge. // This detector is needed in tests to make the difference between: @@ -67,13 +79,17 @@ export class BrowserDetection { this._ua.indexOf('Edge') == -1; } - get supportsCustomElements() { return (typeof(<any>global).customElements !== 'undefined'); } + get supportsCustomElements() { + return (typeof (<any>global).customElements !== 'undefined'); + } get supportsDeprecatedCustomCustomElementsV0() { - return (typeof(document as any).registerElement !== 'undefined'); + return (typeof (document as any).registerElement !== 'undefined'); } - get supportsRegExUnicodeFlag(): boolean { return RegExp.prototype.hasOwnProperty('unicode'); } + get supportsRegExUnicodeFlag(): boolean { + return RegExp.prototype.hasOwnProperty('unicode'); + } get supportsShadowDom() { const testEl = document.createElement('div'); @@ -203,10 +219,10 @@ export function setCookie(name: string, value: string) { } export function supportsWebAnimation(): boolean { - return typeof(<any>Element).prototype['animate'] === 'function'; + return typeof (<any>Element).prototype['animate'] === 'function'; } -export function hasStyle(element: any, styleName: string, styleValue?: string | null): boolean { +export function hasStyle(element: any, styleName: string, styleValue?: string|null): boolean { const value = element.style[styleName] || ''; return styleValue ? value == styleValue : value.length > 0; } diff --git a/packages/platform-browser/testing/src/matchers.ts b/packages/platform-browser/testing/src/matchers.ts index b582f4e599bbb..a6290562696ef 100644 --- a/packages/platform-browser/testing/src/matchers.ts +++ b/packages/platform-browser/testing/src/matchers.ts @@ -123,7 +123,9 @@ export const expect: <T = any>(actual: T) => NgMatchers<T> = _global.expect; return '' + m; } const res: any[] = []; - m.forEach((v: any, k: any) => { res.push(`${String(k)}:${String(v)}`); }); + m.forEach((v: any, k: any) => { + res.push(`${String(k)}:${String(v)}`); + }); return `{ ${res.join(',')} }`; }; @@ -141,7 +143,7 @@ _global.beforeEach(function() { return pass; } else { // TODO(misko): we should change the return, but jasmine.d.ts is not null safe - return undefined !; + return undefined!; } }); jasmine.addMatchers({ @@ -149,7 +151,12 @@ _global.beforeEach(function() { return { compare: function(actual: any) { const pass = typeof actual === 'object' && typeof actual.then === 'function'; - return {pass: pass, get message() { return 'Expected ' + actual + ' to be a promise'; }}; + return { + pass: pass, + get message() { + return 'Expected ' + actual + ' to be a promise'; + } + }; } }; }, @@ -174,7 +181,9 @@ _global.beforeEach(function() { const actualText = elementText(actual); return { pass: actualText == expectedText, - get message() { return 'Expected ' + actualText + ' to be equal to ' + expectedText; } + get message() { + return 'Expected ' + actualText + ' to be equal to ' + expectedText; + } }; } }; @@ -188,7 +197,8 @@ _global.beforeEach(function() { return { pass: hasClass(actual, className) == !isNot, get message() { - return `Expected ${actual.outerHTML} ${isNot ? 'not ' : ''}to contain the CSS class "${className}"`; + return `Expected ${actual.outerHTML} ${ + isNot ? 'not ' : ''}to contain the CSS class "${className}"`; } }; }; @@ -203,8 +213,9 @@ _global.beforeEach(function() { allPassed = hasStyle(actual, styles); } else { allPassed = Object.keys(styles).length !== 0; - Object.keys(styles).forEach( - prop => { allPassed = allPassed && hasStyle(actual, prop, styles[prop]); }); + Object.keys(styles).forEach(prop => { + allPassed = allPassed && hasStyle(actual, prop, styles[prop]); + }); } return { @@ -212,7 +223,8 @@ _global.beforeEach(function() { get message() { const expectedValueStr = typeof styles === 'string' ? styles : JSON.stringify(styles); return `Expected ${actual.outerHTML} ${!allPassed ? ' ' : 'not '}to contain the - CSS ${typeof styles === 'string' ? 'property' : 'styles'} "${expectedValueStr}"`; + CSS ${typeof styles === 'string' ? 'property' : 'styles'} "${ + expectedValueStr}"`; } }; } @@ -225,7 +237,9 @@ _global.beforeEach(function() { const errorMessage = actual.toString(); return { pass: errorMessage.indexOf(expectedText) > -1, - get message() { return 'Expected ' + errorMessage + ' to contain ' + expectedText; } + get message() { + return 'Expected ' + errorMessage + ' to contain ' + expectedText; + } }; } }; @@ -244,8 +258,8 @@ _global.beforeEach(function() { return { pass: missedMethods.length == 0, get message() { - return 'Expected ' + actualObject + ' to have the following methods: ' + - missedMethods.join(', '); + return 'Expected ' + actualObject + + ' to have the following methods: ' + missedMethods.join(', '); } }; } @@ -262,8 +276,8 @@ _global.beforeEach(function() { if (!(actualFixture instanceof ComponentFixture)) { return { pass: false, - message: msgFn( - `Expected actual to be of type \'ComponentFixture\' [actual=${actualFixture.constructor.name}]`) + message: msgFn(`Expected actual to be of type \'ComponentFixture\' [actual=${ + actualFixture.constructor.name}]`) }; } diff --git a/packages/platform-server/package.json b/packages/platform-server/package.json index 3ed7d778ffde8..2ada1e723ca4d 100644 --- a/packages/platform-server/package.json +++ b/packages/platform-server/package.json @@ -23,7 +23,7 @@ }, "dependencies": { "domino": "^2.1.2", - "xhr2": "^0.1.4" + "xhr2": "^0.2.0" }, "repository": { "type": "git", diff --git a/packages/platform-server/src/domino_adapter.ts b/packages/platform-server/src/domino_adapter.ts index 87370fd9fbfee..c8f8f255dd7f4 100644 --- a/packages/platform-server/src/domino_adapter.ts +++ b/packages/platform-server/src/domino_adapter.ts @@ -52,11 +52,15 @@ export class DominoAdapter extends BrowserDomAdapter { console.log(error); } - logGroup(error: string) { console.error(error); } + logGroup(error: string) { + console.error(error); + } logGroupEnd() {} - supportsDOMEvents(): boolean { return false; } + supportsDOMEvents(): boolean { + return false; + } createHtmlDocument(): HTMLDocument { return parseDocument('<html><head><title>fakeTitle'); @@ -72,7 +76,9 @@ export class DominoAdapter extends BrowserDomAdapter { isElementNode(node: any): boolean { return node ? node.nodeType === DominoAdapter.defaultDoc.ELEMENT_NODE : false; } - isShadowRoot(node: any): boolean { return node.shadowRoot == node; } + isShadowRoot(node: any): boolean { + return node.shadowRoot == node; + } getProperty(el: Element, name: string): any { if (name === 'href') { @@ -100,10 +106,10 @@ export class DominoAdapter extends BrowserDomAdapter { } getBaseHref(doc: Document): string { - const base = doc.documentElement !.querySelector('base'); + const base = doc.documentElement!.querySelector('base'); let href = ''; if (base) { - href = base.getAttribute('href') !; + href = base.getAttribute('href')!; } // TODO(alxhub): Need relative path logic from BrowserDomAdapter here? return href; @@ -120,12 +126,24 @@ export class DominoAdapter extends BrowserDomAdapter { } } - getHistory(): History { throw _notImplemented('getHistory'); } - getLocation(): Location { throw _notImplemented('getLocation'); } - getUserAgent(): string { return 'Fake user agent'; } + getHistory(): History { + throw _notImplemented('getHistory'); + } + getLocation(): Location { + throw _notImplemented('getLocation'); + } + getUserAgent(): string { + return 'Fake user agent'; + } - performanceNow(): number { return Date.now(); } + performanceNow(): number { + return Date.now(); + } - supportsCookies(): boolean { return false; } - getCookie(name: string): string { throw _notImplemented('getCookie'); } + supportsCookies(): boolean { + return false; + } + getCookie(name: string): string { + throw _notImplemented('getCookie'); + } } diff --git a/packages/platform-server/src/http.ts b/packages/platform-server/src/http.ts index 37fec07dd8f70..981490dc47e28 100644 --- a/packages/platform-server/src/http.ts +++ b/packages/platform-server/src/http.ts @@ -17,13 +17,15 @@ import {Observable, Observer, Subscription} from 'rxjs'; @Injectable() export class ServerXhr implements XhrFactory { - build(): XMLHttpRequest { return new xhr2.XMLHttpRequest(); } + build(): XMLHttpRequest { + return new xhr2.XMLHttpRequest(); + } } export abstract class ZoneMacroTaskWrapper { wrap(request: S): Observable { return new Observable((observer: Observer) => { - let task: Task = null !; + let task: Task = null!; let scheduled: boolean = false; let sub: Subscription|null = null; let savedResult: any = null; @@ -100,9 +102,13 @@ export abstract class ZoneMacroTaskWrapper { export class ZoneClientBackend extends ZoneMacroTaskWrapper, HttpEvent> implements HttpBackend { - constructor(private backend: HttpBackend) { super(); } + constructor(private backend: HttpBackend) { + super(); + } - handle(request: HttpRequest): Observable> { return this.wrap(request); } + handle(request: HttpRequest): Observable> { + return this.wrap(request); + } protected delegate(request: HttpRequest): Observable> { return this.backend.handle(request); @@ -115,9 +121,6 @@ export function zoneWrappedInterceptingHandler(backend: HttpBackend, injector: I } export const SERVER_HTTP_PROVIDERS: Provider[] = [ - {provide: XhrFactory, useClass: ServerXhr}, { - provide: HttpHandler, - useFactory: zoneWrappedInterceptingHandler, - deps: [HttpBackend, Injector] - } + {provide: XhrFactory, useClass: ServerXhr}, + {provide: HttpHandler, useFactory: zoneWrappedInterceptingHandler, deps: [HttpBackend, Injector]} ]; diff --git a/packages/platform-server/src/location.ts b/packages/platform-server/src/location.ts index 55849154cea97..052e22334a145 100644 --- a/packages/platform-server/src/location.ts +++ b/packages/platform-server/src/location.ts @@ -54,34 +54,40 @@ export class ServerPlatformLocation implements PlatformLocation { } } - getBaseHrefFromDOM(): string { return getDOM().getBaseHref(this._doc) !; } + getBaseHrefFromDOM(): string { + return getDOM().getBaseHref(this._doc)!; + } onPopState(fn: LocationChangeListener): void { // No-op: a state stack is not implemented, so // no events will ever come. } - onHashChange(fn: LocationChangeListener): void { this._hashUpdate.subscribe(fn); } + onHashChange(fn: LocationChangeListener): void { + this._hashUpdate.subscribe(fn); + } - get url(): string { return `${this.pathname}${this.search}${this.hash}`; } + get url(): string { + return `${this.pathname}${this.search}${this.hash}`; + } private setHash(value: string, oldUrl: string) { if (this.hash === value) { // Don't fire events if the hash has not changed. return; } - (this as{hash: string}).hash = value; + (this as {hash: string}).hash = value; const newUrl = this.url; - scheduleMicroTask(() => this._hashUpdate.next({ - type: 'hashchange', state: null, oldUrl, newUrl - } as LocationChangeEvent)); + scheduleMicroTask( + () => this._hashUpdate.next( + {type: 'hashchange', state: null, oldUrl, newUrl} as LocationChangeEvent)); } replaceState(state: any, title: string, newUrl: string): void { const oldUrl = this.url; const parsedUrl = parseUrl(newUrl); - (this as{pathname: string}).pathname = parsedUrl.pathname; - (this as{search: string}).search = parsedUrl.search; + (this as {pathname: string}).pathname = parsedUrl.pathname; + (this as {search: string}).search = parsedUrl.search; this.setHash(parsedUrl.hash, oldUrl); } @@ -89,12 +95,18 @@ export class ServerPlatformLocation implements PlatformLocation { this.replaceState(state, title, newUrl); } - forward(): void { throw new Error('Not implemented'); } + forward(): void { + throw new Error('Not implemented'); + } - back(): void { throw new Error('Not implemented'); } + back(): void { + throw new Error('Not implemented'); + } // History API isn't available on server, therefore return undefined - getState(): unknown { return undefined; } + getState(): unknown { + return undefined; + } } export function scheduleMicroTask(fn: Function) { diff --git a/packages/platform-server/src/platform-server.ts b/packages/platform-server/src/platform-server.ts index 762cefdd88f27..b898bbda544c3 100644 --- a/packages/platform-server/src/platform-server.ts +++ b/packages/platform-server/src/platform-server.ts @@ -7,7 +7,7 @@ */ export {PlatformState} from './platform_state'; -export {ServerModule, platformDynamicServer, platformServer} from './server'; +export {platformDynamicServer, platformServer, ServerModule} from './server'; export {BEFORE_APP_SERIALIZED, INITIAL_CONFIG, PlatformConfig} from './tokens'; export {ServerTransferStateModule} from './transfer_state'; export {renderModule, renderModuleFactory} from './utils'; diff --git a/packages/platform-server/src/platform_state.ts b/packages/platform-server/src/platform_state.ts index 73ecd298ecade..833fb7c1f9981 100644 --- a/packages/platform-server/src/platform_state.ts +++ b/packages/platform-server/src/platform_state.ts @@ -23,10 +23,14 @@ export class PlatformState { /** * Renders the current state of the platform to string. */ - renderToString(): string { return serializeDocument(this._doc); } + renderToString(): string { + return serializeDocument(this._doc); + } /** * Returns the current DOM state. */ - getDocument(): any { return this._doc; } + getDocument(): any { + return this._doc; + } } diff --git a/packages/platform-server/src/server.ts b/packages/platform-server/src/server.ts index 3b1d3d7e933e1..be960ab22cbdb 100644 --- a/packages/platform-server/src/server.ts +++ b/packages/platform-server/src/server.ts @@ -7,9 +7,9 @@ */ import {ɵAnimationEngine} from '@angular/animations/browser'; -import {DOCUMENT, PlatformLocation, ViewportScroller, ɵNullViewportScroller as NullViewportScroller, ɵPLATFORM_SERVER_ID as PLATFORM_SERVER_ID, ɵgetDOM as getDOM} from '@angular/common'; +import {DOCUMENT, PlatformLocation, ViewportScroller, ɵgetDOM as getDOM, ɵNullViewportScroller as NullViewportScroller, ɵPLATFORM_SERVER_ID as PLATFORM_SERVER_ID} from '@angular/common'; import {HttpClientModule} from '@angular/common/http'; -import {Injector, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactory2, StaticProvider, Testability, createPlatformFactory, platformCore, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS, ɵsetDocument} from '@angular/core'; +import {createPlatformFactory, Injector, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, platformCore, PlatformRef, Provider, RendererFactory2, StaticProvider, Testability, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS, ɵsetDocument} from '@angular/core'; import {BrowserModule, EVENT_MANAGER_PLUGINS, ɵSharedStylesHost as SharedStylesHost} from '@angular/platform-browser'; import {ɵplatformCoreDynamic as platformCoreDynamic} from '@angular/platform-browser-dynamic'; import {NoopAnimationsModule, ɵAnimationRendererFactory} from '@angular/platform-browser/animations'; @@ -41,7 +41,9 @@ export const INTERNAL_SERVER_PLATFORM_PROVIDERS: StaticProvider[] = [ ]; function initDominoAdapter(injector: Injector) { - return () => { DominoAdapter.makeCurrent(); }; + return () => { + DominoAdapter.makeCurrent(); + }; } export function instantiateServerRendererFactory( @@ -91,7 +93,7 @@ function _document(injector: Injector) { /** * @publicApi */ -export const platformServer: (extraProviders?: StaticProvider[] | undefined) => PlatformRef = +export const platformServer: (extraProviders?: StaticProvider[]|undefined) => PlatformRef = createPlatformFactory(platformCore, 'server', INTERNAL_SERVER_PLATFORM_PROVIDERS); /** diff --git a/packages/platform-server/src/server_events.ts b/packages/platform-server/src/server_events.ts index 82405f632ca48..ecae52dd8fffe 100644 --- a/packages/platform-server/src/server_events.ts +++ b/packages/platform-server/src/server_events.ts @@ -14,7 +14,9 @@ export class ServerEventManagerPlugin /* extends EventManagerPlugin which is pri constructor(@Inject(DOCUMENT) private doc: any) {} // Handle all events on the server. - supports(eventName: string) { return true; } + supports(eventName: string) { + return true; + } addEventListener(element: HTMLElement, eventName: string, handler: Function): Function { return getDOM().onAndCancel(element, eventName, handler); diff --git a/packages/platform-server/src/server_renderer.ts b/packages/platform-server/src/server_renderer.ts index 306632219ad0e..906052efc8a02 100644 --- a/packages/platform-server/src/server_renderer.ts +++ b/packages/platform-server/src/server_renderer.ts @@ -9,7 +9,7 @@ import {DOCUMENT, ɵgetDOM as getDOM} from '@angular/common'; import {DomElementSchemaRegistry} from '@angular/compiler'; import {Inject, Injectable, NgZone, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2, ViewEncapsulation} from '@angular/core'; -import {EventManager, ɵNAMESPACE_URIS as NAMESPACE_URIS, ɵSharedStylesHost as SharedStylesHost, ɵflattenStyles as flattenStyles, ɵshimContentAttribute as shimContentAttribute, ɵshimHostAttribute as shimHostAttribute} from '@angular/platform-browser'; +import {EventManager, ɵflattenStyles as flattenStyles, ɵNAMESPACE_URIS as NAMESPACE_URIS, ɵSharedStylesHost as SharedStylesHost, ɵshimContentAttribute as shimContentAttribute, ɵshimHostAttribute as shimHostAttribute} from '@angular/platform-browser'; const EMPTY_ARRAY: any[] = []; @@ -90,7 +90,9 @@ class DefaultServerRenderer2 implements Renderer2 { return doc.createTextNode(value); } - appendChild(parent: any, newChild: any): void { parent.appendChild(newChild); } + appendChild(parent: any, newChild: any): void { + parent.appendChild(newChild); + } insertBefore(parent: any, newChild: any, refChild: any): void { if (parent) { @@ -120,9 +122,13 @@ class DefaultServerRenderer2 implements Renderer2 { return el; } - parentNode(node: any): any { return node.parentNode; } + parentNode(node: any): any { + return node.parentNode; + } - nextSibling(node: any): any { return node.nextSibling; } + nextSibling(node: any): any { + return node.nextSibling; + } setAttribute(el: any, name: string, value: string, namespace?: string): void { if (namespace) { @@ -144,9 +150,13 @@ class DefaultServerRenderer2 implements Renderer2 { } } - addClass(el: any, name: string): void { el.classList.add(name); } + addClass(el: any, name: string): void { + el.classList.add(name); + } - removeClass(el: any, name: string): void { el.classList.remove(name); } + removeClass(el: any, name: string): void { + el.classList.remove(name); + } setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void { style = style.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); @@ -189,7 +199,9 @@ class DefaultServerRenderer2 implements Renderer2 { } } - setValue(node: any, value: string): void { node.textContent = value; } + setValue(node: any, value: string): void { + node.textContent = value; + } listen( target: 'document'|'window'|'body'|any, eventName: string, @@ -200,7 +212,7 @@ class DefaultServerRenderer2 implements Renderer2 { target, eventName, this.decoratePreventDefault(callback)); } return <() => void>this.eventManager.addEventListener( - target, eventName, this.decoratePreventDefault(callback)) as() => void; + target, eventName, this.decoratePreventDefault(callback)) as () => void; } private decoratePreventDefault(eventHandler: Function): Function { @@ -227,8 +239,8 @@ class DefaultServerRenderer2 implements Renderer2 { const AT_CHARCODE = '@'.charCodeAt(0); function checkNoSyntheticProp(name: string, nameKind: string) { if (name.charCodeAt(0) === AT_CHARCODE) { - throw new Error( - `Found the synthetic ${nameKind} ${name}. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.`); + throw new Error(`Found the synthetic ${nameKind} ${ + name}. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.`); } } @@ -249,7 +261,9 @@ class EmulatedEncapsulationServerRenderer2 extends DefaultServerRenderer2 { this.hostAttr = shimHostAttribute(componentId); } - applyToHost(element: any) { super.setAttribute(element, this.hostAttr, ''); } + applyToHost(element: any) { + super.setAttribute(element, this.hostAttr, ''); + } createElement(parent: any, name: string): Element { const el = super.createElement(parent, name, this.document); diff --git a/packages/platform-server/src/styles_host.ts b/packages/platform-server/src/styles_host.ts index 243125d784e7a..ceab0cca86de7 100644 --- a/packages/platform-server/src/styles_host.ts +++ b/packages/platform-server/src/styles_host.ts @@ -31,5 +31,7 @@ export class ServerStylesHost extends SharedStylesHost { this.head.appendChild(el); } - onStylesAdded(additions: Set) { additions.forEach(style => this._addStyle(style)); } + onStylesAdded(additions: Set) { + additions.forEach(style => this._addStyle(style)); + } } diff --git a/packages/platform-server/src/utils.ts b/packages/platform-server/src/utils.ts index d83f7d619919b..19a7fcff08bf0 100644 --- a/packages/platform-server/src/utils.ts +++ b/packages/platform-server/src/utils.ts @@ -77,8 +77,9 @@ the server-rendered app can be properly bootstrapped into a client app.`); return Promise .all(asyncPromises.map(asyncPromise => { - return asyncPromise.catch( - e => { console.warn('Ignoring BEFORE_APP_SERIALIZED Exception: ', e); }); + return asyncPromise.catch(e => { + console.warn('Ignoring BEFORE_APP_SERIALIZED Exception: ', e); + }); })) .then(complete); }); diff --git a/packages/platform-server/test/integration_spec.ts b/packages/platform-server/test/integration_spec.ts index 630d2b78c2a61..d073987afffa5 100644 --- a/packages/platform-server/test/integration_spec.ts +++ b/packages/platform-server/test/integration_spec.ts @@ -6,14 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {AnimationBuilder, animate, state, style, transition, trigger} from '@angular/animations'; -import {DOCUMENT, PlatformLocation, isPlatformServer, ɵgetDOM as getDOM} from '@angular/common'; +import {animate, AnimationBuilder, state, style, transition, trigger} from '@angular/animations'; +import {DOCUMENT, isPlatformServer, PlatformLocation, ɵgetDOM as getDOM} from '@angular/common'; import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; -import {ApplicationRef, CompilerFactory, Component, HostListener, Inject, Injectable, Input, NgModule, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation, destroyPlatform, getPlatform} from '@angular/core'; +import {ApplicationRef, CompilerFactory, Component, destroyPlatform, getPlatform, HostListener, Inject, Injectable, Input, NgModule, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation} from '@angular/core'; import {async, inject} from '@angular/core/testing'; -import {BrowserModule, Title, TransferState, makeStateKey} from '@angular/platform-browser'; -import {BEFORE_APP_SERIALIZED, INITIAL_CONFIG, PlatformState, ServerModule, ServerTransferStateModule, platformDynamicServer, renderModule, renderModuleFactory} from '@angular/platform-server'; +import {BrowserModule, makeStateKey, Title, TransferState} from '@angular/platform-browser'; +import {BEFORE_APP_SERIALIZED, INITIAL_CONFIG, platformDynamicServer, PlatformState, renderModule, renderModuleFactory, ServerModule, ServerTransferStateModule} from '@angular/platform-server'; import {ivyEnabled, modifiedInIvy} from '@angular/private/testing'; import {Observable} from 'rxjs'; import {first} from 'rxjs/operators'; @@ -64,7 +64,11 @@ function getAsyncTitleRenderHook(doc: any) { function asyncRejectRenderHook() { return () => { - return new Promise((_resolve, reject) => { setTimeout(() => { reject('reject'); }); }); + return new Promise((_resolve, reject) => { + setTimeout(() => { + reject('reject'); + }); + }); }; } @@ -136,7 +140,9 @@ class ExampleModule2 { @Component({selector: 'app', template: ``}) class TitleApp { constructor(private title: Title) {} - ngOnInit() { this.title.setTitle('Test App Title'); } + ngOnInit() { + this.title.setTitle('Test App Title'); + } } @NgModule({declarations: [TitleApp], imports: [ServerModule], bootstrap: [TitleApp]}) @@ -149,7 +155,9 @@ class MyAsyncServerApp { h1 = ''; @HostListener('window:scroll') - track() { console.error('scroll'); } + track() { + console.error('scroll'); + } ngOnInit() { Promise.resolve(null).then(() => setTimeout(() => { @@ -192,7 +200,8 @@ class SVGServerModule { 'transform': 'translate3d(0, 0, 0)', // not natively supported by Domino })), transition('void => *', [animate('0ms')]), - ], )] + ], + )] }) class MyAnimationApp { state = 'active'; @@ -281,7 +290,7 @@ class NativeExampleModule { @Component({selector: 'my-child', template: 'Works!'}) class MyChildComponent { // TODO(issue/24571): remove '!'. - @Input() public attr !: boolean; + @Input() public attr!: boolean; } @Component({selector: 'app', template: ''}) @@ -311,8 +320,7 @@ class InnerTextModule { @Component({selector: 'app', template: ''}) class MyInputComponent { - @Input() - name = ''; + @Input() name = ''; } @NgModule({ @@ -343,7 +351,9 @@ const STRING_KEY = makeStateKey('testString'); @Component({selector: 'app', template: 'Works!'}) class TransferComponent { constructor(private transferStore: TransferState) {} - ngOnInit() { this.transferStore.set(TEST_KEY, 10); } + ngOnInit() { + this.transferStore.set(TEST_KEY, 10); + } } @Component({selector: 'esc-app', template: 'Works!'}) @@ -380,8 +390,7 @@ class EscapedTransferStoreModule { @Component({selector: 'app', template: ''}) class MyHiddenComponent { - @Input() - name = ''; + @Input() name = ''; } @NgModule({ @@ -393,470 +402,474 @@ class HiddenModule { } (function() { - if (getDOM().supportsDOMEvents()) return; // NODE only +if (getDOM().supportsDOMEvents()) return; // NODE only - describe('platform-server integration', () => { - beforeEach(() => { - if (getPlatform()) destroyPlatform(); - }); +describe('platform-server integration', () => { + beforeEach(() => { + if (getPlatform()) destroyPlatform(); + }); - it('should bootstrap', async(() => { + it('should bootstrap', async(() => { + const platform = + platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}]); + + platform.bootstrapModule(ExampleModule).then((moduleRef) => { + expect(isPlatformServer(moduleRef.injector.get(PLATFORM_ID))).toBe(true); + const doc = moduleRef.injector.get(DOCUMENT); + + expect(doc.head).toBe(doc.querySelector('head')!); + expect(doc.body).toBe(doc.querySelector('body')!); + + expect(doc.documentElement.textContent).toEqual('Works!'); + + platform.destroy(); + }); + })); + + it('should allow multiple platform instances', async(() => { + const platform = + platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}]); + + const platform2 = + platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}]); + + + platform.bootstrapModule(ExampleModule).then((moduleRef) => { + const doc = moduleRef.injector.get(DOCUMENT); + expect(doc.documentElement.textContent).toEqual('Works!'); + platform.destroy(); + }); + + platform2.bootstrapModule(ExampleModule2).then((moduleRef) => { + const doc = moduleRef.injector.get(DOCUMENT); + expect(doc.documentElement.textContent).toEqual('Works too!'); + platform2.destroy(); + }); + })); + + it('adds title to the document using Title service', async(() => { + const platform = platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: {document: ''} + }]); + platform.bootstrapModule(TitleAppModule).then(ref => { + const state = ref.injector.get(PlatformState); + const doc = ref.injector.get(DOCUMENT); + const title = doc.querySelector('title')!; + expect(title.textContent).toBe('Test App Title'); + expect(state.renderToString()).toContain('Test App Title'); + }); + })); + + it('should get base href from document', async(() => { + const platform = platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: {document: ''} + }]); + platform.bootstrapModule(ExampleModule).then((moduleRef) => { + const location = moduleRef.injector.get(PlatformLocation); + expect(location.getBaseHrefFromDOM()).toEqual('/'); + platform.destroy(); + }); + })); + + it('adds styles with ng-transition attribute', async(() => { + const platform = platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: {document: ''} + }]); + platform.bootstrapModule(ExampleStylesModule).then(ref => { + const doc = ref.injector.get(DOCUMENT); + const head = doc.getElementsByTagName('head')[0]; + const styles: any[] = head.children as any; + expect(styles.length).toBe(1); + expect(styles[0].textContent).toContain('color: red'); + expect(styles[0].getAttribute('ng-transition')).toBe('example-styles'); + }); + })); + + it('copies known properties to attributes', async(() => { + const platform = + platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}]); + platform.bootstrapModule(ImageExampleModule).then(ref => { + const appRef: ApplicationRef = ref.injector.get(ApplicationRef); + const app = appRef.components[0].location.nativeElement; + const img = app.getElementsByTagName('img')[0] as any; + expect(img.attributes['src'].value).toEqual('link'); + }); + })); + + describe('PlatformLocation', () => { + it('is injectable', async(() => { const platform = platformDynamicServer( [{provide: INITIAL_CONFIG, useValue: {document: ''}}]); + platform.bootstrapModule(ExampleModule).then(appRef => { + const location: PlatformLocation = appRef.injector.get(PlatformLocation); + expect(location.pathname).toBe('/'); + platform.destroy(); + }); + })); + it('is configurable via INITIAL_CONFIG', () => { + platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: {document: '', url: 'http://test.com/deep/path?query#hash'} + }]) + .bootstrapModule(ExampleModule) + .then(appRef => { + const location: PlatformLocation = appRef.injector.get(PlatformLocation); + expect(location.pathname).toBe('/deep/path'); + expect(location.search).toBe('?query'); + expect(location.hash).toBe('#hash'); + }); + }); + it('parses component pieces of a URL', () => { + platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: {document: '', url: 'http://test.com:80/deep/path?query#hash'} + }]) + .bootstrapModule(ExampleModule) + .then(appRef => { + const location: PlatformLocation = appRef.injector.get(PlatformLocation); + expect(location.hostname).toBe('test.com'); + expect(location.protocol).toBe('http:'); + expect(location.port).toBe('80'); + expect(location.pathname).toBe('/deep/path'); + expect(location.search).toBe('?query'); + expect(location.hash).toBe('#hash'); + }); + }); + it('handles empty search and hash portions of the url', () => { + platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: {document: '', url: 'http://test.com/deep/path'} + }]) + .bootstrapModule(ExampleModule) + .then(appRef => { + const location: PlatformLocation = appRef.injector.get(PlatformLocation); + expect(location.pathname).toBe('/deep/path'); + expect(location.search).toBe(''); + expect(location.hash).toBe(''); + }); + }); + it('pushState causes the URL to update', async(() => { + const platform = platformDynamicServer( + [{provide: INITIAL_CONFIG, useValue: {document: ''}}]); + platform.bootstrapModule(ExampleModule).then(appRef => { + const location: PlatformLocation = appRef.injector.get(PlatformLocation); + location.pushState(null, 'Test', '/foo#bar'); + expect(location.pathname).toBe('/foo'); + expect(location.hash).toBe('#bar'); + platform.destroy(); + }); + })); + it('allows subscription to the hash state', done => { + const platform = + platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}]); + platform.bootstrapModule(ExampleModule).then(appRef => { + const location: PlatformLocation = appRef.injector.get(PlatformLocation); + expect(location.pathname).toBe('/'); + location.onHashChange((e: any) => { + expect(e.type).toBe('hashchange'); + expect(e.oldUrl).toBe('/'); + expect(e.newUrl).toBe('/foo#bar'); + platform.destroy(); + done(); + }); + location.pushState(null, 'Test', '/foo#bar'); + }); + }); + }); - platform.bootstrapModule(ExampleModule).then((moduleRef) => { - expect(isPlatformServer(moduleRef.injector.get(PLATFORM_ID))).toBe(true); - const doc = moduleRef.injector.get(DOCUMENT); + describe('render', () => { + let doc: string; + let called: boolean; + let expectedOutput = + 'Works!

fine

'; - expect(doc.head).toBe(doc.querySelector('head') !); - expect(doc.body).toBe(doc.querySelector('body') !); + beforeEach(() => { + // PlatformConfig takes in a parsed document so that it can be cached across requests. + doc = ''; + called = false; + // We use `window` and `document` directly in some parts of render3 for ivy + // Only set it to undefined for legacy + if (!ivyEnabled) { + (global as any)['window'] = undefined; + (global as any)['document'] = undefined; + } + }); + afterEach(() => { + expect(called).toBe(true); + }); - expect(doc.documentElement.textContent).toEqual('Works!'); + it('using long form should work', async(() => { + const platform = + platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: doc}}]); + + platform.bootstrapModule(AsyncServerModule) + .then((moduleRef) => { + const applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef); + return applicationRef.isStable.pipe(first((isStable: boolean) => isStable)) + .toPromise(); + }) + .then((b) => { + expect(platform.injector.get(PlatformState).renderToString()).toBe(expectedOutput); + platform.destroy(); + called = true; + }); + })); - platform.destroy(); + it('using renderModule should work', async(() => { + renderModule(AsyncServerModule, {document: doc}).then(output => { + expect(output).toBe(expectedOutput); + called = true; }); })); - it('should allow multiple platform instances', async(() => { - const platform = platformDynamicServer( - [{provide: INITIAL_CONFIG, useValue: {document: ''}}]); - - const platform2 = platformDynamicServer( - [{provide: INITIAL_CONFIG, useValue: {document: ''}}]); + modifiedInIvy('Will not support binding to innerText in Ivy since domino does not') + .it('should support binding to innerText', async(() => { + renderModule(InnerTextModule, {document: doc}).then(output => { + expect(output).toBe( + '
Some text
'); + called = true; + }); + })); + + it('using renderModuleFactory should work', + async(inject([PlatformRef], (defaultPlatform: PlatformRef) => { + const compilerFactory: CompilerFactory = + defaultPlatform.injector.get(CompilerFactory, null)!; + const moduleFactory = + compilerFactory.createCompiler().compileModuleSync(AsyncServerModule); + renderModuleFactory(moduleFactory, {document: doc}).then(output => { + expect(output).toBe(expectedOutput); + called = true; + }); + }))); + + it('works with SVG elements', async(() => { + renderModule(SVGServerModule, {document: doc}).then(output => { + expect(output).toBe( + '' + + ''); + called = true; + }); + })); + it('works with animation', async(() => { + renderModule(AnimationServerModule, {document: doc}).then(output => { + expect(output).toContain('Works!'); + expect(output).toContain('ng-trigger-myAnimation'); + expect(output).toContain('opacity:1;'); + expect(output).toContain('transform:translate3d(0 , 0 , 0);'); + expect(output).toContain('font-weight:bold;'); + called = true; + }); + })); - platform.bootstrapModule(ExampleModule).then((moduleRef) => { - const doc = moduleRef.injector.get(DOCUMENT); - expect(doc.documentElement.textContent).toEqual('Works!'); - platform.destroy(); + it('should handle ViewEncapsulation.Native', async(() => { + renderModule(NativeExampleModule, {document: doc}).then(output => { + expect(output).not.toBe(''); + expect(output).toContain('color: red'); + called = true; }); + })); + - platform2.bootstrapModule(ExampleModule2).then((moduleRef) => { - const doc = moduleRef.injector.get(DOCUMENT); - expect(doc.documentElement.textContent).toEqual('Works too!'); - platform2.destroy(); + it('sets a prefix for the _nghost and _ngcontent attributes', async(() => { + renderModule(ExampleStylesModule, {document: doc}).then(output => { + expect(output).toMatch( + /